Skip to content

Commit

Permalink
BloodHound "owned" extensions added
Browse files Browse the repository at this point in the history
  • Loading branch information
porterhau5 committed Mar 22, 2017
1 parent 103416a commit 68e6755
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 7 deletions.
7 changes: 7 additions & 0 deletions README.md
@@ -1,3 +1,10 @@
#BloodHound Extensions
The changes in this fork are a product of the ideas here: http://porterhau5.com/blog/extending-bloodhound-track-and-visualize-your-compromise/

This modified version of BloodHound is intended to be used with the Custom Queries and `bh-owned.rb` script found here: https://github.com/porterhau5/BloodHound-Owned

The Pre-Compiled Binaries have not been updated. Using this app will require building from source. See the Quickstart guide here for more details: https://github.com/porterhau5/BloodHound-Owned#quickstart

#Downloading BloodHound Binaries #Downloading BloodHound Binaries
Pre-Compiled BloodHound binaries can be found [here](https://github.com/adaptivethreat/BloodHound/releases). Pre-Compiled BloodHound binaries can be found [here](https://github.com/adaptivethreat/BloodHound/releases).


Expand Down
29 changes: 28 additions & 1 deletion src/components/Graph.jsx
Expand Up @@ -459,6 +459,23 @@ export default class GraphContainer extends Component {
var id = data.identity.low var id = data.identity.low
var type = data.labels[0] var type = data.labels[0]
var label = data.properties.name var label = data.properties.name
var statement = params.statement
var wave = null
var owned = null
var propswave = data.properties.wave
var propsowned = data.properties.owned
var propsresult = params.props.result
switch (statement) {
case 'MATCH (n)-[r]->(m) WHERE n.wave<=toInt({result}) RETURN n,r,m':
if (propswave == propsresult) wave = propsresult
break;
case 'MATCH (n),(m:Group {name:{result}}),p=shortestPath((n)-[*1..]->(m)) WHERE exists(n.owned) RETURN p':
if (propsowned !== undefined) owned = true
break;
case 'MATCH (n:Group) WHERE n.name =~ {name} WITH n MATCH p=(n)<-[r:MemberOf*1..]-(m) WHERE exists(m.owned) RETURN nodes(p),relationships(p)':
if (propsowned !== undefined) owned = true
break;
}
var node = { var node = {
id: id, id: id,
type: type, type: type,
Expand All @@ -472,6 +489,16 @@ export default class GraphContainer extends Component {
y: Math.random() y: Math.random()
} }


if ((wave !== null) || (owned !== null)){
node.glyphs.push({
'position': 'top-left',
'font': 'FontAwesome',
'content': '\uF0E7',
'fillColor': '#C900FF',
'fontScale': 1.5
})
}

if (label === params.start){ if (label === params.start){
node.start = true node.start = true
node.glyphs.push({ node.glyphs.push({
Expand Down Expand Up @@ -836,4 +863,4 @@ export default class GraphContainer extends Component {
this.state.sigmaInstance = sigmaInstance; this.state.sigmaInstance = sigmaInstance;
this.state.design = design; this.state.design = design;
} }
} }
45 changes: 43 additions & 2 deletions src/components/SearchContainer/Tabs/ComputerNodeData.jsx
Expand Up @@ -20,7 +20,9 @@ export default class ComputerNodeData extends Component {
firstDegreeLocalAdmin: -1, firstDegreeLocalAdmin: -1,
groupDelegatedLocalAdmin: -1, groupDelegatedLocalAdmin: -1,
derivativeLocalAdmin: -1, derivativeLocalAdmin: -1,
sessions: -1 sessions: -1,
ownedInWave: "None",
ownedMethod: "None"
} }


emitter.on('computerNodeClicked', this.getNodeData.bind(this)); emitter.on('computerNodeClicked', this.getNodeData.bind(this));
Expand All @@ -38,7 +40,9 @@ export default class ComputerNodeData extends Component {
sessions: -1, sessions: -1,
firstDegreeLocalAdmin: -1, firstDegreeLocalAdmin: -1,
groupDelegatedLocalAdmin: -1, groupDelegatedLocalAdmin: -1,
derivativeLocalAdmin: -1 derivativeLocalAdmin: -1,
ownedInWave: "None",
ownedMethod: "None"
}) })


var s1 = driver.session() var s1 = driver.session()
Expand All @@ -49,6 +53,8 @@ export default class ComputerNodeData extends Component {
var s6 = driver.session() var s6 = driver.session()
var s7 = driver.session() var s7 = driver.session()
var s8 = driver.session() var s8 = driver.session()
var s9 = driver.session()
var s10 = driver.session()


s1.run("MATCH (a)-[b:AdminTo]->(c:Computer {name:{name}}) RETURN count(a)", {name:payload}) s1.run("MATCH (a)-[b:AdminTo]->(c:Computer {name:{name}}) RETURN count(a)", {name:payload})
.then(function(result){ .then(function(result){
Expand Down Expand Up @@ -97,6 +103,22 @@ export default class ComputerNodeData extends Component {
this.setState({'sessions':result.records[0]._fields[0].low}) this.setState({'sessions':result.records[0]._fields[0].low})
s8.close() s8.close()
}.bind(this)) }.bind(this))

s9.run("MATCH (n {name:{name}}) RETURN n.wave", {name:payload})
.then(function(result){
if (result.records[0]._fields[0] != null) {
this.setState({'ownedInWave':result.records[0]._fields[0].low})
}
s9.close()
}.bind(this))

s10.run("MATCH (n {name:{name}}) RETURN n.owned", {name:payload})
.then(function(result){
if (result.records[0]._fields[0] != null) {
this.setState({'ownedMethod':result.records[0]._fields[0]})
}
s10.close()
}.bind(this))
} }


render() { render() {
Expand Down Expand Up @@ -223,6 +245,25 @@ export default class ComputerNodeData extends Component {
"MATCH (m:Computer {name:{name}})-[r:HasSession]->(n:User) WITH n,r,m WHERE NOT n.name ENDS WITH '$' RETURN n,r,m", {name: this.state.label}) "MATCH (m:Computer {name:{name}})-[r:HasSession]->(n:User) WITH n,r,m WHERE NOT n.name ENDS WITH '$' RETURN n,r,m", {name: this.state.label})
}.bind(this)} /> }.bind(this)} />
</dd> </dd>
<br />
<dt>
Owned in Wave
</dt>
<dd>
<NodeALink
ready={this.state.ownedInWave !== -1}
value={this.state.ownedInWave}
click={function(){
emitter.emit('query', "OPTIONAL MATCH (n1:User {wave:{wave}}) WITH collect(distinct n1) as c1 OPTIONAL MATCH (n2:Computer {wave:{wave}}) WITH collect(distinct n2) + c1 as c2 OPTIONAL MATCH (n3:Group {wave:{wave}}) WITH c2, collect(distinct n3) + c2 as c3 UNWIND c2 as n UNWIND c3 as m MATCH (n)-[r]->(m) RETURN n,r,m", {wave:this.state.ownedInWave}
,this.state.label)
}.bind(this)} />
</dd>
<dt>
Owned via Method
</dt>
<dd>
{this.state.ownedMethod}
</dd>
</dl> </dl>
</div> </div>
); );
Expand Down
30 changes: 28 additions & 2 deletions src/components/SearchContainer/Tabs/GroupNodeData.jsx
Expand Up @@ -17,7 +17,8 @@ export default class GroupNodeData extends Component {
derivativeAdminTo: -1, derivativeAdminTo: -1,
unrolledMemberOf: -1, unrolledMemberOf: -1,
sessions: -1, sessions: -1,
foreignGroupMembership: -1 foreignGroupMembership: -1,
ownedInWave: "None"
} }


