From 25a3cc525160ff93d183dfa1de96ebcfa1e601ae Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:12:53 +0200
Subject: [PATCH 01/15] Update AppContainer.jsx
---
src/AppContainer.jsx | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/AppContainer.jsx b/src/AppContainer.jsx
index 4b64651d3..f53e0f72a 100644
--- a/src/AppContainer.jsx
+++ b/src/AppContainer.jsx
@@ -83,7 +83,12 @@ const fullEdgeList = [
'AddKeyCredentialLink',
'DumpSMSAPassword',
'DCSync',
- 'SyncLAPSPassword'
+ 'SyncLAPSPassword',
+ 'Enroll',
+ 'AutoEnroll',
+ 'ManageCa',
+ 'ManageCertificates',
+ 'EnabledBy'
];
export default class AppContainer extends Component {
From 11e734323c2763a5963f77cdbf21d17cb86325e0 Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:17:18 +0200
Subject: [PATCH 02/15] Adjust query field
---
src/components/RawQuery.jsx | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/components/RawQuery.jsx b/src/components/RawQuery.jsx
index 8f9240e49..77a20b80b 100644
--- a/src/components/RawQuery.jsx
+++ b/src/components/RawQuery.jsx
@@ -24,12 +24,14 @@ const RawQuery = () => {
const onKeyDown = (e) => {
let key = e.keyCode ? e.keyCode : e.which;
- if (key === 13) {
+ if ((key == 10 || key == 13) && e.ctrlKey) {
emitter.emit('query', query);
}
};
const onChange = (e) => {
+ e.target.style.height = 'inherit';
+ e.target.style.height = `${e.target.scrollHeight}px`;
setQueryFromEvent(e.target.value);
};
@@ -59,7 +61,7 @@ const RawQuery = () => {
transition={{ duration: 0.25 }}
animate={open ? 'open' : 'closed'}
>
- {
className={clsx(styles.input, 'form-control')}
autoComplete='off'
placeholder='Enter a cypher query. Your query must return nodes or paths.'
- />
+ /> */}
+
);
From 79bad46143ea19890a41a52842efef6d4eb7f22f Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:19:12 +0200
Subject: [PATCH 03/15] Add edge for PKI
---
.../SearchContainer/EdgeFilter/EdgeFilter.jsx | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/src/components/SearchContainer/EdgeFilter/EdgeFilter.jsx b/src/components/SearchContainer/EdgeFilter/EdgeFilter.jsx
index 68f702626..d1edb653b 100644
--- a/src/components/SearchContainer/EdgeFilter/EdgeFilter.jsx
+++ b/src/components/SearchContainer/EdgeFilter/EdgeFilter.jsx
@@ -112,6 +112,21 @@ const EdgeFilter = ({ open }) => {
+
+
+
+
+
Date: Tue, 4 Jul 2023 22:21:57 +0200
Subject: [PATCH 04/15] Support for icon PKI
---
src/components/SearchContainer/SearchRow.jsx | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/components/SearchContainer/SearchRow.jsx b/src/components/SearchContainer/SearchRow.jsx
index 74c9fe3aa..e2bda714c 100644
--- a/src/components/SearchContainer/SearchRow.jsx
+++ b/src/components/SearchContainer/SearchRow.jsx
@@ -43,6 +43,12 @@ const SearchRow = ({ item, search }) => {
case 'OU':
icon.className = 'fa fa-sitemap';
break;
+ case 'CA':
+ icon.className = 'fa fa-university';
+ break;
+ case 'CertificateTemplate':
+ icon.className = 'fa fa-id-card';
+ break;
case 'Container':
icon.className = 'fa fa-box'
break
@@ -104,8 +110,12 @@ const SearchRow = ({ item, search }) => {
icon.className = 'fa fa-window-restore'
break
default:
- icon.className = 'fa fa-question';
- type = 'Base';
+ if (item.hasOwnProperty("bl-icon")){
+ icon.className = 'fa '+ item["bl-icon"];
+ }else{
+ icon.className = 'fa fa-question';
+ type = 'Base';
+ }
break;
}
From 1210224fb696bce3e79f73b408bd5b92ce4b9b7d Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:26:00 +0200
Subject: [PATCH 05/15] Add support for PKI in tab view
---
.../SearchContainer/TabContainer.jsx | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/src/components/SearchContainer/TabContainer.jsx b/src/components/SearchContainer/TabContainer.jsx
index 3fa5b51d3..5d97b5be2 100644
--- a/src/components/SearchContainer/TabContainer.jsx
+++ b/src/components/SearchContainer/TabContainer.jsx
@@ -8,6 +8,8 @@ import ComputerNodeData from './Tabs/ComputerNodeData';
import DomainNodeData from './Tabs/DomainNodeData';
import GpoNodeData from './Tabs/GPONodeData';
import OuNodeData from './Tabs/OUNodeData';
+import CaNodeData from './Tabs/CANodeData';
+import TemplateNodeData from './Tabs/TemplateNodeData';
import AZGroupNodeData from './Tabs/AZGroupNodeData';
import AZUserNodeData from './Tabs/AZUserNodeData';
import AZContainerRegistryNodeData from './Tabs/AZContainerRegistryNodeData';
@@ -48,6 +50,8 @@ class TabContainer extends Component {
domainVisible: false,
gpoVisible: false,
ouVisible: false,
+ caVisible: false,
+ templateVisible: false,
containerVisible: false,
azGroupVisible: false,
azUserVisible: false,
@@ -92,6 +96,10 @@ class TabContainer extends Component {
this._domainNodeClicked();
} else if (type === 'OU') {
this._ouNodeClicked();
+ } else if (type === 'CA') {
+ this._caNodeClicked();
+ } else if (type === 'CertificateTemplate') {
+ this._templateNodeClicked();
} else if (type === 'GPO') {
this._gpoNodeClicked();
} else if (type === 'AZGroup') {
@@ -225,6 +233,22 @@ class TabContainer extends Component {
});
}
+ _caNodeClicked() {
+ this.clearVisible()
+ this.setState({
+ caVisible: true,
+ selected: 2
+ });
+ }
+
+ _templateNodeClicked() {
+ this.clearVisible()
+ this.setState({
+ templateVisible: true,
+ selected: 2
+ });
+ }
+
_azGroupNodeClicked() {
this.clearVisible()
this.setState({
@@ -405,6 +429,8 @@ class TabContainer extends Component {
!this.state.domainVisible &&
!this.state.gpoVisible &&
!this.state.ouVisible &&
+ !this.state.caVisible &&
+ !this.state.templateVisible &&
!this.state.azGroupVisible &&
!this.state.azUserVisible &&
!this.state.azContainerRegistryVisible &&
@@ -436,6 +462,8 @@ class TabContainer extends Component {
+
+
From f805aeda2bff85069aee75d20d4dcfa1c71682cb Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:27:21 +0200
Subject: [PATCH 06/15] Add support for custom group icon
---
src/components/SearchContainer/SearchRow.jsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/components/SearchContainer/SearchRow.jsx b/src/components/SearchContainer/SearchRow.jsx
index e2bda714c..83756d5e7 100644
--- a/src/components/SearchContainer/SearchRow.jsx
+++ b/src/components/SearchContainer/SearchRow.jsx
@@ -26,7 +26,11 @@ const SearchRow = ({ item, search }) => {
switch (type) {
case 'Group':
- icon.className = 'fa fa-users';
+ if (item.hasOwnProperty("bl-icon")){
+ icon.className = 'fa '+ item["bl-icon"];
+ }else{
+ icon.className = 'fa fa-users';
+ }
break;
case 'User':
icon.className = 'fa fa-user';
From 23ecdeaf0c87755c141bc5ef079c6609c91cb755 Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:29:36 +0200
Subject: [PATCH 07/15] Create CANodeData.jsx
---
.../SearchContainer/Tabs/CANodeData.jsx | 171 ++++++++++++++++++
1 file changed, 171 insertions(+)
create mode 100644 src/components/SearchContainer/Tabs/CANodeData.jsx
diff --git a/src/components/SearchContainer/Tabs/CANodeData.jsx b/src/components/SearchContainer/Tabs/CANodeData.jsx
new file mode 100644
index 000000000..8e4c6ef7b
--- /dev/null
+++ b/src/components/SearchContainer/Tabs/CANodeData.jsx
@@ -0,0 +1,171 @@
+/******************************************************************************************
+* The credit goes to https://github.com/ly4k/BloodHound. Thank you for the excellent work!
+*/
+import clsx from 'clsx';
+import React, { useContext, useEffect, useState } from 'react';
+import { Table } from 'react-bootstrap';
+import { AppContext } from '../../../AppContext';
+import CollapsibleSection from './Components/CollapsibleSection';
+import ExtraNodeProps from './Components/ExtraNodeProps';
+import MappedNodeProps from './Components/MappedNodeProps';
+import NodeCypherLink from './Components/NodeCypherLink';
+import NodePlayCypherLink from './Components/NodePlayCypherLink';
+import NodeCypherNoNumberLink from './Components/NodeCypherNoNumberLink';
+import styles from './NodeData.module.css';
+
+const CANodeData = () => {
+ const [visible, setVisible] = useState(false);
+ const [objectid, setObjectid] = useState(null);
+ const [label, setLabel] = useState(null);
+ const [domain, setDomain] = useState(null);
+ const [nodeProps, setNodeProps] = useState({});
+ const [blocksInheritance, setBlocksInheritance] = useState(false);
+ const context = useContext(AppContext);
+
+ useEffect(() => {
+ emitter.on('nodeClicked', nodeClickEvent);
+
+ return () => {
+ emitter.removeListener('nodeClicked', nodeClickEvent);
+ };
+ }, []);
+
+ const nodeClickEvent = (type, id, blocksinheritance, domain) => {
+ if (type === 'CA') {
+ setVisible(true);
+ setObjectid(id);
+ setDomain(domain);
+ setBlocksInheritance(blocksinheritance);
+ let session = driver.session();
+ session
+ .run(`MATCH (n:CA {objectid: $objectid}) RETURN n AS node`, {
+ objectid: id,
+ })
+ .then((r) => {
+ let props = r.records[0].get('node').properties;
+ setNodeProps(props);
+ setLabel(props.name);
+ session.close();
+ });
+ } else {
+ setObjectid(null);
+ setVisible(false);
+ }
+ };
+
+ const displayMap = {
+ objectid: 'Object ID',
+ 'CA Name': 'CA Name',
+ };
+
+ return objectid === null ? (
+
+ ) : (
+
+
+
{label || objectid}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ (u1:CA {objectid: $objectid}) WHERE r.isacl=true'
+ }
+ end={label}
+ distinct
+ />
+ (g:Group)-[r1:ManageCa|ManageCertificates|Auditor|Operator|Owns]->(u:CA {objectid: $objectid}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.objectid = u.objectid) AND NOT n.objectid = u.objectid'
+ }
+ end={label}
+ distinct
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ (u1:CA {objectid: $objectid}) WHERE r.isacl=true'
+ }
+ end={label}
+ distinct
+ />
+ (g:Group)-[r1:Read|Enroll]->(u:CA {objectid: $objectid}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.objectid = u.objectid) AND NOT n.objectid = u.objectid'
+ }
+ end={label}
+ distinct
+ />
+
+
+
+
+
+
+
+
+ );
+};
+
+CANodeData.propTypes = {};
+export default CANodeData;
From 7f876d1c911b040eee3e6712259ec91544d10da8 Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:31:48 +0200
Subject: [PATCH 08/15] Edit tab of item selection to add count of "Certificate
Templates" & "Certificate Authorities"
---
.../SearchContainer/Tabs/DatabaseDataDisplay.jsx | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx b/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx
index a411c2829..17e3176bb 100644
--- a/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx
+++ b/src/components/SearchContainer/Tabs/DatabaseDataDisplay.jsx
@@ -113,6 +113,20 @@ const DatabaseDataDisplay = () => {
index={index}
label={'Computers'}
/>
+
+
Date: Tue, 4 Jul 2023 22:33:18 +0200
Subject: [PATCH 09/15] Add prebuild queries for PKI
---
.../SearchContainer/Tabs/PrebuiltQueries.json | 170 ++++++++++++++++++
1 file changed, 170 insertions(+)
diff --git a/src/components/SearchContainer/Tabs/PrebuiltQueries.json b/src/components/SearchContainer/Tabs/PrebuiltQueries.json
index 42f628bf8..af222e101 100644
--- a/src/components/SearchContainer/Tabs/PrebuiltQueries.json
+++ b/src/components/SearchContainer/Tabs/PrebuiltQueries.json
@@ -384,6 +384,176 @@
"endNode": "{}"
}
]
+ },
+ {
+ "name": "Find all Certificate Templates",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH (n:CertificateTemplate) RETURN n"
+ }
+ ]
+ },
+ {
+ "name": "Find enabled Certificate Templates",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH (n:CertificateTemplate) WHERE n.Enabled = true RETURN n"
+ }
+ ]
+ },
+ {
+ "name": "Find Certificate Authorities",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH (n:CA) RETURN n"
+ }
+ ]
+ },
+ {
+ "name": "Find Misconfigured Certificate Templates (ESC1)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH (n:CertificateTemplate) WHERE n.`Enrollee Supplies Subject` = true and n.`Client Authentication` = true and n.`Enabled` = true RETURN n"
+ }
+ ]
+ },
+ {
+ "name": "Shortest Paths to Misconfigured Certificate Templates from Owned Principals (ESC1)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH p=allShortestPaths((g {owned:true})-[r*1..]->(n:CertificateTemplate)) WHERE g<>n and n.`Enrollee Supplies Subject` = true and n.`Client Authentication` = true and n.`Enabled` = true and NONE(rel in r WHERE type(rel) in ['EnabledBy','Read','ManageCa','ManageCertificates']) return p"
+ }
+ ]
+ },
+ {
+ "name": "Find Misconfigured Certificate Templates (ESC2)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH (n:CertificateTemplate) WHERE n.`Enabled` = true and n.`Any Purpose` = true RETURN n"
+ }
+ ]
+ },
+ {
+ "name": "Shortest Paths to Misconfigured Certificate Templates from Owned Principals (ESC2)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH p=allShortestPaths((g {owned:true})-[r*1..]->(n:CertificateTemplate)) WHERE g<>n and n.`Enabled` = true and n.`Any Purpose` = true and NONE(rel in r WHERE type(rel) in ['EnabledBy','Read','ManageCa','ManageCertificates']) return p"
+ }
+ ]
+ },
+ {
+ "name": "Find Enrollment Agent Templates (ESC3)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH (n:CertificateTemplate) WHERE n.`Enabled` = true and n.`Enrollment Agent` = true RETURN n"
+ }
+ ]
+ },
+ {
+ "name": "Shortest Paths to Enrollment Agent Templates from Owned Principals (ESC3)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH p=allShortestPaths((g {owned:true})-[r*1..]->(n:CertificateTemplate)) WHERE g<>n and n.`Enabled` = true and n.`Enrollment Agent` = true and NONE(rel in r WHERE type(rel) in ['EnabledBy','Read','ManageCa','ManageCertificates']) return p"
+ }
+ ]
+ },
+ {
+ "name": "Shortest Paths to Vulnerable Certificate Template Access Control (ESC4)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH p=shortestPath((g)-[r*1..]->(n:CertificateTemplate)) WHERE g<>n and n.`Enabled` = true and NONE(rel in r WHERE type(rel) in ['EnabledBy','Read','ManageCa','ManageCertificates','Enroll','AutoEnroll']) RETURN p"
+ }
+ ]
+ },
+ {
+ "name": "Shortest Paths to Vulnerable Certificate Template Access Control from Owned Principals (ESC4)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH p=allShortestPaths((g {owned:true})-[r*1..]->(n:CertificateTemplate)) WHERE g<>n and n.Enabled = true and NONE(rel in r WHERE type(rel) in ['EnabledBy','Read','ManageCa','ManageCertificates','Enroll','AutoEnroll']) return p"
+ }
+ ]
+ },
+ {
+ "name": "Find Certificate Authorities with User Specified SAN (ESC6)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH (n:CA) WHERE n.`User Specified SAN` = 'Enabled' RETURN n"
+ }
+ ]
+ },
+ {
+ "name": "Shortest Paths to Vulnerable Certificate Authority Access Control (ESC7)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH p=allShortestPaths((g)-[r*1..]->(n:CA)) WHERE g<>n and NONE(rel in r WHERE type(rel) in ['EnabledBy','Read','Enroll','AutoEnroll']) RETURN p"
+ }
+ ]
+ },
+ {
+ "name": "Shortest Paths to Vulnerable Certificate Authority Access Control from Owned Principals (ESC7)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH p=allShortestPaths((g {owned:true})-[r*1..]->(n:CA)) WHERE g<>n and NONE(rel in r WHERE type(rel) in ['EnabledBy','Read','Enroll','AutoEnroll']) RETURN p"
+ }
+ ]
+ },
+ {
+ "name": "Find Certificate Authorities with HTTP Web Enrollment (ESC8)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH (n:CA) WHERE n.`Web Enrollment` = 'Enabled' RETURN n"
+ }
+ ]
+ },
+ {
+ "name": "Find Unsecured Certificate Templates (ESC9)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH (n:CertificateTemplate) WHERE 'NoSecurityExtension' in n.`Enrollment Flag` and n.`Enabled` = true RETURN n"
+ }
+ ]
+ },
+ {
+ "name": "Shortest Paths to Unsecured Certificate Templates from Owned Principals (ESC9)",
+ "category": "PKI",
+ "queryList": [
+ {
+ "final": true,
+ "query": "MATCH p=allShortestPaths((g {owned:true})-[r*1..]->(n:CertificateTemplate)) WHERE g<>n and 'NoSecurityExtension' in n.`Enrollment Flag` and n.`Enabled` = true and NONE(rel in r WHERE type(rel) in ['EnabledBy','Read','ManageCa','ManageCertificates']) return p"
+ }
+ ]
}
]
}
From cbc46f227080e13c5858d7630aa2b07f42defc37 Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:38:40 +0200
Subject: [PATCH 10/15] Support for copy/past queries
---
.../Tabs/PrebuiltQueryNode.jsx | 106 +++++++++++++++++-
1 file changed, 101 insertions(+), 5 deletions(-)
diff --git a/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx b/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx
index 7aa7853f3..942d89495 100644
--- a/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx
+++ b/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx
@@ -1,7 +1,37 @@
-import React, { Component } from 'react';
+import React, { Component, useContext } from 'react';
import './PrebuiltQueries.module.css';
+import { motion } from 'framer-motion';
+import { AppContext } from '../../../AppContext';
+
+import GlyphiconSpan from '../../GlyphiconSpan';
+import Icon from '../../Icon';
+import { withAlert } from 'react-alert';
+
export default class PrebuiltQueryNode extends Component {
+ static contextType = AppContext;
+
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ hovered: false
+ }
+ }
+
+ enterQuery() {
+ this.setState({
+ hovered: true
+ })
+ };
+
+
+ exitQuery() {
+ this.setState({
+ hovered: false
+ })
+ };
+
render() {
let c;
@@ -14,11 +44,77 @@ export default class PrebuiltQueryNode extends Component {
}
}.bind(this);
+ const copyQuery = (e) => {
+ let containsProps = false;
+ let queries = []
+ let queryList = this.props.info.queryList
+
+ let containsMultipleQueries = queryList.length > 1;
+
+ for (var i = 0; i < queryList.length; i++) {
+ let query = queryList[i].query;
+
+ if (queries.length > 0) {
+ queries.push("");
+ }
+
+ let props = queryList[i].props;
+ if (props) {
+ containsProps = true;
+ for (const [key, value] of Object.entries(props)) {
+ query = query.replace(`\$${key}`, JSON.stringify(value))
+ }
+ }
+ queries.push(query);
+ }
+
+ navigator.clipboard.writeText(queries.join("\n"))
+
+ this.props.alert.info("Copied query")
+
+ if (containsProps) {
+ this.props.alert.show(WARNING Query contains props. These might not be properly formatted, { type: 'error' })
+ }
+
+ if (containsMultipleQueries) {
+ this.props.alert.show(WARNING Query contains multiple queries, { type: 'error' })
+ }
+ };
+
+ const variants = {
+ open: { height: 'auto', opacity: 1 },
+ closed: { height: 0, opacity: 0 },
+ };
+
return (
-
- {this.props.info.name} |
- |
-
+
+ {this.props.info.name} |
+
+
+ copyQuery()}
+ >
+
+
+
+
+
+ |
+
);
}
}
+export default withAlert()(PrebuiltQueryNode)
From 892740e7837cf10bfcfacd79095395424651464c Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:39:40 +0200
Subject: [PATCH 11/15] Create TemplateNodeData.jsx
---
.../SearchContainer/Tabs/TemplateNodeData.jsx | 171 ++++++++++++++++++
1 file changed, 171 insertions(+)
create mode 100644 src/components/SearchContainer/Tabs/TemplateNodeData.jsx
diff --git a/src/components/SearchContainer/Tabs/TemplateNodeData.jsx b/src/components/SearchContainer/Tabs/TemplateNodeData.jsx
new file mode 100644
index 000000000..6a854200b
--- /dev/null
+++ b/src/components/SearchContainer/Tabs/TemplateNodeData.jsx
@@ -0,0 +1,171 @@
+/******************************************************************************************
+* The credit goes to https://github.com/ly4k/BloodHound. Thank you for the excellent work!
+*/
+import clsx from 'clsx';
+import React, { useContext, useEffect, useState } from 'react';
+import { Table } from 'react-bootstrap';
+import { AppContext } from '../../../AppContext';
+import CollapsibleSection from './Components/CollapsibleSection';
+import ExtraNodeProps from './Components/ExtraNodeProps';
+import MappedNodeProps from './Components/MappedNodeProps';
+import NodeCypherLink from './Components/NodeCypherLink';
+import NodeCypherNoNumberLink from './Components/NodeCypherNoNumberLink';
+import styles from './NodeData.module.css';
+
+const TemplateNodeData = () => {
+ const [visible, setVisible] = useState(false);
+ const [objectid, setObjectid] = useState(null);
+ const [label, setLabel] = useState(null);
+ const [domain, setDomain] = useState(null);
+ const [nodeProps, setNodeProps] = useState({});
+ const [blocksInheritance, setBlocksInheritance] = useState(false);
+ const context = useContext(AppContext);
+
+ useEffect(() => {
+ emitter.on('nodeClicked', nodeClickEvent);
+
+ return () => {
+ emitter.removeListener('nodeClicked', nodeClickEvent);
+ };
+ }, []);
+
+ const nodeClickEvent = (type, id, blocksinheritance, domain) => {
+ if (type === 'CertificateTemplate') {
+ setVisible(true);
+ setObjectid(id);
+ setDomain(domain);
+ setBlocksInheritance(blocksinheritance);
+ let session = driver.session();
+ session
+ .run(`MATCH (n:CertificateTemplate {objectid: $objectid}) RETURN n AS node`, {
+ objectid: id,
+ })
+ .then((r) => {
+ let props = r.records[0].get('node').properties;
+ setNodeProps(props);
+ setLabel(props.name);
+ session.close();
+ });
+ } else {
+ setObjectid(null);
+ setVisible(false);
+ }
+ };
+
+ const displayMap = {
+ objectid: 'Object ID',
+ 'Template Name': 'Template Name',
+ 'Display Name': 'Display Name',
+ };
+
+ return objectid === null ? (
+
+ ) : (
+
+
+
{label || objectid}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ (u1:CertificateTemplate {objectid: $objectid}) WHERE r.isacl=true'
+ }
+ end={label}
+ distinct
+ />
+ (g:Group)-[r1:AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner|WriteProperty|Owns]->(u:CertificateTemplate {objectid: $objectid}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.objectid = u.objectid) AND NOT n.objectid = u.objectid'
+ }
+ end={label}
+ distinct
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ (u1:CertificateTemplate {objectid: $objectid}) WHERE r.isacl=true'
+ }
+ end={label}
+ distinct
+ />
+ (g:Group)-[r1:AutoEnroll|Enroll]->(u:CertificateTemplate {objectid: $objectid}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.objectid = u.objectid) AND NOT n.objectid = u.objectid'
+ }
+ end={label}
+ distinct
+ />
+
+
+
+
+
+
+
+
+ );
+};
+
+TemplateNodeData.propTypes = {};
+export default TemplateNodeData;
From da2f27c7b0222b1a510eb842d859923e48394add Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:40:39 +0200
Subject: [PATCH 12/15] Add support color scheme for PKI
---
src/index.js | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/index.js b/src/index.js
index 03e5815a4..a5df54f48 100644
--- a/src/index.js
+++ b/src/index.js
@@ -142,6 +142,18 @@ global.appStore = {
scale: 1.25,
color: '#FFAA00',
},
+ CA: {
+ font: "'Font Awesome 5 Free'",
+ content: '\uF19C',
+ scale: 1.25,
+ color: '#FFAA00',
+ },
+ CertificateTemplate: {
+ font: "'Font Awesome 5 Free'",
+ content: '\uF2C2',
+ scale: 1.25,
+ color: '#73E6A1',
+ },
Container: {
font: "'Font Awesome 5 Free'",
content: '\uF466',
From df069f60c9acbea68f73abd4c3d09520f5e4d7e3 Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:43:41 +0200
Subject: [PATCH 13/15] Add support for ingestion of new PKI repr by Certipy
---
src/js/newingestion.js | 62 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/src/js/newingestion.js b/src/js/newingestion.js
index 9e91bfddb..4002bbde4 100644
--- a/src/js/newingestion.js
+++ b/src/js/newingestion.js
@@ -20,6 +20,8 @@ export const ADLabels = {
Computer: 'Computer',
OU: 'OU',
GPO: 'GPO',
+ CertificateTemplate: 'CertificateTemplate',
+ CA: 'CA',
Domain: 'Domain',
Container: 'Container',
MemberOf: 'MemberOf',
@@ -34,6 +36,7 @@ export const ADLabels = {
Contains: 'Contains',
GPLink: 'GPLink',
TrustedBy: 'TrustedBy',
+ EnabledBy: 'EnabledBy',
DumpSMSAPassword: 'DumpSMSAPassword',
};
@@ -224,6 +227,65 @@ export function buildGroupJsonNew(chunk) {
return queries;
}
+/**
+ *
+ * @param {Array.} chunk
+ * @returns {{}}
+ */
+export function buildTemplateJsonNew(chunk) {
+ let queries = {};
+
+ queries.properties = {};
+ queries.properties.statement = PROP_QUERY.format(ADLabels.CertificateTemplate);
+ queries.properties.props = [];
+
+ for (let template of chunk) {
+ let properties = template.Properties;
+ let identifier = template.ObjectIdentifier;
+ let aces = template.Aces;
+ let cas = template.cas_ids;
+
+ queries.properties.props.push({ objectid: identifier, map: properties });
+
+ processAceArrayNew(aces, identifier, ADLabels.CertificateTemplate, queries);
+
+ if (cas) {
+ let format = [ADLabels.CertificateTemplate, ADLabels.CA, ADLabels.EnabledBy, NON_ACL_PROPS];
+ let props = cas.map((ca) => {
+ return { source: identifier, target: ca };
+ });
+ insertNew(queries, format, props);
+ }
+ }
+
+ return queries;
+}
+
+/**
+ *
+ * @param {Array.} chunk
+ * @returns {{}}
+ */
+export function buildCaJsonNew(chunk) {
+ let queries = {};
+
+ queries.properties = {};
+ queries.properties.statement = PROP_QUERY.format(ADLabels.CA);
+ queries.properties.props = [];
+
+ for (let ca of chunk) {
+ let properties = ca.Properties;
+ let identifier = ca.ObjectIdentifier;
+ let aces = ca.Aces;
+
+ queries.properties.props.push({ objectid: identifier, map: properties });
+
+ processAceArrayNew(aces, identifier, ADLabels.CA, queries);
+ }
+
+ return queries;
+}
+
/**
*
* @param {Array.} chunk
From ad5cec600565fa64d1830830aa0649fcea0bead9 Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:47:47 +0200
Subject: [PATCH 14/15] Added ESC9
---
src/components/SearchContainer/Tabs/TemplateNodeData.jsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/SearchContainer/Tabs/TemplateNodeData.jsx b/src/components/SearchContainer/Tabs/TemplateNodeData.jsx
index 6a854200b..572dbe226 100644
--- a/src/components/SearchContainer/Tabs/TemplateNodeData.jsx
+++ b/src/components/SearchContainer/Tabs/TemplateNodeData.jsx
@@ -142,7 +142,7 @@ const TemplateNodeData = () => {
property='First Degree Enrollment Rights'
target={objectid}
baseQuery={
- 'MATCH p=(n)-[r:AutoEnroll|Enroll]->(u1:CertificateTemplate {objectid: $objectid}) WHERE r.isacl=true'
+ 'MATCH p=(n)-[r:AutoEnroll|Enroll|GenericAll]->(u1:CertificateTemplate {objectid: $objectid}) WHERE r.isacl=true'
}
end={label}
distinct
@@ -151,7 +151,7 @@ const TemplateNodeData = () => {
property='Group Delegated Enrollment Rights'
target={objectid}
baseQuery={
- 'MATCH p=(n)-[r:MemberOf*1..]->(g:Group)-[r1:AutoEnroll|Enroll]->(u:CertificateTemplate {objectid: $objectid}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.objectid = u.objectid) AND NOT n.objectid = u.objectid'
+ 'MATCH p=(n)-[r:MemberOf*1..]->(g:Group)-[r1:AutoEnroll|Enroll|GenericAll]->(u:CertificateTemplate {objectid: $objectid}) WITH LENGTH(p) as pathLength, p, n WHERE NONE (x in NODES(p)[1..(pathLength-1)] WHERE x.objectid = u.objectid) AND NOT n.objectid = u.objectid'
}
end={label}
distinct
From f9ace41bb31ee910f7a183ec3bbd6fe88211020f Mon Sep 17 00:00:00 2001
From: 1mm0rt41PC <5358076+1mm0rt41PC@users.noreply.github.com>
Date: Tue, 4 Jul 2023 23:25:07 +0200
Subject: [PATCH 15/15] temp fix for building
---
src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx b/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx
index 942d89495..68c24580e 100644
--- a/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx
+++ b/src/components/SearchContainer/Tabs/PrebuiltQueryNode.jsx
@@ -117,4 +117,4 @@ export default class PrebuiltQueryNode extends Component {
);
}
}
-export default withAlert()(PrebuiltQueryNode)
+//export default withAlert()(PrebuiltQueryNode)