Skip to content

Conversation

@Mauriceter
Copy link
Contributor

@Mauriceter Mauriceter commented Nov 11, 2025

Description

This work in Progress PR aims to add the possibility to generate an OpenGraph json file to enriched BloodHound datas after an NetExec run. (Only works for bloudhound-CE)

I made this PR even if it is still work in progress to have some feedback, in particular about the implementation as I am not sure about creating the global object in opengraph.py or modifying netexec.py and proto_flow workflow so let me know if there is any better ways. :)

The goal is to be able to easily add new tags/edges to bloodhound

  • --opengraph path/to/bloudhound/computers.json is used to generate the json. Computers.json data is used to resolve fqdn to Object ID as this is how BloudHound matches nodes. This resolution could later be also done directly with an ldap query or via bloudhound API. However, it is imo the best compromise as it avoids the need of a new ldap connection and the need to have the BloudHound instance reachable.

  • The json file is then generated in the NXCPath/log/ and can be imported directly in BloudHound via the direct import feature.

Example

Adding edges for HasSession when running --reg-sessions and adding host info such as IPs or SMB signing status

image

Generated OpenGraph json

{
  "graph": {
    "nodes": [
      {
        "id": "S-1-5-21-2593841013-2027981200-818290452-1001",
        "kinds": [
          "Computer",
          "Base"
        ],
        "properties": {
          "IP_Address": "192.168.57.11",
          "smbsigning": true
        }
      },
      {
        "id": "S-1-5-21-2593841013-2027981200-818290452-1105",
        "kinds": [
          "Computer",
          "Base"
        ],
        "properties": {
          "IP_Address": "192.168.57.22",
          "smbsigning": false
        }
      }
    ],
    "edges": [
      {
        "kind": "HasSession",
        "start": {
          "value": "S-1-5-21-2593841013-2027981200-818290452-1001",
          "match_by": "id"
        },
        "end": {
          "value": "S-1-5-21-2593841013-2027981200-818290452-1000",
          "match_by": "id"
        }
      },
      {
        "kind": "HasSession",
        "start": {
          "value": "S-1-5-21-2593841013-2027981200-818290452-1105",
          "match_by": "id"
        },
        "end": {
          "value": "S-1-5-21-2593841013-2027981200-818290452-1113",
          "match_by": "id"
        }
      },
      {
        "kind": "HasSession",
        "start": {
          "value": "S-1-5-21-2593841013-2027981200-818290452-1105",
          "match_by": "id"
        },
        "end": {
          "value": "S-1-5-21-2593841013-2027981200-818290452-1121",
          "match_by": "id"
        }
      }
    ]
  }
}

After import in bloodhound:

image

An example of adding a tag with a module can be found in ntlm_reflection.py for example.

Implementation

New helper class OpenGraph that create a new global object opengraph.
This exposed 2 main methods:

  • opengraph.add_tag(id, name, value)
  • opengraph.add_edge(kind, start, end) (optional match by value arguments)

Host info are extrated by adding a new optional proto_flow() step opengraph_host_info() and module can directly call add_*() to add new info.

TODOs

  • Custom cypher cheatsheet for nxc new tags
  • Implementation for other modules
  • Improve CLI flags
  • Optional auto upload of the json via the bloodhound api
  • Currently only add tags to computers' account
  • Other things?

Type of change

Insert an "x" inside the brackets for relevant items (do not delete options)

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Deprecation of feature or functionality
  • This change requires a documentation update
  • This requires a third party update (such as Impacket, Dploot, lsassy, etc)

Setup guide for the review

Test were done on GOAD-Light, run bloodhound first then you can import opengraph json data as any bloudhound data.

Checklist:

Insert an "x" inside the brackets for completed and relevant items (do not delete options)

  • I have ran Ruff against my changes (via poetry: poetry run python -m ruff check . --preview, use --fix to automatically fix what it can)
  • I have added or updated the tests/e2e_commands.txt file if necessary (new modules or features are required to be added to the e2e tests)
  • New and existing e2e tests pass locally with my changes
  • If reliant on changes of third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)

@NeffIsBack
Copy link
Member

Hi, thanks for the PR! Looks like a cool idea.

At the moment nxc is using a connector to directly communicate with the neo4j database directly. This might be changed in the future so that it uses the bloodhound API, but perhaps we could integrate that directly. Related PRs: #616, #472

@Mauriceter
Copy link
Contributor Author

Hi,
Yeah, one of the objectives of this PR is to be able to enriched bloudhound data without the need to be connected to the Neo4j database or having access to the API. I do plan to add an auto upload feature via the API but I will wait for #616 to be merged for this part.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants