Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2021-4034-CTF-writeup — Writeup de desafio CTF pwn explorando CVE-2021-4034 (pkexec) através de manipulação de heap, com engenharia reversa baseada em Ghidra e um binário auxiliar customizado shelly.so. | Kitploit
Ferramentas/GitHubGitHub/wechicken456/cve-2021-4034-ctf-writeup
ExploraçãoEngenharia ReversaCTFAprendizado e EducaçãoExploração de BináriosLabs e Prática
GitHubwechicken456/cve-2021-4034-ctf-writeup

CVE-2021-4034-CTF-writeup

Writeup de desafio CTF pwn explorando CVE-2021-4034 (pkexec) através de manipulação de heap, com engenharia reversa baseada em Ghidra e um binário auxiliar customizado shelly.so.

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
Ver Repositório
2há 2 anosAinda não revisado

CVE-2021-4034-CTF-writeup

Este é um desafio CTF do tipo pwn que escrevi em C e que exige que o usuário explore a vulnerabilidade CVE-2021-4034. Os jogadores recebem 2 binários no diretório challenge deste repositório. O binário chal implementa o desafio CTF e o shelly.so é um binário auxiliar.

Como emular este desafio

No momento em que este writeup foi escrito, o Dockerfile ainda não está completo. O Dockerfile é necessário para implantar este desafio durante um CTF ao vivo, mas não localmente: Você ainda pode emular este desafio localmente configurando as permissões de usuário e instalando os pacotes vulneráveis da seguinte forma:

  1. Para explorar com sucesso essa vulnerabilidade, os jogadores que não estiverem em uma máquina Linux devem primeiro instalar uma VM Linux. Em seguida, você precisará instalar um kernel vulnerável. Instruções de como fazer isso: [https://askubuntu.com/a/700221]
  2. Instale os pacotes libpolkit-gobject-1-0=0.105-26ubuntu1 libpolkit-agent-1-0=0.105-26ubuntu1 policykit-1=0.105-26ubuntu1.
  3. Crie um arquivo flag.txt pertencente a root:root no diretório atual.
  4. Crie um usuário não privilegiado. Troque para esse usuário.
  5. Baixe os arquivos na pasta challenge para o diretório atual.
  6. Execute chal como o usuário não privilegiado.

Análise Cega

Executar o binário chal nos dá uma ideia vaga do que esse binário faz:``` 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:~
Brincar com o tamanho da entrada na função `Add` e os índices das funções `Cancel` não nos dá nada especial (sem overflow ou segmentation fault). No entanto, algumas coisas interessantes:
  - Parece que os pedidos são colocados numa lista (talvez uma lista ligada) e são indexados a partir de 0?
  - Há este `Order number: 0x7ffde93681f0` que parece imprimir alguma localização na pilha?
  - Além disso, o programa cria um diretório com o nome que introduzimos juntamente com 3 executáveis, um dos quais é o ficheiro binário auxiliar que nos é dado:```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

Os executáveis contêm nossa entrada.


Análise no Ghidra

Você pode brincar com as outras funções e torcer para encontrar alguns bugs (o que é provável), mas vou direto ao ponto e abrir o programa no Ghidra.

Comparando as strings que aparecem durante a execução do programa e as strings presentes no Ghidra, podemos renomear algumas das funções FUN_* para nomes 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:~
O `printf("Order number: %p\n",&local_18);` **imprime a localização de uma variável local na pilha.**

Uma verificação rápida no gdb mostra-nos que o endereço vazado é o endereço do ponteiro para a string constante `/recipe`:```gdb
...
Order number: 0x7fffffffdfc0
...
gef➤  x/gx 0x7fffffffdfc0
0x7fffffffdfc0:	0x0000555555559020
gef➤  x/s 0x0000555555559020
0x555555559020:	"/recipe"

Vemos uma chamada mkdir, que cria um diretório com o nome da nossa entrada no diretório atual.

Isso é consistente com nossa observação quando executamos o programa. Em seguida, ele inicializa alguma variável antes de chamar a função main_menu, que se parece com algo como:```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:~
Se você não está familiarizado com REV, esta é a descompilação para a declaração `switch` em C. Há uma opção interessante, também conhecida como `0x539`. Ela permite que os jogadores adivinhem `0x10` bytes aleatórios. Se todos os bytes forem iguais, então chama `FUN_00101504();` que chama `system("cat flag.txt");`. Caso contrário, o programa encerra.

No entanto, fazer bruteforce de 16 bytes aleatórios equivale a tentar todas as 256**16 = 340282366920938463463374607431768211456 possibilidades. Boa sorte com essa, lol.

Mesmo que você passe por todas as possibilidades, o programa ainda não tem privilégios, então não pode ler a flag. Esta declaração `cat flag.txt` é intencional, não apenas para enganar jogadores inexperientes a escolherem esta opção de menu `0x539`, mas também para impedir que os jogadores simplesmente chamem essa função para ler a flag, algo que exploraremos mais adiante.
***```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;
  }
...

Se algumas das variáveis não parecem fáceis de ler assim para você, é porque eu dediquei um tempo para renomeá-las, o que você deve fazer durante a engenharia reversa para acompanhar as coisas. Tudo bem, vamos aos pontos principais:

  • puVar2 é um chunk malloc de tamanho 0x30.
  • O campo 0 é o ponteiro puVar3, que tem 8 bytes e aponta para as notes do chunk atual.
  • Os próximos 4 campos têm cada um 8 bytes, embora possamos ler 9 bytes no 4º campo, então podemos transbordar 1 byte para o 5º campo.
  • No entanto, a linha puVar2[5] = 0; sobrescreve nosso transbordamento de 1 byte para NULL de qualquer maneira.
  • Então, se a variável global DAT_00105140 == 0, apenas a definimos como o novo chunk, então isso pode ser um ponteiro head para a lista?
  • Caso contrário, percorremos até o último chunk na lista atual e definimos seu 5º campo como o novo chunk. => O 5º campo é o ponteiro para o próximo chunk na lista ligada.

Edit```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:~
Existe uma verificação para o nosso índice de entrada, então não podemos editar locais arbitrários.

**No entanto, a leitura de 9 bytes no 4º campo que transborda 1 byte no 5º campo ainda está lá!!! Podemos sobrescrever 1 byte no ponteiro `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á até o caractere NULL, portanto, se nosso 4º campo tiver 8 bytes de comprimento, então o 4º %s imprimirá o 4º campo do nosso chunk + o valor do ponteiro nxt no 5º campo. => Obtemos um vazamento de heap!!!


Cancelar

Nada de interessante aqui.


Finalizar compra```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:~
Então ele cria os arquivos `recipe` e `notes` no diretório com o nome do nosso input. As instruções `chmod` definem esses arquivos no modo executável: `0x1f8` e `0x1c0` são `0700` e `0770` em 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 grande bloco de código bagunçado basicamente escreve o conteúdo de nossos chunks para esses arquivos, depois executa o comando cp /tmp/shelly.so dir_name, onde dir_name é o nome que damos ao programa no início.


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 é uma função interessante. Ela chama `pkexec` com `NULL argv` e o ponteiro do array `notes` como variáveis de ambiente, que é o comando vulnerável no nosso CVE-2021-4034. Isso sugere que temos que criar nosso `notes` e redirecionar a execução para esta função de alguma forma?

***
## shelly.so
Abrir isso no Ghidra nos dirá que é obviamente uma biblioteca `set-UID-root`, semelhante às usadas no exploit do CVE.

***
# Resumo
Alguns pontos principais:
1. Temos um vazamento de pilha no início do programa (Order number) que contém o endereço da string constante `/recipe` usada na criação do arquivo `recipe`.
2. O campo 0 de um chunk é o seu ponteiro `notes`.
3. O campo 5 de um chunk é o ponteiro `nxt` para o próximo chunk na lista encadeada.
4. `Show` pode vazar um endereço do heap que é o campo 5 de um chunk.
5. `Edit` pode sobrescrever 1 byte no campo 5. **ESTE É O ÚNICO BUG DE ESCRITA ARBITRÁRIA!!**
6. `FUN_00101e84` chama `pkexec` com NULL argv e as variáveis de ambiente que controlamos (`notes` array).

***
# Exploração

## Layout do chunk de heap
Uma inspeção rápida no gdb pode nos dizer o deslocamento entre diferentes chunks no nosso 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

Portanto, os chunks têm tamanho 0x40 (incluindo 0x10 bytes de metadados), e estão separados (do início do chunk atual ao início do próximo chunk) por um offset de 0x40+0x40 = 0x80. Isso ocorre porque as notes são alocadas sempre que alocamos um chunk, então elas sempre ficam entre chunks consecutivos.

Escrever em localização arbitrária

Antes de entrarmos no exploit, como abusamos dos bugs de estouro de 1 byte para escrever em um endereço arbitrário? Podemos usar a seguinte estratégia:

  1. Alocar 3 chunks A , B , C.
  2. Vazar o ponteiro nxt do 2º chunk (2º ponto na seção Sumário).
    • O endereço vazado (que é o próximo chunk) é C.
    • Então, o endereço do chunk atual será B = C-0x80.
    • Seja o byte MENOS significativo de B igual a x.
  3. Transbordar o byte x + 8 no 5º campo do chunk atual. O ponteiro nxt resultante será B + 8.
    • Temos que ter cuidado: usando o trecho de memória acima, se deixarmos o chunk atual ser o primeiro, ou seja, A = 0x55555555aae0, então o endereço vazado seria B = 0x55555555ab60 e x = 0xe8.
    • Se sobrescrevermos o último byte no 5º campo de A em 0x55555555ab08 com , o ponteiro resultante será , que não é o que queríamos.

1ª maneira - saltar para "cat flag.txt"

Existe uma maneira de saltar para esta função, a qual não vou discutir. Mas antes de tentar isso, dê uma olhada no Dockerfile. Percebe algo sobre flag.txt?``` ... chown root:root /home/ctf/flag.txt ... USER peasant CMD ["/home/ctf/start.sh"]

root@kitploit:~
***ELE É PROPRIEDADE DO ROOT, ENQUANTO O PROGRAMA É EXECUTADO E PROPRIEDADE DE UM USUÁRIO NÃO PRIVILEGIADO***.
Portanto, mesmo se você executar "cat flag.txt", ele não imprimirá a flag porque o programa não tem permissão para acessar o arquivo. Isso implica que precisamos escalar para root para ler a flag.
## 2ª maneira - CVE-2021-4034
Para explorar a CVE-2021-4034, precisamos da seguinte configuração:
1. Um diretório com o nome `GCONV_PATH=.`
2. Neste diretório, precisamos de um arquivo executável que seja o nome do diretório onde nosso arquivo de configuração `gconv-modules` estará. Seja este `recipe` em nosso desafio.
3. No diretório `recipe`, precisamos de um arquivo chamado `gconv-modules` com o conteúdo que controlamos, e uma biblioteca dinâmica que nos dará um shell (talvez este seja o binário fornecido `shelly.so`)?
4. Então, em nosso `gconv-modules`, precisamos ter o mesmo CHARSET do passo 5 abaixo, e o nome da nossa biblioteca dinâmica `shelly.so` assim: `module UTF-8// SHELLY// shelly 2`.
5. Após tudo isso, chamamos `pkexec` com argv NULL e o array de variáveis de ambiente criado = `{"recipe", "PATH=GCONV_PATH=.", "CHARSET=SHELLY", "SHELL=shelly", NULL}`.

Agora, exploramos este desafio para obter a configuração acima.

***
### Objetivo 1-2
Os passos 1-2 são fáceis: só precisamos fazer login com um nome de usuário `GCONV_PATH=.` para criar este diretório. Então fazemos um `checkout` para criar o executável `recipe` neste diretório. Depois retornamos ao primeiro menu.
***
### Objetivo 3-4
Fazemos login com o nome de usuário `recipe` para criar este diretório. Agora, *precisamos* criar um arquivo chamado `gconv-modules`, mas `checkout` cria apenas os arquivos `recipe` e `notes`.

E se sobrescrevêssemos a localização da memória para a string `/notes` para se tornar `/gconv-modules`? Isso poderia funcionar, mas requer que essa memória seja gravável. Vamos verificar isso no gdb:```gdb
gef➤  search-pattern /notes
[+] Searching '/notes' in memory
[+] In '/home/peasant/Desktop/chal'(0x555555559000-0x55555555a000), permission=rw-
  0x555555559010 - 0x555555559016  →   "/notes" 

And it is writable!

How do we perform the write?

  1. We use the arbitrary write strategy described above to arbitrarily READ the location of the leaked stack address. This gives us where /recipe is.
  2. A quick inspection in gdb can tell us the offset from /recipe to /notes is 0x10. Subtract this from the /recipe address in step 1 to get /notes address.
  3. Use the arbitrary write strategy above to overwrite /notes to /gconv-modules.

As to writing the correct content to the gconv-modules file, we know that at checkout, the notes content of the chunks will be written to /notes, which is now /gconv-modules. To be sure, we can just provide the string module UTF-8// SHELLY// shelly 2 to every notes.

So now, whenever we call checkout, it will create 2 files recipe and gconv-modules and write some module UTF-8// SHELLY// shelly 2 (we use shelly because that's the name of the .so set-uid-root library given) lines to the gconv-modules file. It will also copy a shelly.so into the same directory.

Goal 5

  1. We allocate 4 chunks whose notes are the corrsponding environment variables. The notes pointer array will have 4 pointers to these 4 notes and an ending NULL pointer, which is exactly what we wanted.
  2. Allocate additional chunks and use the same strategy we used for goal 3-4 to overwrite the RIP for the main function to our secret function FUN_00101e84.
    • How do we know where the RIP is? Remember our leaked stack address at the beginning of the program? Go into gdb to find out its offset to the RIP, then add that offset to the actual leaked address to get the RIP.
    • How do we know where main is? Remember our leaked address of /recipe from step 2 of Goal 3-4? Do the same thing here!
  3. Enjoy your shell :)

Solve script with comments is provided on this repo. Feel free to contact me if you have any questions :)

Baixar ferramenta
x
nxt
0x55555555**ab**e8 != 0x55555555**aa**e8
  • Portanto, temos que escolher um chunk atual e o próximo chunk de modo que seu 2º byte MENOS significativo sejam iguais.
  • Então agora temos o 3º chunk em B + 8 em vez de C. Editar o chunk B para que seu 1º campo (bread) contenha o endereço target para o qual queremos escrever.
  • Editar o 3º chunk, que sobrescreverá o conteúdo a partir de B + 8. O ponteiro notes deste 3º chunk é o target! Portanto, o que escrevermos em notes será escrito em target!