Support drag and drop file ingestion

master
Rohan Vazarkar 2018-07-16 15:00:37 -04:00
parent d96651635d
commit f093a6863c
8 changed files with 96 additions and 34 deletions

View File

@ -2,7 +2,6 @@ const electron = require('electron')
var platform = require('os').platform()
// Module to control application life.
const app = electron.app
const Tray = electron.Tray
const Menu = electron.Menu
// Module to create native browser window.

View File

@ -1,6 +1,6 @@
{
"name": "bloodhound",
"version": "2.0",
"version": "2.0.0",
"description": "Graph Theory for Active Directory",
"keywords": [
"Graph",
@ -58,6 +58,7 @@
"eventemitter2": "^4.1.0",
"jquery": "^3.2.1",
"linkurious": "^1.5.1",
"magic-number": "^0.1.6",
"mustache": "^2.3.0",
"neo4j-driver": "^1.6.2",
"prop-types": "^15.6.2",

View File

@ -20,6 +20,19 @@ import About from "./components/Modals/About.jsx";
import { CSSTransition, TransitionGroup } from "react-transition-group";
export default class AppContainer extends Component {
componentDidMount(){
document.addEventListener('dragover', function(event){
event.preventDefault();
return false;
}, false)
document.addEventListener('drop', function(event){
emitter.emit("filedrop", event)
event.preventDefault();
return false;
}, false)
}
render() {
return (
<TransitionGroup className="max">

View File

@ -39,6 +39,8 @@ export default class Login extends Component {
if (this.state.password !== "") {
this.checkDBCreds();
}else{
this.checkDBPresence();
}
}
@ -59,8 +61,6 @@ export default class Login extends Component {
checkDBPresence() {
var url = this.state.url;
var icon = this.state.icon;
var jicon = jQuery(icon);
var btn = jQuery(this.refs.loginButton);
if (url === "") {
return;

View File

@ -12,16 +12,17 @@ import {
buildOuJson
} from "utils";
import { If, Then, Else } from "react-if";
const { dialog, app } = require("electron").remote;
var fs = require("fs");
var async = require("async");
var unzip = require("unzipper");
var fpath = require("path");
import { remote } from "electron";
const { dialog, app } = remote;
import { unlinkSync, createReadStream, createWriteStream } from "fs";
import { eachSeries } from "async";
import { Parse } from "unzipper";
import { join } from "path";
const Pick = require("stream-json/filters/Pick");
const { streamArray } = require("stream-json/streamers/StreamArray");
const { chain } = require("stream-chain");
const Asm = require("stream-json/Assembler");
import { withParser } from "stream-json/filters/Pick";
import { streamArray } from "stream-json/streamers/StreamArray";
import { chain } from "stream-chain";
import { connectTo } from "stream-json/Assembler";
export default class MenuContainer extends Component {
constructor() {
@ -35,6 +36,42 @@ export default class MenuContainer extends Component {
};
emitter.on("cancelUpload", this.cancelUpload.bind(this));
emitter.on("filedrop", this.fileDrop.bind(this))
}
fileDrop(e){
let fileNames = []
$.each(e.dataTransfer.files, function(_, file){
fileNames.push({ path: file.path, name: file.name });
})
this.unzipNecessary(fileNames).then(
function(results) {
eachSeries(
results,
function(file, callback) {
emitter.emit(
"showAlert",
"Processing file {}".format(file.name)
);
this.getFileMeta(file.path, callback);
}.bind(this),
function done() {
setTimeout(
function() {
this.setState({ uploading: false });
}.bind(this),
3000
);
$.each(results, function(_, file) {
if (file.delete) {
unlinkSync(file.path);
}
});
}.bind(this)
);
}.bind(this)
);
}
cancelUpload() {
@ -88,13 +125,13 @@ export default class MenuContainer extends Component {
var input = jQuery(this.refs.fileInput);
var fileNames = [];
$.each(input[0].files, function(index, file) {
$.each(input[0].files, function(_, file) {
fileNames.push({ path: file.path, name: file.name });
});
this.unzipNecessary(fileNames).then(
function(results) {
async.eachSeries(
eachSeries(
results,
function(file, callback) {
emitter.emit(
@ -112,7 +149,7 @@ export default class MenuContainer extends Component {
);
$.each(results, function(index, file) {
if (file.delete) {
fs.unlinkSync(file.path);
unlinkSync(file.path);
}
});
}.bind(this)
@ -132,12 +169,11 @@ export default class MenuContainer extends Component {
var name = files[index].name;
if (path.endsWith(".zip")) {
await fs
.createReadStream(path)
.pipe(unzip.Parse())
await createReadStream(path)
.pipe(Parse())
.on("entry", function(entry) {
var output = fpath.join(tempPath, entry.path);
entry.pipe(fs.createWriteStream(output));
var output = join(tempPath, entry.path);
entry.pipe(createWriteStream(output));
processed.push({
path: output,
name: entry.path,
@ -175,11 +211,11 @@ export default class MenuContainer extends Component {
console.log(file);
let pipeline = chain([
fs.createReadStream(file, { encoding: "utf8" }),
Pick.withParser({ filter: "meta" })
createReadStream(file, { encoding: "utf8" }),
withParser({ filter: "meta" })
]);
let asm = Asm.connectTo(pipeline);
let asm = connectTo(pipeline);
asm.on(
"done",
function(asm) {
@ -199,8 +235,8 @@ export default class MenuContainer extends Component {
processJson(file, callback, count, type) {
let pipeline = chain([
fs.createReadStream(file, { encoding: "utf8" }),
Pick.withParser({ filter: type }),
createReadStream(file, { encoding: "utf8" }),
withParser({ filter: type }),
streamArray()
]);

View File

@ -650,7 +650,7 @@ export default class SearchContainer extends Component {
}
query +=
" WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p";
" WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(m)) RETURN p";
emitter.emit("query", query, { aprop: start, bprop: end });
}
@ -809,7 +809,7 @@ export default class SearchContainer extends Component {
}
query +=
" WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(m)) RETURN p";
" WITH m,n MATCH p=allShortestPaths((n)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(m)) RETURN p";
emitter.emit("query", query, { aprop: start, bprop: end });
}

View File

@ -9,7 +9,7 @@
<li onclick="emitter.emit('setEnd', '{{type}}:{{label}}')">
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
</li>
<li onclick="emitter.emit('query', 'MATCH (n:User {name:{name}}),(m:User),p=shortestPath((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
<li onclick="emitter.emit('query', 'MATCH (n:User {name:{name}}),(m:User),p=shortestPath((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
</li>
{{/type_user}} {{#type_computer}}
@ -19,7 +19,7 @@
<li onclick="emitter.emit('setEnd', '{{type}}:{{label}}')">
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
</li>
<li onclick="emitter.emit('query', 'MATCH (n:Computer {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
<li onclick="emitter.emit('query', 'MATCH (n:Computer {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
</li>
{{/type_computer}} {{#type_group}}
@ -29,7 +29,7 @@
<li onclick="emitter.emit('setEnd', '{{type}}:{{label}}')">
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
</li>
<li onclick="emitter.emit('query', 'MATCH (n:Group {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
<li onclick="emitter.emit('query', 'MATCH (n:Group {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}}')">
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
</li>
{{/type_group}} {{#type_gpo}}
@ -39,7 +39,7 @@
<li onclick="emitter.emit('setEnd', '{{type}}:{{guid}}')">
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
</li>
<li onclick="emitter.emit('query', 'MATCH (n:GPO {name:{name}}),(m),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.guid={guid} RETURN p', {name: '{{name}}'})">
<li onclick="emitter.emit('query', 'MATCH (n:GPO {name:{name}}),(m),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.guid={guid} RETURN p', {name: '{{name}}'})">
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
</li>
{{/type_gpo}} {{#type_ou}}
@ -49,7 +49,7 @@
<li onclick="emitter.emit('setEnd', '{{type}}:{{guid}}')">
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
</li>
<li onclick="emitter.emit('query', 'MATCH (n:OU {guid:{guid}}),(m),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.guid={guid} RETURN p', {guid: '{{guid}}'})">
<li onclick="emitter.emit('query', 'MATCH (n:OU {guid:{guid}}),(m),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.guid={guid} RETURN p', {guid: '{{guid}}'})">
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
</li>
{{/type_ou}} {{#type_domain}}
@ -59,7 +59,7 @@
<li onclick="emitter.emit('setEnd', '{{type}}:{{label}}')">
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
</li>
<li onclick="emitter.emit('query', 'MATCH (n:Domain {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}} ')">
<li onclick="emitter.emit('query', 'MATCH (n:Domain {name:{name}}),(m:User),p=allShortestPaths((m)-[r:MemberOf|AdminTo|HasSession|Contains|GpLink|Owns|DCSync|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|CanRDP|ExecuteDCOM*1..]->(n)) WHERE NOT m.name={name} RETURN p', {name: '{{label}}'}, '{{label}} ')">
<i class="glyphicon glyphicon-screenshot"> </i> Shortest Paths to Here
</li>
{{/type_domain}} {{#expand}}

View File

@ -593,6 +593,7 @@ export function buildComputerJson(chunk) {
let localadmins = comp.LocalAdmins;
let rdpers = comp.RemoteDesktopUsers;
let primarygroup = comp.PrimaryGroup;
let dcom = comp.DcomUsers;
if (!queries.properties) {
if (primarygroup === null) {
@ -638,6 +639,18 @@ export function buildComputerJson(chunk) {
let p = { name: name, target: aName };
insert(queries, hash, statement, p);
});
$.each(dcom, function(_, dcomu) {
let aType = dcomu.Type;
let aName = dcomu.Name;
let rel = "ExecuteDCOM";
let hash = rel + aType;
let statement = baseQuery.format(aType, rel);
let p = { name: name, target: aName };
insert(queries, hash, statement, p);
});
});
return queries;
}