emitter.on('groupNodeClicked', this.getNodeData.bind(this)); emitter.on('groupNodeClicked', this.getNodeData.bind(this));
Expand All @@ -32,7 +33,8 @@ export default class GroupNodeData extends Component {
derivativeAdminTo: -1, derivativeAdminTo: -1,
unrolledMemberOf: -1, unrolledMemberOf: -1,
sessions: -1, sessions: -1,
foreignGroupMembership: -1 foreignGroupMembership: -1,
ownedInWave: "None"
}) })


var domain = '@' + payload.split('@').last() var domain = '@' + payload.split('@').last()
Expand All @@ -43,6 +45,7 @@ export default class GroupNodeData extends Component {
var s5 = driver.session() var s5 = driver.session()
var s6 = driver.session() var s6 = driver.session()
var s7 = driver.session() var s7 = driver.session()
var s8 = driver.session()


s1.run("MATCH (a)-[b:MemberOf]->(c:Group {name:{name}}) RETURN count(a)", {name:payload}) s1.run("MATCH (a)-[b:MemberOf]->(c:Group {name:{name}}) RETURN count(a)", {name:payload})
.then(function(result){ .then(function(result){
Expand Down Expand Up @@ -85,6 +88,16 @@ export default class GroupNodeData extends Component {
this.setState({'foreignGroupMembership':result.records[0]._fields[0].low}) this.setState({'foreignGroupMembership':result.records[0]._fields[0].low})
s7.close() s7.close()
}.bind(this)) }.bind(this))

s8.run("MATCH (n {name:{name}}) RETURN n.wave", {name:payload})
.then(function(result){
if (result.records[0]._fields[0] != null) {
this.setState({'ownedInWave':result.records[0]._fields[0].low})
} else {
this.setState({'ownedInWave':'None'})
}
s8.close()
}.bind(this))
} }


