# DogWhisperer - BloodHound Cypher Cheat Sheet (v2)
Collection of **BloodHound Cypher Query Examples**
* [I- Raw](#i--raw)
* [II- Built-In](#ii--built-in)
* [III- Custom](#iii--custom)
* [IV- DB Manipulation](#iv--db-manipulation)
* [V- REST API](#v--rest-api) _(PowerShell)_
_see also [Neo4j Syntax Reference](http://neo4j.com/docs/cypher-refcard/current/) for more Cypher madness_
This is a **quick guide** and is not ment to be exhaustive or anything. Just a collection of bits and pieces I found here and there. Enough to start **scratching the surface** by looking at some **examples**, but surely not enough to master the full power of **BloodHound Cypher Queries**.
For advance BloodHound Cypher, [check the pros...](#moaaar-stuff)
_Note: All examples in this guide can be run against the [Bloodhound sample database](#sample-db) for testing_
![HostBusters](https://github.com/SadProcessor/Cheats/blob/master/HostBustersWallpaper.jpg)
***
## I- Raw
Can be entered in the **Raw Query** input box at the bottom of the BloodHound UI
### A- Nodes
#### All Nodes
```
MATCH (n) RETURN n
```
#### All User Nodes (Computer/Group/Domain)
```
MATCH (n:User) RETURN n
```
#### Node by Name
```
MATCH (x:Computer {name: 'APOLLO.EXTERNAL.LOCAL'}) RETURN x
```
> Return Computer node name 'APOLLO.EXTERNAL.LOCAL'
```
MATCH (x:Computer) WHERE x.name='APOLLO.EXTERNAL.LOCAL' RETURN x
```
> Same as above, different syntax
```
MATCH (x) WHERE x.name='APOLLO.EXTERNAL.LOCAL' RETURN x
```
> Same without specifying node type (probably less eco-friendly)
#### Node by Property - Property Exists
```
MATCH (n:User) WHERE exists(n.test) RETURN n
```
> Return all nodes that have a property 'test' (value or not)
#### Node by Property - Does Not Exists
```
MATCH (n:User) WHERE NOT exists(n.test) RETURN n
```
> Return all user that dont have a property called 'test'
#### Node by Property - Property Value
```
MATCH (n:User) WHERE n.test='helloWorld' RETURN n
```
> Return all user that have a property 'test' with value 'helloworld'
```
MATCH (X:Group) WHERE X.name CONTAINS 'ADMIN' RETURN X
```
> Return All Groups with 'ADMIN' in name (case sensitive)
```
MATCH (X:Group) WHERE X.name =~ '(?i).*aDMiN.*' RETURN X
```
> Same as above, using (case insensitive) regex
#### Comparaison Operators
List of operators that can be used with the `WHERE` clause
| OPERATOR | SYNTAX |
| ---: | :--- |
| Is Equal To | `=` |
| Is Not Equal To | `<>` |
| Is Less Than | `<` |
| Is Greater Than | `>` |
| Is Less or Equal | `<=` |
| Is Greater or Equal | `>=` |
| Is Null | `IS NULL` |
| Us Not Null |`IS NOT NULL`|
| Prefix Search \* | `STARTS WITH`|
| Suffix Search \* | `ENDS WITH`|
| Inclusion Search \* | `CONTAINS`|| Regex \* | `=~` |
\* String specific
### B- Edges
> TIP: It's possible to **paste multi-lines** in the query box
#### Group Membership - Direct
```
MATCH
(A:User),
(B:Group {name: 'CONTRACTINGH@INTERNAL.LOCAL'}),
p=(A)-[r:MemberOf*1..1]->(B)
RETURN p
```
#### Group Membership - Degree 4
```
MATCH
(A:User),
(B:Group {name: 'CONTRACTINGH@INTERNAL.LOCAL'}),
p=(A)-[r:MemberOf*1..4]->(B)
RETURN p
```
#### Group Membership - Any degree
```
MATCH
(A:User),
(B:Group {name: 'CONTRACTINGH@INTERNAL.LOCAL'}),
p=(A)-[r:MemberOf*1..]->(B)
RETURN p
```
List of **available Edges** types (ACL since 1.3)
| Source Node Type | Edge Type | Target Node Type |
| :---: | :---: | :---: |
| User/Group | `:MemberOf` | Group |
| User/Group | `:AdminTo` | Computer |
| Computer | `:HasSession` | User |
| Domain | `:TrustedBy` | Domain |
| User/Group | `:ForceChangePassword` \* | User |
| User/Group | `:AddMembers` \* | Group |
| User/Group | `:GenericAll` \* | User/Computer/Group |
| User/Group | `:GenericWrite` \* | User/Computer/Group |
| User/Group | `:WriteOwner` \* | User/Computer/Group|
| User/Group | `:WriteDACL` \* | User/Computer/Group |
| User/Group | `:AllExtendedRights` \* | User/Computer/Group |
\* More info on [ACLs](https://wald0.com/?p=112)
### C- Paths
#### Shortest Path from A to B - any Edge type
```
MATCH
(A:User {name: 'ACHAVARIN@EXTERNAL.LOCAL'}),
(B:Group {name: 'DOMAIN ADMINS@INTERNAL.LOCAL'}),
x=shortestPath((A)-[*1..]->(B))
RETURN x
```
#### Shortest Path from A to B - specific Edge types
```
MATCH
(A:User {name: 'ACHAVARIN@EXTERNAL.LOCAL'}),
(B:Group {name: 'DOMAIN ADMINS@INTERNAL.LOCAL'}),
x=shortestPath((A)-[:HasSession|:AdminTo|:MemberOf*1..]->(B))
RETURN x
```
#### Advanced Path
```
MATCH
(A:User),
(B:Computer {name: 'WEBSERVER3.INTERNAL.LOCAL'}),
p=(A)-[r:MemberOf|:AdminTo*1..3]->(B)
RETURN p
```
> All admin user max 3 hops away by group membership from specified target computer
#### All Sortest Paths
```
MATCH
(A:User {name: 'ACHAVARIN@EXTERNAL.LOCAL'}),
(B:Group {name: 'DOMAIN ADMINS@INTERNAL.LOCAL'}),
x=allShortestPaths((A)-[*1..]->(B))
RETURN x
```
The `allShortestPaths()` function works the same way as `shortestPath()` but returns all possible shortest path
(= more ways to get to target with same amount of hops)
/!\ Restrict _Edge type_ / _max hops_ for heavy queries
#### Union
Multiple returned results can be combined into a single output/graph using `UNION` or `UNION ALL`
In this Example a **Path from A to B via C**
```
MATCH
(A:User {name: 'ACHAVARIN@EXTERNAL.LOCAL'}),
(C:User {name: 'CBARCLAY@INTERNAL.LOCAL'}),
x=shortestPath((A)-[*1..]->(C))
RETURN x
UNION ALL
MATCH
(C:User {name: 'CBARCLAY@INTERNAL.LOCAL'}),
(B:Group {name: 'DOMAIN USERS@INTERNAL.LOCAL'}),
x=shortestPath((C)-[*0..]->(B))
RETURN x
```
***
## II- Built-In
Commonly used queries. Found under the Query Tab.
Avoids having to come up with syntax every time.
A lot of cool example in there.
_source code can be found [here](https://github.com/BloodHoundAD/BloodHound/blob/master/src/components/SearchContainer/Tabs/PrebuiltQueries.json)_
Below is there equivalent syntax if you were to insert them in the Query Box.
### All Domain Admin
```
MATCH (n:Group) WHERE n.name =~ "(?i).*DOMAIN ADMINS.*"
WITH n
MATCH (n)<-[r:MemberOf*1..]-(m)
RETURN n,r,m
```
### Shortest Path to Domain Admin
```
MATCH
(n:User),
(m:Group {name: 'DOMAIN ADMINS@INTERNAL.LOCAL'}),
p=shortestPath((n)-[*1..]->(m))
RETURN p
```
### All Logged in Admins
```
MATCH
p=(a:Computer)-[r:HasSession]->(b:User)
WITH a,b,r
MATCH
p=shortestPath((b)-[:AdminTo|MemberOf*1..]->(a))
RETURN b,a,r
```
### Top 10 Users with Most Sessions
```
MATCH
(n:User),(m:Computer),
(n)<-[r:HasSession]-(m)
WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON'
AND NOT n.name='' WITH n,
count(r) as rel_count
order by rel_count desc
LIMIT 10
MATCH
(m)-[r:HasSession]->(n)
RETURN n,r,m
```
### Top 10 Users with Most Local Admin Rights
```
MATCH
(n:User),
(m:Computer),
(n)-[r:AdminTo]->(m)
WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON'
AND NOT n.name='' WITH n,
count(r) as rel_count
order by rel_count desc
LIMIT 10
MATCH
(m)<-[r:AdminTo]-(n)
RETURN n,r,m
```
### Top 10 Computers with Most Admins
```
MATCH
(n:User),
(m:Computer),
(n)-[r:AdminTo]->(m)
WHERE NOT n.name STARTS WITH 'ANONYMOUS LOGON'
AND NOT n.name='' WITH m,
count(r) as rel_count
order by rel_count desc
LIMIT 10
MATCH
(m)<-[r:AdminTo]-(n)
RETURN n,r,m
```
### Users with Foreign Domain Group Membership
```
MATCH
(n:User)
WHERE n.name ENDS WITH ('@' + 'INTERNAL.LOCAL')
WITH n
MATCH (n)-[r:MemberOf]->(m:Group)
WHERE NOT m.name ENDS WITH ('@' + 'INTERNAL.LOCAL')
RETURN n,r,m
```
### Groups with Foreign Group Membership
```
MATCH
(n:Group)
WHERE n.name ENDS WITH '@EXTERNAL.LOCAL'
WITH n
MATCH
(n)-[r:MemberOf*1..]->(m:Group)
WHERE NOT m.name ENDS WITH '@EXTERNAL.LOCAL'
RETURN n,r,m
```
### Map Domain Trusts
```
MATCH (n:Domain) MATCH p=(n)-[r]-() RETURN p
```
***
## III- Custom
Add **homemade** queries to the interface (= ease of use).
**Looks & feels exactly like built-in queries** once added.
To add custom queries, click on the pen icon all the way at the bottom of the query tab.
Open in Notepad. Paste Query.
/!\ Don't forget to save changes.
Will be saved to `C:\Users\\AppData\Roaming\bloodhound\customqueries`.
Click on refresh icon next to pen.
Voila.
_Check [Built-In](#ii--built-in) query source code for syntax examples_
_Check @cptjesus [intro to Cypher](https://blog.cptjesus.com/posts/introtocypher) for more info_
***
## IV- DB Manipulation
Add/Delete Nodes/Properties/Edges to/from DB. (The world is yours...)
### Create Node
```
MERGE (n:User {name: 'bob'})
```
> Creates Node if doesn't already exist
### Add/Update Node property
```
MATCH (n) WHERE n.name='bob' SET n.age=23
```
```
MATCH (n) WHERE n.name='bob' SET n.age=27, n.hair='black', n.sport='Chess-Boxing'
```
> Both Create missing properties, overwrites existing property values
### Remove Node property
```
MATCH (n) WHERE n.name='Bob' REMOVE n.sport
```
```
MATCH (U:User) WHERE EXISTS(U.age) REMOVE U.age
```
```
MATCH (U:User) WHERE EXISTS(U.hair) REMOVE U.age, U.hair RETURN U
```
> Removes property from node (Single Node / multiple Nodes / multiple props)
### Create Edge between Nodes (/!\ direction)
```
MATCH (A:User {name: 'alice'})
MATCH (B:User {name: 'bob'})
CREATE (A)-[r:IsSister]->(B)
```
```
MATCH (A:User {name: 'alice'})
MATCH (B:User {name: 'bob'})
CREATE (A)<-[r:IsBrother]-(B)
```
### Delete Edge
```
MATCH (n:User {name: 'alice'})-[r:IsSister]->(m:User {name: 'bob'})
DELETE r
```
> /!\ not specifying any Edge type will remove all Edges between specified Nodes
### Delete Node (and all connected edges)
```
MATCH (n:User {name: 'bob'}) DETACH DELETE n
```
### Create Node & Properties
**/!\ DANGER ZONE /!\\**
```
MERGE (n:User {name: 'alice', age:23, hair:'black'}) RETURN n
```
> /!\ Use only if Node name doesn't already exist. Prefer safer MERGE/SET command
### Create nodes & Properties & Edges
```
MERGE (A:User {name:bob})-[r:IsBrother]->(B:User {name:'Paul'})
```
```
MERGE (A:User {name:'Jack', age:14, hair:'black'})-[r:IsBrother]->(B:User {name:'Jimmy'})
```
> /!\ Use only if Nodes don't already exist. otherwise MERGE or MERGE/SET each block sperately
**Recommended syntax**:
```
MERGE (A:User {name:'bob'})
MERGE (B:User {name: 'Paul'})
MERGE (A)-[r:IsBrother]->(B)
```
```
MERGE(X:User {name:'Jack'}) SET X.age=14, X.hair='black'
MERGE(Y:User {name:'Jimmy'}) SET Y.age=21, X.hair='black'
MERGE (X)-[r:IsBrother]->(Y)
```
### Nuke DB
```
MATCH (x) DETACH DELETE x
```
> /!\ Simple and efficient. Try at your own (data) expense
***
## V- REST API
Access/Manipulate **BloodHound data via REST API**.
Example here is with PowerShell, but you can apply same method with language of your choosing.
Note: To Access Bloodhound (on localhost) via API, uncomment `#dbms.security.auth_enabled=false` in neo4j config file
### API Call - Basic
```PowerShell
# Prep Vars
$Server = 'localhost'
$Port = '7474'
$Uri = "http://$Server:$Port/db/data/cypher"
$Header = @{'Accept'='application/json; charset=UTF-8';'Content-Type'='application/json'}
$Method = 'POST'
$Body = '----- tbd -----'
# Make Call
$Reply = Invoke-RestMethod -Uri $Uri -Method $Method -Headers $Header -Body $Body
# Node Data
$NodeData = $Reply.data.data
```
> Only need to add `$Body` to build query. The rest stays the same. See examples below...
### A- Node
#### Node View
```Powershell
$Body = '{
"query" : "MATCH (A:Computer {name: {ParamA}}) RETURN A",
"params" : { "ParamA" : "APOLLO.EXTERNAL.LOCAL" }
}'
```
#### Node New
```Powershell
$Body = '{
"query" : "MERGE (n:User {name: {P1}}) RETURN n",
"params" : { "P1" : "bob" }
}'
```
#### Node Add Property / Update Value
```Powershell
$Body = '{
"query" : "MATCH (n) WHERE n.name={Usr} SET n.number={Val}",
"params" : { "Usr" : "bob", "Val" : 8 }
}'
```
#### Node remove property
```Powershell
$Body = '{
"query" : "MATCH (n) WHERE n.name={input} REMOVE n.number",
"params": {"input": "Alice"}
}'
```
#### Node Delete
```Powershell
$Body = '{
"query" : "MATCH (n:User {name: {thisname}}) DETACH DELETE n",
"params": { "thisname" : "bob" }
}'
```
### B- Edge
#### Edge View
```Powershell
$Body = '{
"query" : "MATCH (A:User),(B:User {name: {ParamB}}) MATCH p=(A)-[r:MemberOf*1..1]->(B) RETURN A",
"params" : { "ParamB" : "AUDIT_B@EXTERNAL.LOCAL" }
}'
```
#### Edge Create
```Powershell
$Body = '{
"query" : "MERGE (n:User {name: {U1}}) MERGE (m:User {name: {U2}}) MERGE (m)-[r:IsSister]->(n)",
"params": { "U1" : "bob", "U2" : "alice"}
}'
```
### C- Path
#### Shortest Path
```Powershell
$Body = '{
"query" : "MATCH (A:User {name: {ParamA}}), (B:Group {name: {ParamB}}), x=shortestPath((A)-[*1..]->(B)) RETURN x",
"params" : { "ParamA" : "ACHAVARIN@EXTERNAL.LOCAL", "ParamB" : "DOMAIN ADMINS@EXTERNAL.LOCAL" }
}'
```
#### Putting it all together...
Post to server. Get reply. Parse data. Automate other stuff with that data... Fantastic!
![GreatestDogInTheWorld](https://github.com/SadProcessor/Cheats/blob/master/MostImportantDog.png)
A basic **PowerShell** function to call the API could look like this...
```Powershell
## Function
function Invoke-DogPost{
[CmdletBinding()]
[Alias('DogPost')]
Param(
[Parameter(Mandatory=1)][string]$Body,
[Parameter()][String]$Server='localhost',
[Parameter()][int]$Port=7474,
[Parameter()][Switch]$RawData
)
$Uri = "http://${Server}:${Port}/db/data/cypher"
$Header=@{'Accept'='application/json; charset=UTF-8';'Content-Type'='application/json'}
$Result = Try{Invoke-RestMethod -Uri $Uri -Method Post -Headers $Header -Body $Body}Catch{$Error[0].Exception}
if($RawData){Return $result}
else{Return $Result.data.data}
}
## TestCall
$Body='
{
"query" : "MATCH (A:Computer {name: {ParamA}}) RETURN A",
"params" : { "ParamA" : "APOLLO.EXTERNAL.LOCAL" }
}
'
DogPost $Body
```
> _Works exact same way with a `curl` on linux_
**Attackers Think in Graph... Automations Don't.**
Returning Graphs is not suited for all command line tools (ba dum tsss!), but computers love data...
Return Nodes, or parse Paths into Objects
_Example: (just an idea)_
```
Step StartNode Edge Direction EndNode
---- --------- ---- --------- -------
0 ACHAVARIN@EXTERNAL.LOCAL MemberOf -> INFORMATIONTECHNOLOGY7@EXTERNAL.LOCAL
1 INFORMATIONTECHNOLOGY7@EXTERNAL.LOCAL MemberOf -> DOMAIN ADMINS@EXTERNAL.LOCAL
2 DOMAIN ADMINS@EXTERNAL.LOCAL AdminTo -> DESKTOP11.EXTERNAL.LOCAL
3 DESKTOP11.EXTERNAL.LOCAL HasSession -> AMEADORS@EXTERNAL.LOCAL
4 AMEADORS@EXTERNAL.LOCAL MemberOf -> CONTRACTINGF@INTERNAL.LOCAL
5 CONTRACTINGF@INTERNAL.LOCAL MemberOf -> CONTRACTINGG@INTERNAL.LOCAL
6 CONTRACTINGG@INTERNAL.LOCAL MemberOf -> CONTRACTINGH@INTERNAL.LOCAL
7 CONTRACTINGH@INTERNAL.LOCAL MemberOf -> CONTRACTINGI@INTERNAL.LOCAL
8 CONTRACTINGI@INTERNAL.LOCAL AdminTo -> MANAGEMENT7.INTERNAL.LOCAL
9 MANAGEMENT7.INTERNAL.LOCAL HasSession -> ASANDERS.ADMIN@INTERNAL.LOCAL
10 ASANDERS.ADMIN@INTERNAL.LOCAL MemberOf -> DOMAIN ADMINS@INTERNAL.LOCAL
```
***
## Moaaar Stuff
### Useful links
Links to more info on/around the topic
#### **Github**
- [BloodHound Code](https://github.com/BloodHoundAD/BloodHound)
- [Wiki](https://github.com/BloodHoundAD/BloodHound/wiki)
#### **Twitter**
- [@harmj0y](https://twitter.com/harmj0y) Click on Follow...
- [@_wald0](https://twitter.com/_wald0) Click on Follow...
- [@CptJesus](https://twitter.com/CptJesus) Click on Follow...
- [@Porterhau5](https://twitter.com/porterhau5) Click on Follow
#### **Slack**
- [Channel Here](https://bloodhoundhq.slack.com/messages/general/) get invite [here](https://bloodhoundgang.herokuapp.com/)
#### **Blog**
- [Introducing BloodHound](https://wald0.com/?p=68) by @_wald0
- [Intro to Cypher](https://blog.cptjesus.com/posts/introtocypher) by @CptJesus
- [ACL Attack Paths](https://wald0.com/?p=112) by @_wald0
- [Extending Bloodhound...](https://porterhau5.com/blog/extending-bloodhound-track-and-visualize-your-compromise/) by @Porterhau5
- [Representing Password Reuse in BloodHound](https://porterhau5.com/blog/representing-password-reuse-in-bloodhound/) by @Porterhau5
#### **Video**
- [Six degrees of Domain Admin](https://youtu.be/lxd2rerVsLo) by @_wald0 & Co - BSides LV 2016
- [Here Be Dragons...](https://youtu.be/z8thoG7gPd0) by @_wald0 & Co - DerbyCon 2017
- [GoFetch](https://youtu.be/lbJPCnjQxCU) by @TaltheMaor @TalBerySec - BlackHat 2017
- [Extending Bloodhound for RedTeamers](https://youtu.be/Pn7GWRXfgeI) by @Porterhau5 - WWHF 2017
- [Requiem for an Admin](https://youtu.be/uMg18TvLAcE?list=PLdhDuST3OlrNRull1hITtWVYzIQPfWjXD) by @SadProcessor - BSides Amsterdam 2017 (Shameless Plug)
#### **Noe4j**
- [Cypher Reference Card](http://neo4j.com/docs/cypher-refcard/current/)
- [Cypher Syntax Online Documentation](https://neo4j.com/docs/developer-manual/current/cypher/syntax/)
- [Common cypher confusions](https://neo4j.com/blog/common-confusions-cypher/)
#### **More Cool Tools**
- [CypherDog/DogStrike](https://github.com/SadProcessor/EmpireDog/tree/master/Modules) PowerShell Module to interact with BloodHound (& Empire) API (1.4 soon...)
- [GoFetch](https://github.com/GoFetchAD/GoFetch) Automation of lateral movement with BloodHound & Empire
- [AngryPuppy](https://www.mdsec.co.uk/2017/08/introducing-angrypuppy/) BH & CS automation by @Vysec and @Spartan
***
### Sample DB
Want to play with BloodHound but don't have an AD at hand? Install the supplied sample DB.
With bloodhound/neo4j stopped:
- Copy `BloodHoundExampleDB.graphdb` folder to `[...]/neo4j/data/database/`
- Open `[...]/neo4j/conf/ne4j` in text editor
- Uncomment and set db name to mount to `dbms.active_database=BloodHoundExampleDB.graphdb`
- Uncomment `#dbms.allow_upgrade=true`
- Save changes
- start neo4j/bloodhound
(you should see a graph from sample data)
- Re-comment `dbms.allow_upgrade=true` and Save change
- Done
_For automated BloodHound install script check [here](https://github.com/SadProcessor/SomeStuff/blob/master/BloodHoundw64_LTI.ps1) (windows64)_
---
### KeyBoard Shortcuts
| KEY | ACTION |
| :---: | :--- |
| `CTRL` | Node **labels** ON/OFF |
| `CTRL`+`SPACE`| Node **Search** Dialog Box |
| `CRTL`+`R` | **Restart** BloodHound |
| `CTRL`+`SHIFT`+`I`| Console **Debug** |
Can use `CTRL`+`Z` and `CTRL`+`Y` in **Query Box** as kind of history function
_Note: Debuging queries is easier via neo4j browser (http://localhost:7474/Browser)_
---
### UI Tweaks
:( _Made some cool ones (Dark Theme). Didn't document process. Deleted VM. Will have to try that again later..._
:) _Check out @porterhau5 in [links](#moaaar-stuff) for some awesome stuff_
***
That's all I got for now. Like I said, this is just scratching the surface of Cypher queries. You can get quite funky with it (try googling for non-bloodhound cypher stuff... quite cool). I'll keep digging.
Hope this will be useful to someone somewhere. Now you can take your Dog for a walk.
Hack the Planet...