Permalink
Browse files

BloodHound "owned" extensions added

  • Loading branch information...
porterhau5 committed Mar 22, 2017
1 parent 103416a commit 68e6755902bb32b6552dc43e51a28c53db3e542a
View
@@ -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
Pre-Compiled BloodHound binaries can be found [here](https://github.com/adaptivethreat/BloodHound/releases).
View
@@ -459,6 +459,23 @@ export default class GraphContainer extends Component {
var id = data.identity.low
var type = data.labels[0]
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 = {
id: id,
type: type,
@@ -472,6 +489,16 @@ export default class GraphContainer extends Component {
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){
node.start = true
node.glyphs.push({
@@ -836,4 +863,4 @@ export default class GraphContainer extends Component {
this.state.sigmaInstance = sigmaInstance;
this.state.design = design;
}
}
}
@@ -20,7 +20,9 @@ export default class ComputerNodeData extends Component {
firstDegreeLocalAdmin: -1,
groupDelegatedLocalAdmin: -1,
derivativeLocalAdmin: -1,
sessions: -1
sessions: -1,
ownedInWave: "None",
ownedMethod: "None"
}
emitter.on('computerNodeClicked', this.getNodeData.bind(this));
@@ -38,7 +40,9 @@ export default class ComputerNodeData extends Component {
sessions: -1,
firstDegreeLocalAdmin: -1,
groupDelegatedLocalAdmin: -1,
derivativeLocalAdmin: -1
derivativeLocalAdmin: -1,
ownedInWave: "None",
ownedMethod: "None"
})
var s1 = driver.session()
@@ -49,6 +53,8 @@ export default class ComputerNodeData extends Component {
var s6 = driver.session()
var s7 = 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})
.then(function(result){
@@ -97,6 +103,22 @@ export default class ComputerNodeData extends Component {
this.setState({'sessions':result.records[0]._fields[0].low})
s8.close()
}.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() {
@@ -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})
}.bind(this)} />
</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>
</div>
);
@@ -17,7 +17,8 @@ export default class GroupNodeData extends Component {
derivativeAdminTo: -1,
unrolledMemberOf: -1,
sessions: -1,
foreignGroupMembership: -1
foreignGroupMembership: -1,
ownedInWave: "None"
}
emitter.on('groupNodeClicked', this.getNodeData.bind(this));
@@ -32,7 +33,8 @@ export default class GroupNodeData extends Component {
derivativeAdminTo: -1,
unrolledMemberOf: -1,
sessions: -1,
foreignGroupMembership: -1
foreignGroupMembership: -1,
ownedInWave: "None"
})
var domain = '@' + payload.split('@').last()
@@ -43,6 +45,7 @@ export default class GroupNodeData extends Component {
var s5 = driver.session()
var s6 = 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})
.then(function(result){
@@ -85,6 +88,16 @@ export default class GroupNodeData extends Component {
this.setState({'foreignGroupMembership':result.records[0]._fields[0].low})
s7.close()
}.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() {
@@ -183,6 +196,19 @@ export default class GroupNodeData extends Component {
"",this.state.label)
}.bind(this)} />
</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>
</div>
);
@@ -20,7 +20,9 @@ export default class UserNodeData extends Component {
firstDegreeLocalAdmin: -1,
groupDelegatedLocalAdmin: -1,
derivativeLocalAdmin: -1,
sessions: -1
sessions: -1,
ownedInWave: "None",
ownedMethod: "None"
}
emitter.on('userNodeClicked', this.getNodeData.bind(this));
@@ -38,7 +40,9 @@ export default class UserNodeData extends Component {
firstDegreeLocalAdmin: -1,
groupDelegatedLocalAdmin: -1,
derivativeLocalAdmin: -1,
sessions: -1
sessions: -1,
ownedInWave: "None",
ownedMethod: "None"
})
var domain = '@' + payload.split('@').last()
@@ -50,6 +54,8 @@ export default class UserNodeData extends Component {
var s5 = driver.session()
var s6 = 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})
.then(function(result){
@@ -92,6 +98,22 @@ export default class UserNodeData extends Component {
this.setState({'sessions':result.records[0]._fields[0].low})
s7.close()
}.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() {
@@ -211,6 +233,25 @@ export default class UserNodeData extends Component {
,this.state.label)
}.bind(this)} />
</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>
</div>
);

0 comments on commit 68e6755

Please sign in to comment.