From febdbf17c45f2214fac297d93a6fddc2690bfa4c Mon Sep 17 00:00:00 2001 From: simondotsh Date: Sat, 30 Jul 2022 17:44:27 -0400 Subject: [PATCH] Added the SyncLAPSPassword edge with its documentation --- docs/data-analysis/edges.rst | 36 ++++++++++++ src/AppContainer.jsx | 3 +- src/components/Menu/MenuContainer.jsx | 10 ++++ src/components/Modals/AddEdgeModal.jsx | 4 +- src/components/Modals/HelpModal.jsx | 4 +- .../HelpTexts/SyncLAPSPassword/Abuse.jsx | 15 +++++ .../HelpTexts/SyncLAPSPassword/General.jsx | 13 +++++ .../HelpTexts/SyncLAPSPassword/Opsec.jsx | 6 ++ .../HelpTexts/SyncLAPSPassword/References.jsx | 7 +++ .../SyncLAPSPassword/SyncLAPSPassword.jsx | 57 +++++++++++++++++++ .../SearchContainer/EdgeFilter/EdgeFilter.jsx | 4 +- src/index.js | 5 +- 12 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 src/components/Modals/HelpTexts/SyncLAPSPassword/Abuse.jsx create mode 100644 src/components/Modals/HelpTexts/SyncLAPSPassword/General.jsx create mode 100644 src/components/Modals/HelpTexts/SyncLAPSPassword/Opsec.jsx create mode 100644 src/components/Modals/HelpTexts/SyncLAPSPassword/References.jsx create mode 100644 src/components/Modals/HelpTexts/SyncLAPSPassword/SyncLAPSPassword.jsx diff --git a/docs/data-analysis/edges.rst b/docs/data-analysis/edges.rst index 0016b7ce9..4829a91ff 100644 --- a/docs/data-analysis/edges.rst +++ b/docs/data-analysis/edges.rst @@ -1937,6 +1937,42 @@ http://www.harmj0y.net/blog/redteaming/the-trustpocalypse/ | +SyncLAPSPassword +^^^^^^^^^^^^^^^^^^^^ + +A principal with this signifies the capability of retrieving, through a directory +synchronization, the value of confidential and RODC filtered attributes, such as +LAPS' *ms-Mcs-AdmPwd*. + +Abuse Info +---------- + +To abuse these privileges, use DirSync: + +:: + + Sync-LAPS -LDAPFilter '(samaccountname=TargetComputer$)' + +For other optional parameters, view the DirSync documentation. + +Opsec Considerations +-------------------- + +Executing the attack will generate a 4662 (An operation was performed on an object) +event at the domain controller if an appropriate SACL is in place on the target object. + +References +---------- + +* https://github.com/simondotsh/DirSync +* https://simondotsh.com/infosec/2022/07/11/dirsync.html + +| + +---- + +| + AZAddMembers ^^^^^^^^^^^^ diff --git a/src/AppContainer.jsx b/src/AppContainer.jsx index 7bb9a9116..0c7b9e665 100644 --- a/src/AppContainer.jsx +++ b/src/AppContainer.jsx @@ -69,7 +69,8 @@ const fullEdgeList = [ 'AdminTo', 'AddSelf', 'WriteSPN', - 'AddKeyCredentialLink' + 'AddKeyCredentialLink', + 'SyncLAPSPassword' ]; export default class AppContainer extends Component { diff --git a/src/components/Menu/MenuContainer.jsx b/src/components/Menu/MenuContainer.jsx index fe4b39458..ac0819805 100644 --- a/src/components/Menu/MenuContainer.jsx +++ b/src/components/Menu/MenuContainer.jsx @@ -399,6 +399,16 @@ const MenuContainer = () => { console.log(err); }); + const createSyncLAPSPasswordStatement = "MATCH (n)-[:GetChangesInFilteredSet]->(m:Domain) WHERE (n)-[:GetChanges]->(m) AND NOT (n)-[:GetChangesAll]->(m) AND NOT n.objectid ENDS WITH '-S-1-5-9' MATCH (o:Computer {haslaps: true, domainsid: m.domainsid}) CREATE (n)-[:SyncLAPSPassword {isacl: true, isinherited: false}]->(o)" + await session.run(createSyncLAPSPasswordStatement, null).catch((err) => { + console.log(err); + }); + + const deleteGetChangesInFilteredSetStatement = "MATCH ()-[r:GetChangesInFilteredSet]->(:Domain) DELETE r" + await session.run(deleteGetChangesInFilteredSetStatement, null).catch((err) => { + console.log(err); + }); + await session.close(); console.log("Post processing done") } diff --git a/src/components/Modals/AddEdgeModal.jsx b/src/components/Modals/AddEdgeModal.jsx index 84bf93804..21cdfae9b 100644 --- a/src/components/Modals/AddEdgeModal.jsx +++ b/src/components/Modals/AddEdgeModal.jsx @@ -149,7 +149,8 @@ const AddEdgeModal = () => { edgeValue === 'ReadLAPSPassword' || edgeValue === 'WriteSPN' || edgeValue === 'AddKeyCredentialLink' || - edgeValue === 'AddSelf' + edgeValue === 'AddSelf' || + edgeValue === 'SyncLAPSPassword' ) { edgepart = `[r:${edgeValue} {isacl: true}]`; } else if (edgeValue === 'SQLAdmin') { @@ -311,6 +312,7 @@ const AddEdgeModal = () => { + {errors.edgeErrors.length > 0 && ( diff --git a/src/components/Modals/HelpModal.jsx b/src/components/Modals/HelpModal.jsx index d7f7a7afa..7cf4ba2b5 100644 --- a/src/components/Modals/HelpModal.jsx +++ b/src/components/Modals/HelpModal.jsx @@ -47,6 +47,7 @@ import Default from './HelpTexts/Default/Default'; import WriteSPN from "./HelpTexts/WriteSPN/WriteSPN"; import AddSelf from "./HelpTexts/AddSelf/AddSelf"; import AddKeyCredentialLink from "./HelpTexts/AddKeyCredentialLink/AddKeyCredentialLink"; +import SyncLAPSPassword from "./HelpTexts/SyncLAPSPassword/SyncLAPSPassword"; const HelpModal = () => { const [sourceName, setSourceName] = useState(''); @@ -124,7 +125,8 @@ const HelpModal = () => { AZVMContributor: AZVMContributor, WriteSPN: WriteSPN, AddSelf: AddSelf, - AddKeyCredentialLink: AddKeyCredentialLink + AddKeyCredentialLink: AddKeyCredentialLink, + SyncLAPSPassword: SyncLAPSPassword }; const Component = edge in components ? components[edge] : Default; diff --git a/src/components/Modals/HelpTexts/SyncLAPSPassword/Abuse.jsx b/src/components/Modals/HelpTexts/SyncLAPSPassword/Abuse.jsx new file mode 100644 index 000000000..e27be3df0 --- /dev/null +++ b/src/components/Modals/HelpTexts/SyncLAPSPassword/Abuse.jsx @@ -0,0 +1,15 @@ +const Abuse = (sourceName, sourceType, targetName, targetType) => { + let text = `To abuse this privilege with DirSync, first import DirSync into your agent session or into a PowerShell instance at the console. You must authenticate to the Domain Controller as ${ + sourceType === 'User' + ? `${sourceName} if you are not running a process as that user` + : `a member of ${sourceName} if you are not running a process as a member` + }. Then, execute the Sync-LAPS function: + + Sync-LAPS -LDAPFilter '(samaccountname=TargetComputer$) + + You can target a specific domain controller using the -Server parameter. + `; + return { __html: text }; +}; + +export default Abuse; diff --git a/src/components/Modals/HelpTexts/SyncLAPSPassword/General.jsx b/src/components/Modals/HelpTexts/SyncLAPSPassword/General.jsx new file mode 100644 index 000000000..ce7e3fbeb --- /dev/null +++ b/src/components/Modals/HelpTexts/SyncLAPSPassword/General.jsx @@ -0,0 +1,13 @@ +import { groupSpecialFormat} from '../Formatter'; + +const General = (sourceName, sourceType, targetName, targetType) => { + let text = `${groupSpecialFormat( + sourceType, + sourceName + )} the ability to synchronize the password set by Local Administrator Password Solution (LAPS) on the computer ${targetName}. + + The local administrator password for a computer managed by LAPS is stored in the confidential and Read-Only Domain Controller (RODC) filtered LDAP attribute ms-mcs-AdmPwd.`; + return { __html: text }; +}; + +export default General; diff --git a/src/components/Modals/HelpTexts/SyncLAPSPassword/Opsec.jsx b/src/components/Modals/HelpTexts/SyncLAPSPassword/Opsec.jsx new file mode 100644 index 000000000..b5d6f16bc --- /dev/null +++ b/src/components/Modals/HelpTexts/SyncLAPSPassword/Opsec.jsx @@ -0,0 +1,6 @@ +const Opsec = () => { + let text = `Executing the attack will generate a 4662 (An operation was performed on an object) event at the domain controller if an appropriate SACL is in place on the target object.`; + return { __html: text }; +}; + +export default Opsec; diff --git a/src/components/Modals/HelpTexts/SyncLAPSPassword/References.jsx b/src/components/Modals/HelpTexts/SyncLAPSPassword/References.jsx new file mode 100644 index 000000000..4f57e8656 --- /dev/null +++ b/src/components/Modals/HelpTexts/SyncLAPSPassword/References.jsx @@ -0,0 +1,7 @@ +const References = () => { + let text = `https://github.com/simondotsh/DirSync + https://simondotsh.com/infosec/2022/07/11/dirsync.html`; + return { __html: text }; +}; + +export default References; diff --git a/src/components/Modals/HelpTexts/SyncLAPSPassword/SyncLAPSPassword.jsx b/src/components/Modals/HelpTexts/SyncLAPSPassword/SyncLAPSPassword.jsx new file mode 100644 index 000000000..07e3e9744 --- /dev/null +++ b/src/components/Modals/HelpTexts/SyncLAPSPassword/SyncLAPSPassword.jsx @@ -0,0 +1,57 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Tabs, Tab } from 'react-bootstrap'; +import General from './General'; +import Abuse from './Abuse'; +import Opsec from './Opsec'; +import References from './References'; + +const SyncLAPSPassword = ({ + sourceName, + sourceType, + targetName, + targetType, +}) => { + return ( + + + + + + + ); +}; + +SyncLAPSPassword.propTypes = { + sourceName: PropTypes.string, + sourceType: PropTypes.string, + targetName: PropTypes.string, + targetType: PropTypes.string, +}; +export default SyncLAPSPassword; diff --git a/src/components/SearchContainer/EdgeFilter/EdgeFilter.jsx b/src/components/SearchContainer/EdgeFilter/EdgeFilter.jsx index f77ba37ab..c6fa5af36 100644 --- a/src/components/SearchContainer/EdgeFilter/EdgeFilter.jsx +++ b/src/components/SearchContainer/EdgeFilter/EdgeFilter.jsx @@ -58,7 +58,8 @@ const EdgeFilter = ({ open }) => { 'ReadGMSAPassword', 'AddKeyCredentialLink', 'WriteSPN', - 'AddSelf' + 'AddSelf', + 'SyncLAPSPassword' ]} sectionName='ACL' /> @@ -75,6 +76,7 @@ const EdgeFilter = ({ open }) => { +