render() { render() {
Expand Down Expand Up @@ -183,6 +196,19 @@ export default class GroupNodeData extends Component {
"",this.state.label) "",this.state.label)
}.bind(this)} /> }.bind(this)} />
</dd> </dd>
<br />
<dt>
Owned in Wave
</dt>
<dd>
<NodeALink
ready={this.state.ownedInWave !== -1}
value={this.state.ownedInWave}
click={function(){
emitter.emit('query', "OPTIONAL MATCH (n1:User {wave:{wave}}) WITH collect(distinct n1) as c1 OPTIONAL MATCH (n2:Computer {wave:{wave}}) WITH collect(distinct n2) + c1 as c2 OPTIONAL MATCH (n3:Group {wave:{wave}}) WITH c2, collect(distinct n3) + c2 as c3 UNWIND c2 as n UNWIND c3 as m MATCH (n)-[r]->(m) RETURN n,r,m", {wave:this.state.ownedInWave}
,this.state.label)
}.bind(this)} />
</dd>
</dl> </dl>
</div> </div>
); );
Expand Down
45 changes: 43 additions & 2 deletions src/components/SearchContainer/Tabs/UserNodeData.jsx
Expand Up @@ -20,7 +20,9 @@ export default class UserNodeData extends Component {
firstDegreeLocalAdmin: -1, firstDegreeLocalAdmin: -1,
groupDelegatedLocalAdmin: -1, groupDelegatedLocalAdmin: -1,
derivativeLocalAdmin: -1, derivativeLocalAdmin: -1,
sessions: -1 sessions: -1,
ownedInWave: "None",
ownedMethod: "None"
} }


emitter.on('userNodeClicked', this.getNodeData.bind(this)); emitter.on('userNodeClicked', this.getNodeData.bind(this));
Expand All @@ -38,7 +40,9 @@ export default class UserNodeData extends Component {
firstDegreeLocalAdmin: -1, firstDegreeLocalAdmin: -1,
groupDelegatedLocalAdmin: -1, groupDelegatedLocalAdmin: -1,
derivativeLocalAdmin: -1, derivativeLocalAdmin: -1,
sessions: -1 sessions: -1,
ownedInWave: "None",
ownedMethod: "None"
}) })


var domain = '@' + payload.split('@').last() var domain = '@' + payload.split('@').last()
Expand All @@ -50,6 +54,8 @@ export default class UserNodeData extends Component {
var s5 = driver.session() var s5 = driver.session()
var s6 = driver.session() var s6 = driver.session()
var s7 = driver.session() var s7 = driver.session()
var s8 = driver.session()
var s9 = driver.session()


s1.run("MATCH (n:Group) WHERE NOT n.name ENDS WITH {domain} WITH n MATCH (m:User {name:{name}}) MATCH (m)-[r:MemberOf*1..]->(n) RETURN count(n)", {name:payload, domain: domain}) s1.run("MATCH (n:Group) WHERE NOT n.name ENDS WITH {domain} WITH n MATCH (m:User {name:{name}}) MATCH (m)-[r:MemberOf*1..]->(n) RETURN count(n)", {name:payload, domain: domain})
.then(function(result){ .then(function(result){
Expand Down Expand Up @@ -92,6 +98,22 @@ export default class UserNodeData extends Component {
this.setState({'sessions':result.records[0]._fields[0].low}) this.setState({'sessions':result.records[0]._fields[0].low})
s7.close() s7.close()
}.bind(this)) }.bind(this))

s8.run("MATCH (n {name:{name}}) RETURN n.wave", {name:payload})
.then(function(result){
if (result.records[0]._fields[0] != null) {
this.setState({'ownedInWave':result.records[0]._fields[0].low})
}
s8.close()
}.bind(this))

s9.run("MATCH (n {name:{name}}) RETURN n.owned", {name:payload})
.then(function(result){
if (result.records[0]._fields[0] != null) {
this.setState({'ownedMethod':result.records[0]._fields[0]})
}
s9.close()
}.bind(this))
} }


render() { render() {
Expand Down Expand Up @@ -211,6 +233,25 @@ export default class UserNodeData extends Component {
,this.state.label) ,this.state.label)
}.bind(this)} /> }.bind(this)} />
</dd> </dd>
<br />
<dt>
Owned in Wave
</dt>
<dd>
<NodeALink
ready={this.state.ownedInWave !== -1}
value={this.state.ownedInWave}
click={function(){
emitter.emit('query', "OPTIONAL MATCH (n1:User {wave:{wave}}) WITH collect(distinct n1) as c1 OPTIONAL MATCH (n2:Computer {wave:{wave}}) WITH collect(distinct n2) + c1 as c2 OPTIONAL MATCH (n3:Group {wave:{wave}}) WITH c2, collect(distinct n3) + c2 as c3 UNWIND c2 as n UNWIND c3 as m MATCH (n)-[r]->(m) RETURN n,r,m", {wave:this.state.ownedInWave}
,this.state.label)
}.bind(this)} />
</dd>
<dt>
Owned via Method
</dt>
<dd>
{this.state.ownedMethod}
</dd>
</dl> </dl>
</div> </div>
); );
Expand Down

0 comments on commit 68e6755

Please sign in to comment.