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 depois que todas as relocations foram aplicadas. O objetivo é impedir que um atacante sobrescreva entradas na GOT (Global Offset Table) ou outras tabelas relacionadas a relocations que são desreferenciadas durante a execução do programa (ex.: __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 loader dinâmico terminar de aplicar as relocations. Consequentemente, estouros de buffer típicos na .bss não conseguem mais alcançar a GOT e primitivos de escrita arbitrária não podem ser usados para sobrescrever ponteiros de função que residem 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 relroquando invocandolddiretamente). - Apenas a parte non-PLT da GOT (a parte usada para relocations 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, um primitivo de arbitrary write ainda pode redirecionar o fluxo de execução sobrescrevendo uma entrada da PLT (ou realizando ret2dlresolve).
- O impacto em performance é negligível e, portanto, quase todas as distribuições vêm empacotando pacotes com pelo menos Partial RELRO há anos (é o default do GCC/Binutils desde 2016).
Full RELRO
- Produzido com ambas as flags
-Wl,-z,relro,-z,now(a.k.a.-z relro -z now).-z nowforça o loader dinâmico a resolver todos os símbolos antecipadamente (eager binding) de modo que .got.plt nunca precise ser escrita novamente e possa ser mapeada seguramente como somente leitura. - A GOT inteira, .got.plt, .fini_array, .init_array, .preinit_array e algumas tabelas internas adicionais do glibc terminam dentro de um segmento
PT_GNU_RELROsomente leitura. - Adiciona overhead mensurável no start-up (todas as relocations dinâmicas são processadas no lançamento) mas sem overhead em tempo de execução.
Desde 2023 várias distribuições mainstream mudaram para compilar a system tool-chain (e a maioria dos pacotes) com Full RELRO por padrão – ex.: Debian 12 “bookworm” (dpkg-buildflags 13.0.0) e Fedora 35+. Como pentester, você deve portanto esperar encontrar binários onde toda entrada da GOT é somente leitura.
How to Check the RELRO status of a binary
$ 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, use 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 set-uid root helper), 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 code
# 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 tanto para GCC/clang (passado após -Wl,) quanto para 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
| RELRO level | Primitiva típica | Técnicas de exploração possíveis |
|---|---|---|
| Nenhum / Parcial | Arbitrary write | 1. Sobrescrever .got.plt entry e pivotar execução. 2. ret2dlresolve – craft fake Elf64_Rela & Elf64_Sym in a writable segment and call _dl_runtime_resolve.3. Sobrescrever function pointers em .fini_array / atexit() list. |
| Full | GOT é somente leitura | 1. Procure por outros ponteiros de código graváveis (C++ vtables, __malloc_hook < glibc 2.34, __free_hook, callbacks em seções .data customizadas, páginas JIT).2. Abuse relative read primitives para leak libc e perform SROP/ROP into libc. 3. Injete um shared object malicioso via DT_RPATH/ LD_PRELOAD (se o ambiente for controlado pelo atacante) ou ld_audit.4. Explore format-string ou overwrite parcial de ponteiro para desviar o control-flow sem tocar no GOT. |
💡 Mesmo com Full RELRO a GOT of loaded shared libraries (e.g. libc itself) é only Partial RELRO porque esses objetos já estão mapeados quando o loader aplica as relocations. Se você ganhar uma primitiva de arbitrary write que possa mirar as páginas de outro shared object você ainda pode pivotar a execução sobrescrevendo entradas da GOT do libc ou a stack
__rtld_global, uma técnica regularmente explorada em CTFs modernos.
Exemplo de bypass no mundo real (2024 CTF – pwn.college “enlightened”)
O desafio veio com Full RELRO. O exploit usou um off-by-one para corromper o tamanho de um chunk do heap, leaked 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 hook removal (2.34 → present) – os malloc/free hooks foram extraídos do libc principal para o opcional
libc_malloc_debug.so, eliminando uma primitiva comum de bypass de Full‑RELRO; exploits modernos devem mirar outros ponteiros graváveis. - GNU ld RELRO page‑alignment fix (binutils 2.39+/2.41) – o bug do linker 30612 fazia com que os últimos bytes de
PT_GNU_RELROcompartilhassem uma página gravável em sistemas com páginas de 64 KiB; as binutils atuais alinham RELRO aomax-page-size, fechando essa “RELRO gap”.
Referências
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.


