Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AddSelf edge functionality to UI #370

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/components/Modals/AddEdgeModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ const AddEdgeModal = () => {
edgeValue === 'GenericWrite' ||
edgeValue === 'AllExtendedRights' ||
edgeValue === 'AddMember' ||
edgeValue === 'AddSelf' ||
edgeValue === 'ForceChangePassword' ||
edgeValue === 'Owns' ||
edgeValue === 'WriteDacl' ||
Expand Down Expand Up @@ -241,6 +242,7 @@ const AddEdgeModal = () => {
AllExtendedRights
</option>
<option value='AddMember'>AddMember</option>
<option value='AddSelf'>AddSelf</option>
<option value='ForceChangePassword'>
ForceChangePassword
</option>
Expand Down
2 changes: 2 additions & 0 deletions src/components/Modals/HelpModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import AllExtendedRights from './HelpTexts/AllExtendedRights/AllExtendedRights';
import AdminTo from './HelpTexts/AdminTo/AdminTo';
import HasSession from './HelpTexts/HasSession/HasSession';
import AddMember from './HelpTexts/AddMember/AddMember';
import AddSelf from './HelpTexts/AddSelf/AddSelf';
import ForceChangePassword from './HelpTexts/ForceChangePassword/ForceChangePassword';
import GenericWrite from './HelpTexts/GenericWrite/GenericWrite';
import Owns from './HelpTexts/Owns/Owns';
Expand Down Expand Up @@ -69,6 +70,7 @@ const HelpModal = () => {
AdminTo: AdminTo,
HasSession: HasSession,
AddMember: AddMember,
AddSelf: AddSelf,
ForceChangePassword: ForceChangePassword,
GenericWrite: GenericWrite,
Owns: Owns,
Expand Down
25 changes: 25 additions & 0 deletions src/components/Modals/HelpTexts/AddSelf/Abuse.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const Abuse = (sourceName, sourceType, targetName, targetType) => {
let text = `There are at least two ways to execute this attack. The first and most obvious is by using the built-in net.exe binary in Windows (e.g.: net group "Domain Admins" dfm.a /add /domain). See the opsec considerations tab for why this may be a bad idea. The second, and highly recommended method, is by using the Add-DomainGroupMember function in PowerView. This function is superior to using the net.exe binary in several ways. For instance, you can supply alternate credentials, instead of needing to run a process as or logon as the user with the AddMember privilege. Additionally, you have much safer execution options than you do with spawning net.exe (see the opsec tab).

To abuse this privilege with PowerView's Add-DomainGroupMember, first import PowerView into your agent session or into a PowerShell instance at the console.

You may need to 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`
}. To do this in conjunction with Add-DomainGroupMember, first create a PSCredential object (these examples comes from the PowerView help documentation):

<code>$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\\dfm.a', $SecPassword)</code>

Then, use Add-DomainGroupMember, optionally specifying $Cred if you are not already running a process as ${sourceName}:

<code>Add-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y' -Credential $Cred</code>

Finally, verify that the user was successfully added to the group with PowerView's Get-DomainGroupMember:

<code>Get-DomainGroupMember -Identity 'Domain Admins'</code>`;
return { __html: text };
};

export default Abuse;
52 changes: 52 additions & 0 deletions src/components/Modals/HelpTexts/AddSelf/AddSelf.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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 AddSelf = ({ sourceName, sourceType, targetName, targetType }) => {
return (
<Tabs defaultActiveKey={1} id='help-tab-container' justified>
<Tab
eventKey={1}
title='Info'
dangerouslySetInnerHTML={General(
sourceName,
sourceType,
targetName,
targetType
)}
/>
<Tab
eventKey={2}
title='Abuse Info'
dangerouslySetInnerHTML={Abuse(
sourceName,
sourceType,
targetName,
targetType
)}
/>
<Tab
eventKey={3}
title='Opsec Considerations'
dangerouslySetInnerHTML={Opsec()}
/>
<Tab
eventKey={4}
title='References'
dangerouslySetInnerHTML={References()}
/>
</Tabs>
);
};

AddSelf.propTypes = {
sourceName: PropTypes.string,
sourceType: PropTypes.string,
targetName: PropTypes.string,
targetType: PropTypes.string,
};
export default AddSelf;
15 changes: 15 additions & 0 deletions src/components/Modals/HelpTexts/AddSelf/General.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { groupSpecialFormat, typeFormat } from '../Formatter';

const General = (sourceName, sourceType, targetName, targetType) => {
let text = `${groupSpecialFormat(
sourceType,
sourceName
)} the ability to add ${
sourceType === 'Group' ? 'themselves' : 'itself'
} to the group ${targetName}. Because of security group delegation, the members of a security group have the same privileges as that group.

By adding itself to the group, ${sourceName} will gain the same privileges that ${targetName} already has.`;
return { __html: text };
};

export default General;
10 changes: 10 additions & 0 deletions src/components/Modals/HelpTexts/AddSelf/Opsec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const Opsec = () => {
let text = `Executing this abuse with the net binary will require command line execution. If your target organization has command line logging enabled, this is a detection opportunity for their analysts.

Regardless of what execution procedure you use, this action will generate a 4728 event on the domain controller that handled the request. This event may be centrally collected and analyzed by security analysts, especially for groups that are obviously very high privilege groups (i.e.: Domain Admins). Also be mindful that Powershell 5 introduced several key security features such as script block logging and AMSI that provide security analysts another detection opportunity.

You may be able to completely evade those features by downgrading to PowerShell v2.`;
return { __html: text };
};

export default Opsec;
8 changes: 8 additions & 0 deletions src/components/Modals/HelpTexts/AddSelf/References.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const References = () => {
let text = `<a href="https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1">https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1</a>
<a href="https://www.youtube.com/watch?v=z8thoG7gPd0">https://www.youtube.com/watch?v=z8thoG7gPd0</a>
<a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4728">https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4728</a>`;
return { __html: text };
};

export default References;
15 changes: 15 additions & 0 deletions src/components/SearchContainer/EdgeFilter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const EdgeFilter = ({ open }) => {
} else if (section === 'acl') {
current.AllExtendedRights = false;
current.AddMember = false;
current.AddSelf = false;
current.ForceChangePassword = false;
current.GenericAll = false;
current.GenericWrite = false;
Expand Down Expand Up @@ -50,6 +51,7 @@ const EdgeFilter = ({ open }) => {
} else if (section === 'acl') {
current.AllExtendedRights = true;
current.AddMember = true;
current.AddSelf = true;
current.ForceChangePassword = true;
current.GenericAll = true;
current.GenericWrite = true;
Expand Down Expand Up @@ -219,6 +221,19 @@ const EdgeFilter = ({ open }) => {
AddMember
</label>
</div>
<div>
<input
className='checkbox-inline'
type='checkbox'
name='AddSelf'
checked={edgeIncluded.AddSelf}
onChange={e => handleEdgeChange(e)}
/>
<label onClick={e => handleEdgeChange(e)} name='AddSelf'>
{' '}
AddSelf
</label>
</div>
<div>
<input
className='checkbox-inline'
Expand Down
4 changes: 2 additions & 2 deletions src/components/SearchContainer/Tabs/ComputerNodeData.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ const ComputerNodeData = () => {
property='Unrolled Object Controllers'
target={objectid}
baseQuery={
'MATCH p=(n)-[r:MemberOf*1..]->(g:Group)-[r1:AddMember|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(u:Computer {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:AddMember|AddSelf|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(u:Computer {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
Expand Down Expand Up @@ -396,7 +396,7 @@ const ComputerNodeData = () => {
property='Transitive Object Control'
target={objectid}
baseQuery={
'MATCH (n) WHERE NOT n.objectid=$objectid WITH n MATCH p = shortestPath((c:Computer {objectid: $objectid})-[r:MemberOf|AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n))'
'MATCH (n) WHERE NOT n.objectid=$objectid WITH n MATCH p = shortestPath((c:Computer {objectid: $objectid})-[r:MemberOf|AddMember|AddSelf|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n))'
}
start={label}
distinct
Expand Down
4 changes: 2 additions & 2 deletions src/components/SearchContainer/Tabs/GPONodeData.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const GPONodeData = () => {
property='Explicit Object Controllers'
target={objectid}
baseQuery={
'MATCH p = (n)-[r:AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(g:GPO {objectid: $objectid})'
'MATCH p = (n)-[r:AddMember|AddSelf|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(g:GPO {objectid: $objectid})'
}
end={label}
distinct
Expand All @@ -145,7 +145,7 @@ const GPONodeData = () => {
property='Transitive Object Controllers'
target={objectid}
baseQuery={
'MATCH (n) WHERE NOT n.objectid= $objectid WITH n MATCH p = shortestPath((n)-[r:MemberOf|AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(g:GPO {objectid: $objectid}))'
'MATCH (n) WHERE NOT n.objectid= $objectid WITH n MATCH p = shortestPath((n)-[r:MemberOf|AddMember|AddSelf|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(g:GPO {objectid: $objectid}))'
}
end={label}
distinct
Expand Down
6 changes: 3 additions & 3 deletions src/components/SearchContainer/Tabs/GroupNodeData.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ const GroupNodeData = () => {
property='Transitive Object Control'
target={objectid}
baseQuery={
'MATCH (n) WHERE NOT n.objectid=$objectid WITH n MATCH p = shortestPath((g:Group {objectid: $objectid})-[r:MemberOf|AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n))'
'MATCH (n) WHERE NOT n.objectid=$objectid WITH n MATCH p = shortestPath((g:Group {objectid: $objectid})-[r:MemberOf|AddMember|AddSelf|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n))'
}
start={label}
distinct
Expand All @@ -259,7 +259,7 @@ const GroupNodeData = () => {
property='Explicit Object Controllers'
target={objectid}
baseQuery={
'MATCH p = (n)-[r:AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(g:Group {objectid: $objectid})'
'MATCH p = (n)-[r:AddMember|AddSelf|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(g:Group {objectid: $objectid})'
}
end={label}
distinct
Expand All @@ -279,7 +279,7 @@ const GroupNodeData = () => {
property='Transitive Object Controllers'
target={objectid}
baseQuery={
'MATCH (n) WHERE NOT n.objectid=$objectid WITH n MATCH p = shortestPath((n)-[r:MemberOf|AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(g:Group {objectid: $objectid}))'
'MATCH (n) WHERE NOT n.objectid=$objectid WITH n MATCH p = shortestPath((n)-[r:MemberOf|AddMember|AddSelf|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(g:Group {objectid: $objectid}))'
}
end={label}
distinct
Expand Down
4 changes: 2 additions & 2 deletions src/components/SearchContainer/Tabs/UserNodeData.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ const UserNodeData = () => {
property='Transitive Object Control'
target={objectId}
baseQuery={
'MATCH (n) WHERE NOT n.objectid=$objectid MATCH p=shortestPath((u:User {objectid: $objectid})-[r1:MemberOf|AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n))'
'MATCH (n) WHERE NOT n.objectid=$objectid MATCH p=shortestPath((u:User {objectid: $objectid})-[r1:MemberOf|AddMember|AddSelf|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n))'
}
start={label}
distinct
Expand All @@ -305,7 +305,7 @@ const UserNodeData = () => {
property='Unrolled Object Controllers'
target={objectId}
baseQuery={
'MATCH p=(n)-[r:MemberOf*1..]->(g:Group)-[r1:AddMember|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(u:User {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:AddMember|AddSelf|AllExtendedRights|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns]->(u:User {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
Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ global.appStore = {
WriteDacl: 'tapered',
WriteOwner: 'tapered',
AddMember: 'tapered',
AddSelf: 'tapered',
TrustedBy: 'curvedArrow',
DCSync: 'tapered',
Contains: 'tapered',
Expand Down Expand Up @@ -207,6 +208,7 @@ global.appStore = {
WriteDacl: 'line',
WriteOwner: 'line',
AddMember: 'line',
AddSelf: 'line',
TrustedBy: 'curvedArrow',
DCSync: 'line',
Contains: 'line',
Expand Down Expand Up @@ -303,6 +305,7 @@ if (typeof conf.get('edgeincluded') === 'undefined') {
AdminTo: true,
AllExtendedRights: true,
AddMember: true,
AddSelf: true,
ForceChangePassword: true,
GenericAll: true,
GenericWrite: true,
Expand Down
2 changes: 2 additions & 0 deletions src/js/newingestion.js
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,8 @@ function processAceArrayNew(aces, objectid, objecttype, queries) {
rights.push('AllExtendedRights');
} else if (aceType === 'User-Force-Change-Password') {
rights.push('ForceChangePassword');
} else if (aceType === 'AddSelf') {
rights.push('AddSelf');
} else if (aceType === 'AddMember') {
rights.push('AddMember');
} else if (aceType === 'AllowedToAct') {
Expand Down