BloodHound API Bug: AdminTo Edges Not Created

by ADMIN 46 views

Hey guys, let's dive into a quirky issue we've been seeing with BloodHound's Graph API. Specifically, it seems like the AdminTo edges aren't being created as expected when enriching data. This can be a real headache when you're trying to map out those critical admin relationships in your environment. So, let’s break down the problem, how to reproduce it, and what the expected behavior should be.

Description of the Issue

In this section, let's clearly define the problem. BloodHound, for those who might be new to it, is an awesome tool for mapping relationships in Active Directory and beyond. One of its key features is the ability to ingest data and create a graph of connections, helping you visualize attack paths. The Graph API allows you to programmatically add data, which is super useful for automation and integration with other tools. However, when attempting to add edges with the AdminTo kind, things don't seem to work as expected. Specifically, the edges aren't being created, which can throw a wrench in your analysis.

I've been trying to enrich my data with local Admin Information that I've gathered from another source, leveraging the Graph API. I'm currently running BloodHound version 8.2.0 with the pg driver for PostgreSQL. I made sure I pulled the latest Docker images just to be certain I was working with the most up-to-date setup. The core issue is this: when I use a tag other than AdminTo, the data enrichment works perfectly. Custom and non-custom edges are attached to the nodes exactly where they should be. Let me show you an example. If I use the following JSON payload with the MemberOf kind:

{
    "metadata":{"source_kind":"TEST"},
    "graph": {
        "edges": [{
            "kind": "MemberOf",
            "start": {
                "value": "S-1-5-XXXXX",
                "match_by": "id"
            },
            "end": {
                "value": "S-1-5-YYYYYYY",
                "match_by": "id"
            }
        }],
        "nodes": []
    }
}

Using MemberOf, the node correctly identifies a local admin, and an outbound edge is created just as expected. This behavior is consistent across various edge kinds, except for AdminTo. When I switch the kind to AdminTo, the file uploads and ingests without any errors, which is misleading. However, the critical information isn't populated, and no outbound AdminTo edge is created. This is a huge issue because accurately mapping admin relationships is crucial for understanding privilege escalation paths. It's like having a map with missing roads – you can't reliably plan your route! Moreover, there are no error messages or warnings to indicate that something went wrong, making it even more challenging to troubleshoot. This inconsistent behavior makes it difficult to trust the data being ingested, especially when dealing with critical relationships like AdminTo. This is a major pain point, guys, and we need to get it sorted!

{
    "metadata":{"source_kind":"TEST"},
    "graph": {
        "edges": [{
            "kind": "AdminTo",
            "start": {
                "value": "S-1-5-XXXXXX",
                "match_by": "id"
            },
            "end": {
                "value": "S-1-5-YYYYYY",
                "match_by": "id"
            }
        }],
        "nodes": []
    }
}

Components Affected

This bug seems to be impacting several components of BloodHound, which makes it a pretty significant issue. We're likely looking at problems in the UI, where the edges aren't being displayed as expected. The API is also suspect since it's not correctly processing the AdminTo edge creation. The data stores themselves, both Neo4j (if you're using it) and PostgreSQL, could be involved if they're not receiving or storing the data correctly. Pinpointing the exact component is crucial for a proper fix, but it's clear this issue has a wide reach within the application.

  • UI
  • API
  • Neo4j
  • PostgreSQL

Steps to Reproduce

To reproduce this pesky bug, follow these simple steps. First, you need to prepare a JSON file with the structure I showed you earlier, making sure to set the edge kind to AdminTo. Include the start and end nodes with appropriate values, using the id for matching. Next, upload this JSON file to BloodHound using the Graph API. You can do this through the UI or programmatically, whichever you prefer. Once the file is uploaded and ingested, navigate to the node specified as the start in your JSON. Now, this is the crucial part: check the outbound edges for this node in the UI. You should expect to see an AdminTo edge, but if you're experiencing the bug, it won't be there. That's it! You've successfully reproduced the issue. This straightforward process should help anyone confirm they're encountering the same problem, and it’s a good starting point for debugging. This clear and concise reproduction process is essential for the development team to understand and address the issue effectively.

  1. Upload the JSON files as specified with the AdminTo edge kind.
  2. Check outbound Edges for the given Node in the UI.

Expected Behavior

