Skip to content

secdev02/HostHound

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HostHound MVP

HostHound is a defensive local privilege-escalation graph explorer inspired by BloodHound-style path analysis. It ingests local host facts, derives relationships, and lets analysts query paths from a real user context to privileged targets such as NT AUTHORITY\\SYSTEM or local Administrators.

This MVP now has a module-first design for community contributions, similar in spirit to WINPEAS checks:

  • collector output is normalized graph JSON;
  • the graph server is completely Python: Flask + NetworkX + Python analyzer modules;
  • the browser graph visualization uses D3.js force-directed rendering;
  • built-in and community analyzer modules use the same contract;
  • community checks can be dropped into a plugin directory and loaded with --plugin-dir.

Layout

hosthound/
  app/
    server.py                         # Python Flask server
    requirements.txt
    hosthound/
      context.py                      # ctx API passed to modules
      plugins.py                      # Python module loader
    modules/                          # built-in Python analyzer modules
      always_install_elevated.py
      dangerous_token_privileges.py
      inherited_group_edges.py
      writable_privileged_execution.py
    static/index.html                 # D3.js browser UI served by Flask
  collector/
    Invoke-HostHoundCollector.ps1     # Existing Windows collector
    modules/                          # PowerShell collector modules
  docs/
    MODULE_DEVELOPMENT.md             # Legacy PowerShell collector module notes
    PYTHON_MODULE_DEVELOPMENT.md      # Python server/analyzer module guide
  templates/
    example_python_analyzer_module.py
  samples/
    sample-windows.json

Run the Python server

cd hosthound/app
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python server.py --input ../samples/sample-windows.json

Open:

http://127.0.0.1:8000

The server exposes:

GET  /api/health
GET  /api/graph
GET  /api/findings
GET  /api/modules
GET  /api/search?q=alice
GET  /api/paths?source=<node-id>&target=<optional-node-id>
POST /api/reload

Load community modules

Create a directory outside the app, copy in a Python analyzer module, and run:

python app/server.py \
  --input samples/sample-windows.json \
  --plugin-dir community-modules

A minimal module looks like this:

MODULE = {
    "name": "community.example_check",
    "kind": "analyzer",
    "version": "0.1.0",
    "description": "Example community check.",
}


def analyze(ctx):
    for source, target, data in list(ctx.graph.edges(data=True)):
        if data.get("type") == "CAN_WRITE":
            ctx.finding(
                severity="medium",
                title="Writable object observed",
                description="A collected principal can write to an object.",
                evidence=f"{source} can write {target}.",
                remediation="Review and remove unnecessary write permissions.",
                related=[source, target],
            )

See docs/PYTHON_MODULE_DEVELOPMENT.md and templates/example_python_analyzer_module.py.

Built-in Python analyzer modules

builtin.writable_privileged_execution
  Derives CAN_ESCALATE_TO when a writable object is executed/loaded by a privileged runtime.

builtin.inherited_group_edges
  Propagates group capabilities to group members for accurate user-to-admin path queries.

builtin.dangerous_token_privileges
  Flags SeImpersonatePrivilege, SeDebugPrivilege, SeBackupPrivilege, and similar Windows token rights.

builtin.always_install_elevated
  Flags AlwaysInstallElevated policy relationships emitted by collectors.

Collect on Windows

Set-ExecutionPolicy -Scope Process Bypass -Force
.\collector\Invoke-HostHoundCollector.ps1 -OutFile .\hosthound-host.json -Pretty

Then ingest the result:

python app/server.py --input hosthound-host.json

Defensive boundary

HostHound is intentionally an audit and explanation tool. Modules should gather or analyze facts, emit graph relationships and findings, and provide remediation. They should not exploit paths, deploy payloads, or modify host state.

JSON encoding compatibility

The Flask server reads collection files with utf-8-sig, so JSON exported from Windows PowerShell with a UTF-8 BOM and JSON exported without a BOM both import correctly. The PowerShell collector now writes UTF-8 without BOM when possible.

BloodHound OpenGraph export

HostHound can export its native collection JSON into a BloodHound OpenGraph payload. BloodHound OpenGraph payloads use this top-level shape:

{
  "graph": {
    "nodes": [],
    "edges": []
  },
  "metadata": {
    "source_kind": "HostHound"
  }
}

OpenGraph support is intended for BloodHound Enterprise or BloodHound Community v8+ with a PostgreSQL graph database. Save the exported payload as .json and upload/import it as OpenGraph data in BloodHound.

Export from the Python CLI

cd hosthound/app
python export_opengraph.py \
  --input ../collector/hosthound-WINDEV.json \
  --output ../collector/hosthound-WINDEV-opengraph.json

You can also convert a directory of HostHound JSON files into one OpenGraph payload:

python export_opengraph.py \
  --input ../collections \
  --output ../collections/hosthound-fleet-opengraph.json

Export from the Flask server command

cd hosthound/app
python server.py \
  --input ../collector/hosthound-WINDEV.json \
  --export-opengraph ../collector/hosthound-WINDEV-opengraph.json

Export from the running server

Start the server normally, then request:

GET http://127.0.0.1:8000/api/opengraph

The OpenGraph exporter maps HostHound objects like this:

HostHound native node type -> OpenGraph kinds
Host                     -> ["HostHoundHost", "HostHound"]
User                     -> ["HostHoundUser", "HostHound"]
Group                    -> ["HostHoundGroup", "HostHound"]
Service                  -> ["HostHoundService", "HostHound"]
ScheduledTask            -> ["HostHoundScheduledTask", "HostHound"]
File                     -> ["HostHoundFile", "HostHound"]
Privilege                -> ["HostHoundPrivilege", "HostHound"]
Finding                  -> ["HostHoundFinding", "HostHound"]

Relationship types are mapped to namespaced OpenGraph kinds such as:

MEMBER_OF       -> HostHoundMemberOf
CAN_WRITE       -> HostHoundCanWrite
EXECUTED_BY     -> HostHoundExecutedBy
RUNS_AS         -> HostHoundRunsAs
CAN_ESCALATE_TO -> HostHoundCanEscalateTo

Each OpenGraph edge uses ID matching:

{
  "kind": "HostHoundCanWrite",
  "start": { "value": "Group:BUILTIN\\Users", "match_by": "id" },
  "end": { "value": "File:C:\\HostHoundLab\\WritableService\\HostHoundLabSvc.exe", "match_by": "id" }
}

Findings are exported as HostHoundFinding nodes by default. Use --no-findings with export_opengraph.py or --no-opengraph-findings with server.py to omit them.

About

Local Privilege Escalation like BloodHound for Local Priv Esc Paths

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages