Relro
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.
Relro
RELRO significa Relocation Read-Only e é uma mitigação implementada pelo linker (ld
) que torna um subconjunto dos segmentos de dados do ELF somente leitura após todas as realocações terem sido aplicadas. O objetivo é impedir que um atacante sobrescreva entradas na GOT (Tabela de Deslocamento Global) ou outras tabelas relacionadas a realocações que são desreferenciadas durante a execução do programa (por exemplo, __fini_array
).
Linkers modernos implementam RELRO reordenando a GOT (e algumas outras seções) para que elas fiquem antes da .bss e – mais importante – criando um segmento dedicado PT_GNU_RELRO
que é remapeado R–X
logo após o carregador dinâmico terminar de aplicar as realocações. Consequentemente, estouros de buffer típicos na .bss não podem mais alcançar a GOT e primitivas de escrita arbitrária não podem ser usadas para sobrescrever ponteiros de função que estão dentro de uma página protegida por RELRO.
Existem dois níveis de proteção que o linker pode emitir:
Partial RELRO
- Produzido com a flag
-Wl,-z,relro
(ou apenas-z relro
ao invocarld
diretamente). - Apenas a parte não-PLT da GOT (a parte usada para realocações de dados) é colocada no segmento somente leitura. Seções que precisam ser modificadas em tempo de execução – mais importante .got.plt que suporta lazy binding – permanecem graváveis.
- Por causa disso, uma primitiva de escrita arbitrária ainda pode redirecionar o fluxo de execução sobrescrevendo uma entrada PLT (ou realizando ret2dlresolve).
- O impacto no desempenho é negligenciável e, portanto, quase todas as distribuições têm enviado pacotes com pelo menos Partial RELRO há anos (é o padrão do GCC/Binutils desde 2016).
Full RELRO
- Produzido com ambas as flags
-Wl,-z,relro,-z,now
(também conhecido como-z relro -z now
).-z now
força o carregador dinâmico a resolver todos os símbolos antecipadamente (binding ansioso) para que .got.plt nunca precise ser escrita novamente e possa ser mapeada com segurança como somente leitura. - A GOT inteira, .got.plt, .fini_array, .init_array, .preinit_array e algumas tabelas internas adicionais da glibc acabam dentro de um segmento
PT_GNU_RELRO
somente leitura. - Adiciona uma sobrecarga de inicialização mensurável (todas as realocações dinâmicas são processadas na inicialização), mas sem sobrecarga em tempo de execução.
Desde 2023, várias distribuições populares mudaram para compilar a ferramenta de sistema (e a maioria dos pacotes) com Full RELRO por padrão – por exemplo, Debian 12 “bookworm” (dpkg-buildflags 13.0.0) e Fedora 35+. Como um pentester, você deve, portanto, esperar encontrar binários onde cada entrada da GOT é somente leitura.
Como verificar o status RELRO de um binário
$ checksec --file ./vuln
[*] '/tmp/vuln'
Arch: amd64-64-little
RELRO: Full
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
checksec
(parte do pwntools e muitas distribuições) analisa os cabeçalhos ELF
e imprime o nível de proteção. Se você não puder usar checksec
, confie em readelf
:
# Partial RELRO → PT_GNU_RELRO is present but BIND_NOW is *absent*
$ readelf -l ./vuln | grep -E "GNU_RELRO|BIND_NOW"
GNU_RELRO 0x0000000000600e20 0x0000000000600e20
# Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag
$ readelf -d ./vuln | grep BIND_NOW
0x0000000000000010 (FLAGS) FLAGS: BIND_NOW
Se o binário estiver em execução (por exemplo, um helper set-uid root), você ainda pode inspecionar o executável via /proc/$PID/exe
:
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
Habilitando RELRO ao compilar seu próprio código
# GCC example – create a PIE with Full RELRO and other common hardenings
$ gcc -fPIE -pie -z relro -z now -Wl,--as-needed -D_FORTIFY_SOURCE=2 main.c -o secure
-z relro -z now
funciona para ambos GCC/clang (passado após -Wl,
) e ld diretamente. Ao usar CMake 3.18+ você pode solicitar Full RELRO com o preset embutido:
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) # LTO
set(CMAKE_ENABLE_EXPORTS OFF)
set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-z,relro,-z,now")
Técnicas de Bypass
Nível RELRO | Primitiva típica | Técnicas de exploração possíveis |
---|---|---|
Nenhum / Parcial | Escrita arbitrária | 1. Sobrescrever a entrada .got.plt e mudar a execução. 2. ret2dlresolve – criar Elf64_Rela & Elf64_Sym falsos em um segmento gravável e chamar _dl_runtime_resolve .3. Sobrescrever ponteiros de função na lista .fini_array / atexit(). |
Completo | GOT é somente leitura | 1. Procurar outros ponteiros de código graváveis (vtables C++, __malloc_hook < glibc 2.34, __free_hook , callbacks em seções .data personalizadas, páginas JIT).2. Abusar de primitivas de leitura relativa para vazar libc e realizar SROP/ROP em libc. 3. Injetar um objeto compartilhado malicioso via DT_RPATH/ LD_PRELOAD (se o ambiente for controlado pelo atacante) ou ld_audit .4. Explorar format-string ou sobrescrita parcial de ponteiro para desviar o fluxo de controle sem tocar na GOT. |
💡 Mesmo com Full RELRO, a GOT de bibliotecas compartilhadas carregadas (por exemplo, a própria libc) é apenas RELRO Parcial porque esses objetos já estão mapeados quando o carregador aplica relocations. Se você ganhar uma primitiva de escrita arbitrária que pode direcionar as páginas de outro objeto compartilhado, ainda pode mudar a execução sobrescrevendo as entradas da GOT da libc ou a pilha
__rtld_global
, uma técnica frequentemente explorada em desafios modernos de CTF.
Exemplo de bypass no mundo real (2024 CTF – pwn.college “enlightened”)
O desafio foi enviado com Full RELRO. O exploit usou um off-by-one para corromper o tamanho de um chunk de heap, vazou libc com tcache poisoning
e finalmente sobrescreveu __free_hook
(fora do segmento RELRO) com um one-gadget para obter execução de código. Nenhuma escrita na GOT foi necessária.
Pesquisas recentes & vulnerabilidades (2022-2025)
- glibc 2.40 descontinuou
__malloc_hook
/__free_hook
(2025) – A maioria dos exploits modernos de heap que abusaram desses símbolos agora deve mudar para vetores alternativos, comortld_global._dl_load_jump
ou tabelas de exceção C++. Como os hooks vivem fora do RELRO, sua remoção aumenta a dificuldade de bypasses de Full-RELRO. - Correção “max-page-size” do Binutils 2.41 (2024) – Um bug permitiu que os últimos bytes do segmento RELRO compartilhassem uma página com dados graváveis em algumas compilações ARM64, deixando um pequeno gap RELRO que poderia ser escrito após
mprotect
. O upstream agora alinhaPT_GNU_RELRO
aos limites de página, eliminando esse caso extremo.
Referências
- Documentação do Binutils –
-z relro
,-z now
ePT_GNU_RELRO
- “RELRO – Full, Partial and Bypass Techniques” – post no blog @ wolfslittlered 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.