Cargad0r (Rev 200)

By @xbytemx

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.


Funcion valida_llave

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