So, what should happen when you upload a JSON file with an AdminTo edge? The expected behavior is pretty straightforward: an outbound edge with the AdminTo tag should pop up in the BloodHound UI. This means that when you inspect the node that's supposed to be administering another node, you should see a clear visual connection indicating this relationship. It’s all about having that clear, visual representation of who has admin rights over what. This is crucial for understanding potential attack paths and privilege escalation opportunities within the network. If BloodHound is working correctly, these edges should be created reliably and consistently. It’s this consistency that allows security professionals to trust the data and make informed decisions about their security posture. Without it, the tool loses a significant portion of its value, and that's something we definitely want to avoid.

Actual Behavior

Okay, now let's talk about the actual behavior, which, as we've seen, is not what we expect. Instead of creating the AdminTo outbound edge, BloodHound simply doesn't create it. The file uploads, it ingests, and everything seems to go smoothly on the surface, but the crucial link is missing. This can lead to a misrepresentation of the network's security posture, as critical admin relationships are not visualized. Think of it like this: you're trying to build a puzzle, and some of the key pieces are just vanishing. It makes the whole picture incomplete and misleading. This discrepancy between expected and actual behavior is exactly what makes this bug so problematic. It gives a false sense of security, as you might assume all admin relationships are correctly mapped when, in reality, some are not. Identifying this gap is the first step in fixing it, and it highlights the importance of thorough testing and validation when dealing with security tools.

For AdminTo kinds, no outbound edge is created, which is a major issue for accurately mapping admin relationships.

Environment Information

To help nail down this bug, here's the environment I'm working in. The more details we have, the better! I'm running BloodHound version 8.2.0, and here's the Docker image info:

{
    "Id": "sha256:aab3b56fbf33665eab01c5be9c2117587c3a49817833efa26a25d34009c8f2fb",
    "RepoTags": [
        "specterops/bloodhound:latest"
    ],
    "RepoDigests": [
        "specterops/bloodhound@sha256:f567ffa8409098ffe99a8877ea340b0fcc3a9770c6be50f9cf843151dadfdb8e"
    ]
}

My operating system is Ubuntu 24.04.2 LTS (Noble Numbat), which is pretty fresh off the press. Knowing the OS can sometimes provide clues, especially if there are compatibility issues. The specifics are:

Distributor ID:	Ubuntu
Description:	Ubuntu 24.04.2 LTS
Release:	24.04
Codename:	noble

For the database, I'm using a Postgres container, and here's its info. Since BloodHound relies on the database to store the graph data, this is a critical piece of the puzzle. If there's an issue with how Postgres is handling the AdminTo edges, it could explain the problem.

{
    "Id": "sha256:4d79c308564d282500888ff3b555f2f256ed3369b6b0a8a13f38393aa693f5eb",
    "RepoTags": [
        "postgres:16"
    ],
    "RepoDigests": [
        "postgres@sha256:fb9aa6d07b0fe53e94e8470977f316d1cdb39b8cdb33c45279ff0bbe32067c25"
    ]
}

And finally, I'm running Docker version 28.1.1+1, build 068a01e. Docker versions can sometimes introduce bugs or compatibility issues, so it's good to have this on record. Providing all this information ensures that the developers have a clear picture of the environment where the bug is occurring, which can significantly speed up the debugging process. The more details we provide, the better our chances of getting a fix quickly!

Docker version 28.1.1+1, build 068a01e

Related Issues

Interestingly, there's a related issue already reported on GitHub (https://github.com/SpecterOps/BloodHound/issues/686), which suggests this might be a known problem. It's always a good idea to check for related issues before diving too deep, as someone else might have already encountered the same bug and possibly even found a workaround. Reviewing this related issue could provide additional context or insights into the problem. It might even offer a temporary solution or a better understanding of the root cause. Cross-referencing issues is a smart move in bug hunting, as it leverages the collective knowledge of the community and can save time and effort in the long run.

Contributor Checklist

Before wrapping up, I've made sure to go through the contributor checklist to ensure this bug report is thorough and helpful. This is a good practice for anyone reporting issues, as it helps maintain the quality of bug reports and makes it easier for developers to address them. Let's run through it:

  • [x] I have searched the issue tracker to ensure this bug hasn't been reported before or is not already being addressed.
  • [x] I have provided clear steps to reproduce the issue.
  • [x] I have included relevant environment information details.
  • [x] I have attached necessary supporting documents (in this case, the JSON examples).
  • [x] I have checked that any JSON files I am attempting to upload to BloodHound are valid.

By ticking all these boxes, I'm confident that this report provides a solid foundation for investigating and resolving this AdminTo edge issue in BloodHound. Let’s hope we can get this sorted soon, guys, so we can get back to mapping those admin relationships accurately!