By @xbytemx
Descripcion:
Funcion valida_llave
Se resta menos 1 al valor donde se encuentra almacenado la longitud del string, evaluando si dentro de cada byte hay algún carácter "=". Esto con la finalidad de identificar la longitud final del lugar donde colocaremos el cambio de encoding.
Descripcion:
Gran trabajo con esa shell en la DMZ!!!! Eso nos ha permitido encontrar el binario adjunto que parece permite descargar
informacion de un servidor Web en la red interna del banco! Pero requiere de
una llave de descarga para poder conectarse correctamente, encuentrala y obten
la flag!
Primero trataremos de identificar con file
el binario para obtener información inicial sobre el reto:
Se guarda argc y argv respectivamente, para
después validar si argc es mayor a 1, si es falso, el bloque siguiente es el de
la izquierda en donde se imprime como usar el binario. En caso de que argc sea
mayor a 1, se toma el camino de la derecha, en donde se imprimen dos mensajes
de bienvenida. Posteriormente se carga en RAX el valor de primer argumento del
programa, argv[1].
Dicho valor es entregado a STRLEN, el cual
nos devolverá la longitud del argv[1], el cual por defecto se almacena en RAX.
La penúltima linea evalúa si RAX es igual a
8, y posteriormente tenemos un jmp condicional a not zero.
En el siguiente bloque observamos que se le
manda la llave ingresada en el primer argumento hacia la función valida_llave.
La salida de esta función es almacenada en src.
Posterior a eso, se llama a socket_connect
con los argumentos "hack.def" y 9000. La salida de la creación del
socket se almacena en fd.
La siguiente función en ser llamada es
strcat, que combina 3 valores hexadecimales que en realidad son el string
"GET /flag.txt HTTP/1.0\n" y la salida de valida_llave, para
almacenarlo en RBP+dest. Todo esto moviendo los punteros
correspondientes a donde se encuentran almacenados los strings. Adicionalmente
en las ultimas instrucciones, se agrega un “\n\n” a RBP+dest para
finalizar el request.
El programa continua con un strlen de dest,
que después es enviado a write con los argumentos del socket(fd), buffer
(RBP+dest) y el tamaño del buffer (strlen(RBP+dest)). Lo cual envía los
datos sobre el socket recién establecido, en este caso un GET request sobre el
puerto 9000 (HTTP) a hack.def.
En la primera parte, se carga un string en
el stack, se hace un malloc de 50 bytes, se extrae la longitud del string
cargado en el stack. Se realizan varias operaciones sobre la longitud del
string y se almacenan en la variable [rbp+k].
Se resta menos 1 al valor donde se encuentra almacenado la longitud del string, evaluando si dentro de cada byte hay algún carácter "=". Esto con la finalidad de identificar la longitud final del lugar donde colocaremos el cambio de encoding.
Esta parte es de las mas importantes pues
se evalúan si los contadores o la comparativa del loop se ha cumplido, de
terminar el if, se llama a strcat que recibe el valor generado por la función
meme_del_gato y el string "User-Agent: " lo cual nos da un claro
guiño al valor que debe devolver meme_del_gato.
La función meme_del_gato recibe dos
argumentos: la llave y el b64_decoded.
Es en este momento cuando strcat retorna el
resultado a main.
Funcion meme_del_gato
Al inicio de la función tenemos el stack
frame, la asignación de argumentos a variables, la declaración de la variable
output vía malloc de strlen(buffer) y la inicialización de counter.
El siguiente bloque obtiene la longitud del
buffer y lo compara contra el contador. Posteriormente tenemos un salto
condicional en donde si el contador es menor al tamaño de buffer, brincamos a
la izquierda. Caso contrario, hemos acabado la función y tomamos el camino
derecho.
En la segunda parte tenemos un compare entre
strlen(buffer) y counter, lo que valida basicamente si ya salio del loop.
Dentro del loop estaremos realizando algo como lo siguiente:
modulo =
counter % strlen(llave)
output[counter] = llave[modulo] ^ buffer[counter]
Finalmente, cuando counter alcanza al
tamaño del buffer, devolvemos output como resultado de la operación del xor
entre la llave y el buffer.
Solucion
El binario usa la llave para generar un
User-Agent especifico, que es recibido por el servidor hackdef.
Se pudiera atacar por fuerza bruta el
programa usando xortool, ya que como sabemos, la longitud de la llave es de 8
caracteres. Pero xortool aun sin este dato, tampoco puede hacer una
aproximación exacta de la longitud, dado el tamaño pequeño del texto protegido:
Pero la solución usando xortool, la
herramienta tardaría muchísimo tiempo dado que no conocemos ni los caracteres
validos ni si se trata de combinaciones o permutaciones. Adicionalmente,
xortool almacena todos los resultados que encuentra, por lo que si revisamos
con crunch, podríamos tener un aproximado de solo minúsculas mas números:
Si analizamos la estructura de 5 de los
user-agents mas usados (https://github.com/yusuzech/top-50-user-agents) veremos lo siguiente:
Mozilla/5.0
(Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/60.0.3112.113 Safari/537.36
Mozilla/5.0
(Windows NT 5.1; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Mozilla/5.0
(Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
Mozilla/5.0
(Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/60.0.3112.90 Safari/537.36
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36
Los caracteres validos dentro del
User-Agent son los siguientes: a-zA-Z0-9 + “ .,;()/”
Usando un código más optimizado, podemos
plantear que cualquier combinación de letra+numero+caracteres_especiales, debe
encontrarse tanto en la llave como en el mensaje:
#!/usr/bin/env python3 from base64 import b64decode import string, random def xor_this(secret, key): output = "" for idx1 in range(0,len(secret)): idx2 = idx1 % len(key); output += chr(secret[idx1] ^ ord(key[idx2])) return output def main(): b64 = "OlUBBRJGUFQMBlIcXUIRTA0aAUxGUVJWEA==" protected_bytes = b64decode(b64) user_agent = "" valid_keys = tries = type = 0 while valid_keys < 10: if tries == 30000000: if type == 0: key = ''.join(random.choices(string.digits, k=8)) print('.',end="") elif tries < 60000000 and tries >= 30000000: key = ''.join(random.choices(string.ascii_lowercase, k=8)) print(',',end="") elif tries < 90000000 and tries >= 60000000: key = ''.join(random.choices(string.ascii_lowercase, k=4)) + ''.join(random.choices(string.digits, k=4)) print('!',end="") else: key = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8)) print('|',end="") user_agent = xor_this(protected_bytes,key) tries += 1 if (all(c in (string.ascii_uppercase + string.ascii_lowercase + ' ') for c in user_agent)): print("key: {} \ttext: {} -- Tries: {}".format(key,user_agent,tries)) valid_keys += 1 input("Press Enter to continue...") if __name__ == "__main__": main()
La primera opción es que en las primeras iteraciones
se encuentre la flag compuesta de puros números, en la segunda iteración
grande, que se trate de solo letras, en la tercera, hacemos una combinación
tipo split de ambas y en la final una combinación masiva. La generación es
aleatoria, por lo que teniendo el primer resultado, sabremos como esta
compuesta la estructura de la flag.
Después de aproximadamente 20M de intentos
y 6 minutos, tenemos la estructura de la flag, como una combinación entre 4
letras minúsculas y 4 números:
Modificamos el script para únicamente
realizar este tipo de validación y tenemos los siguiente:
Un par de intentos después tendremos los
siguientes candidatos:
Primera ronda
key: tnrl2095
text: N;si viaxh por(yyts takcd --
Tries: 176846
key:
tnrd2798 text: N;sa qilxh xou(tyts(tfknd
-- Tries: 333916
key:
unil2399 text: O;hi uimyh;poq(uxth tbkoe
-- Tries: 1839580
key: huid2699
text: R ha pimds;xot(ueoh(tgkox --
Tries: 2042045
key: cnrd2615
text: Y;sa paaoh xot ynts(tgccs --
Tries: 2109496
key: curd2085
text: Y sa vhaos xor)ynos(tajcs --
Tries: 2210414
key: uuid2098
text: O ha vilys;xor(txoh(takne --
Tries: 2543835
key: cnrl2315
text: Y;si uaaoh poq ynts tbccs --
Tries: 2756961
key: inid2298
text: S;ha tileh;xop(tdth(tckny --
Tries: 3315860
key: xurd2498
text: B sa rilts xov(tuos(teknh --
Tries: 4155864
Segunda ronda
key: burl2219
text: X si tamns pop uoos tccor --
Tries: 50292
key: inil2288
text: S;hi thleh;pop)tdth tcjny --
Tries: 152109
key: tnrl2518
text: N;si salxh pow tyts tdcnd --
Tries: 159605
key: jnrd2395
text: P;sa uiafh xoq(ygts(tbkcz --
Tries: 770008
key: hurw2285
text: R sr thads kop)yeos;tcjcx --
Tries: 900503
key: tuil2495
text: N hi riaxs;pov(yyoh tekcd --
Tries: 993534
key: iniw2519
text: S;hr sameh;kow udth;tdcoy --
Tries: 1201082
key: tnrw2585
text: N;sr shaxh kow)yyts;tdjcd --
Tries: 3816142
key: ynrl2389
text: C;si uhmuh poq)utts tbjoi --
Tries: 5475274
key: yuid2319
text: C ha uamus;xoq utoh(tbcoi --
Tries: 7410570
Tercera ronda
key: xniw2619
text: B;hr pamth;kot uuth;tgcoh -- Tries:
169329
key: cnid2598
text: Y;ha siloh;xow(tnth(tdkns --
Tries: 260452
key: ynil2698
text: C;hi piluh;pot(ttth tgkni --
Tries: 344210
key: unrw2389
text: O;sr uhmyh koq)uxts;tbjoe --
Tries: 855353
key: jnrw2218
text: P;sr talfh kop tgts;tccnz -- Tries:
1087420
key: unrl2318
text: O;si ualyh poq txts tbcne --
Tries: 1357224
key: jnrl2598
text: P;si silfh pow(tgts tdknz --
Tries: 1502782
key: xniw2595
text: B;hr siath;kow(yuth;tdkch --
Tries: 2461945
key: xurl2595
text: B si siats pow(yuos tdkch --
Tries: 2982824
key: tnrw2388 text:
N;sr uhlxh koq)tyts;tbjnd -- Tries: 3498920
Después de tres rondas, es evidente que el
5 carácter de la llave es ‘2’. Usando una
muestra de 30 elementos, podemos buscar la normalización por TLC y estimar los
siguientes candidatos:
Las posibilidades para el carácter:
Modificando nuevamente el script de la
siguiente manera, nos arrojara los primeros 100 resultados validos con los
caracteres previos y ya que hemos visto que el carácter espacio si esta
presente (los demas se ven aleatorios), mantendremos unicamente el carácter
especial espacio:
#!/usr/bin/env python3 from base64 import b64decode import string, random def xor_this(secret, key): output = "" for idx1 in range(0,len(secret)): idx2 = idx1 % len(key); output += chr(secret[idx1] ^ ord(key[idx2])) return output def main(): b64 = "OlUBBRJGUFQMBlIcXUIRTA0aAUxGUVJWEA==" protected_bytes = b64decode(b64) user_agent = "" valid_keys = tries = 0 base = pow(10,7) while valid_keys < 100: key = ''.join(random.choices("txucyjihb") + random.choices("nu") + random.choices("ri") + random.choices("ldw") + random.choices("2") + random.choices("3526047") + random.choices("918") + random.choices("859")) user_agent = xor_this(protected_bytes,key) tries += 1 if (all(c in (string.ascii_uppercase + string.ascii_lowercase + ' ') for c in user_agent)): print("key: {} \ttext: {} -- Tries: {}".format(key,user_agent,tries)) valid_keys += 1 if __name__ == "__main__": main()
El resultado es el siguiente:
xbytemx@laptop:~/dev/reto200/bunkerClientDownloader2$
time python3 get_llave4.py
key: uurl2519
text: O si samys pow uxos tdcoe --
Tries: 7
key: iurl2419
text: S si rames pov udos tecoy --
Tries: 21
key: yurl2319
text: C si uamus poq utos tbcoi --
Tries: 84
key: curl2615
text: Y si paaos pot ynos tgccs --
Tries: 167
key: jurl2418
text: P si ralfs pov tgos tecnz --
Tries: 205
key: jurl2018 text: P si valfs por tgos tacnz -- Tries: 247
key: uurl2218 text: O si talys pop txos tccne -- Tries: 252
key: hurl2219 text: R si tamds pop ueos tccox -- Tries: 270
key: hurl2515
text: R si saads pow yeos tdccx --
Tries: 290
key: iurl2618
text: S si pales pot tdos tgcny --
Tries: 305
key: uurl2419
text: O si ramys pov uxos tecoe --
Tries: 315
key: uurl2218
text: O si talys pop txos tccne --
Tries: 384
key: xurl2215
text: B si taats pop yuos tccch --
Tries: 485
key: jurl2219
text: P si tamfs pop ugos tccoz --
Tries: 548
key: turl2518
text: N si salxs pow tyos tdcnd --
Tries: 603
key: jurl2318
text: P si ualfs poq tgos tbcnz --
Tries: 721
key: burl2318
text: X si ualns poq toos tbcnr --
Tries: 733
key: turl2219
text: N si tamxs pop uyos tccod --
Tries: 804
key: yurl2718
text: C si qalus pou ttos tfcni --
Tries: 830
key: yurl2318
text: C si ualus poq ttos tbcni --
Tries: 840
key: xurl2319
text: B si uamts poq uuos tbcoh --
Tries: 885
key: hurl2615
text: R si paads pot yeos tgccx --
Tries: 904
key: curl2319 text: Y si uamos poq unos tbcos -- Tries: 927
key: turl2219
text: N si tamxs pop uyos tccod --
Tries: 939
key: curl2719 text: Y si qamos pou unos tfcos -- Tries: 973
key: burl2215
text: X si taans pop yoos tcccr --
Tries: 1013
key: burl2618
text: X si palns pot toos tgcnr --
Tries: 1046
key: iurl2019
text: S si vames por udos tacoy --
Tries: 1093
key: hurl2015
text: R si vaads por yeos taccx --
Tries: 1160
key: iurl2715
text: S si qaaes pou ydos tfccy --
Tries: 1181
key: yurl2418
text: C si ralus pov ttos tecni --
Tries: 1196
key: turl2715
text: N si qaaxs pou yyos tfccd --
Tries: 1219
key: yurl2215
text: C si taaus pop ytos tccci --
Tries: 1238
key: jurl2715
text: P si qaafs pou ygos tfccz --
Tries: 1247
key: iurl2518
text: S si sales pow tdos tdcny --
Tries: 1276
key: turl2218
text: N si talxs pop tyos tccnd --
Tries: 1308
key: yurl2715
text: C si qaaus pou ytos tfcci --
Tries: 1342
key: jurl2715
text: P si qaafs pou ygos tfccz --
Tries: 1417
key: curl2219 text: Y si tamos pop unos tccos -- Tries: 1429
key: jurl2215 text: P si taafs pop ygos tcccz -- Tries: 1518
key: curl2319 text: Y si uamos poq unos tbcos -- Tries: 1557
key: iurl2419
text: S si rames pov udos tecoy --
Tries: 1654
key: hurl2015
text: R si vaads por yeos taccx --
Tries: 1680
key: turl2418
text: N si ralxs pov tyos tecnd --
Tries: 1687
key: turl2219
text: N si tamxs pop uyos tccod --
Tries: 1695
key: yurl2718
text: C si qalus pou ttos tfcni --
Tries: 1700
key: yurl2218
text: C si talus pop ttos tccni --
Tries: 1736
key: turl2715
text: N si qaaxs pou yyos tfccd --
Tries: 1737
key: yurl2519
text: C si samus pow utos tdcoi --
Tries: 1759
key: turl2515
text: N si saaxs pow yyos tdccd --
Tries: 1799
key: uurl2315
text: O si uaays poq yxos tbcce --
Tries: 1821
key: xurl2515
text: B si saats pow yuos tdcch --
Tries: 1829
key: yurl2618
text: C si palus pot ttos tgcni --
Tries: 1847
key: iurl2415
text: S si raaes pov ydos teccy --
Tries: 1884
key: turl2418
text: N si ralxs pov tyos tecnd -- Tries:
1914
key: xurl2318
text: B si ualts poq tuos tbcnh --
Tries: 1953
key: xurl2615
text: B si paats pot yuos tgcch --
Tries: 2007
key: iurl2318
text: S si uales poq tdos tbcny --
Tries: 2051
key: uurl2218
text: O si talys pop txos tccne --
Tries: 2095
key: hurl2519
text: R si samds pow ueos tdcox --
Tries: 2104
key: iurl2715
text: S si qaaes pou ydos tfccy --
Tries: 2158
key: jurl2415
text: P si raafs pov ygos teccz --
Tries: 2192
key: xurl2018
text: B si valts por tuos tacnh --
Tries: 2274
key: curl2618
text: Y si palos pot tnos tgcns --
Tries: 2308
key: xurl2219
text: B si tamts pop uuos tccoh --
Tries: 2371
key: uurl2415
text: O si raays pov yxos tecce --
Tries: 2381
key: yurl2715
text: C si qaaus pou ytos tfcci --
Tries: 2389
key: curl2415
text: Y si raaos pov ynos teccs --
Tries: 2501
key: turl2719
text: N si qamxs pou uyos tfcod --
Tries: 2567
key: iurl2519
text: S si sames pow udos tdcoy --
Tries: 2570
key: yurl2419
text: C si ramus pov utos tecoi --
Tries: 2648
key: jurl2019 text: P si vamfs por ugos tacoz -- Tries: 2720
key: jurl2015 text: P si vaafs por ygos taccz -- Tries: 2809
key: turl2618
text: N si palxs pot tyos tgcnd --
Tries: 2825
key: xurl2019 text: B si vamts por uuos tacoh -- Tries: 2844
key: yurl2219
text: C si tamus pop utos tccoi --
Tries: 2902
key: hurl2618
text: R si palds pot teos tgcnx --
Tries: 2906
key: iurl2518
text: S si sales pow tdos tdcny --
Tries: 2962
key: jurl2519
text: P si samfs pow ugos tdcoz --
Tries: 2975
key: uurl2015 text: O si vaays por yxos tacce -- Tries: 3045
key: uurl2315
text: O si uaays poq yxos tbcce --
Tries: 3062
key: hurl2518
text: R si salds pow teos tdcnx --
Tries: 3149
key: curl2615
text: Y si paaos pot ynos tgccs --
Tries: 3166
key: xurl2515
text: B si saats pow yuos tdcch --
Tries: 3224
key: uurl2318
text: O si ualys poq txos tbcne --
Tries: 3274
key: iurl2515
text: S si saaes pow ydos tdccy --
Tries: 3299
key: jurl2719 text: P si qamfs pou ugos tfcoz -- Tries: 3307
key: curl2219 text: Y si tamos pop unos tccos -- Tries: 3385
key: yurl2518
text: C si salus pow ttos tdcni --
Tries: 3456
key: turl2519
text: N si samxs pow uyos tdcod --
Tries: 3488
key: burl2018
text: X si valns por toos tacnr --
Tries: 3495
key: turl2718
text: N si qalxs pou tyos tfcnd --
Tries: 3532
key: uurl2415
text: O si raays pov yxos tecce --
Tries: 3534
key: turl2218
text: N si talxs pop tyos tccnd --
Tries: 3557
key: turl2318
text: N si ualxs poq tyos tbcnd --
Tries: 3642
key: uurl2215
text: O si taays pop yxos tccce --
Tries: 3653
key: iurl2618
text: S si pales pot tdos tgcny --
Tries: 3687
key: xurl2418
text: B si ralts pov tuos tecnh --
Tries: 3734
key: yurl2418
text: C si ralus pov ttos tecni --
Tries: 3764
key: jurl2618
text: P si palfs pot tgos tgcnz --
Tries: 3794
real 0m0.185s
user 0m0.163s
sys 0m0.020s
Ahora, hemos identificado 3 letras mas y un
numero. La segunda, la tercera, la cuarta y la séptima: “u”, “r”, “l” y “1”.
Ahora podemos dejar fijas dichas letras en
el get_key y ejecutar otra ronda:
curl es la
palabra, ya que ninguna otra letra le da sentido a la sentencia. Ahora solo
falta identificar los números restantes:
Ya con la llave y el User-Agent valido: "Y si vamos por unos tacos", el servidor web contesta con la flag!!!
Comments
Post a Comment