
Binary Hardening
- 260 installs
- 155 repo stars
- Updated June 27, 2026
- mohitmishra786/low-level-dev-skills
Harden compiled binaries with stack protections, RELRO, PIE, and fortification flags to reduce exploit surface before release.
About
Documents binary hardening for native builds: selecting compiler and linker mitigations, enabling stack canaries and full RELRO, enforcing PIE and NX, auditing ELF/PE security properties, and validating protections before shipping executables.
- Compiler hardening flag sets
- RELRO, PIE, and NX enforcement
- Stack canary verification
- Strip vs symbol retention tradeoffs
- checksec-style binary audits
Binary Hardening by the numbers
- 260 all-time installs (skills.sh)
- +20 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #668 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill binary-hardeningAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 260 |
|---|---|
| repo stars | ★ 155 |
| Last updated | June 27, 2026 |
| Repository | mohitmishra786/low-level-dev-skills ↗ |
What it does
Harden compiled binaries with stack protections, RELRO, PIE, and fortification flags to reduce exploit surface before release.
Files
Binary Hardening
Purpose
Guide agents through enabling and verifying binary security mitigations: checksec analysis, compiler and linker hardening flags (RELRO, PIE, stack canaries, FORTIFY_SOURCE, CFI), hardware shadow stack, and seccomp-bpf syscall filtering for defense-in-depth.
Triggers
- "How do I harden my binary against exploits?"
- "How do I check what security mitigations my binary has?"
- "What does checksec output mean?"
- "How do I enable RELRO, PIE, and stack canaries?"
- "How do I use seccomp to restrict syscalls?"
- "How do I enable CFI (control flow integrity)?"
Workflow
1. Analyze existing binary with checksec
# Install checksec
pip install checksec.py # or: apt install checksec
# Check a binary
checksec --file=./mybinary
checksec --file=/usr/bin/ssh
# Output example
# RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
# Full RELRO Canary found NX PIE No RPATH No RUNPATH No Symbols Yes 6 10 ./mybinary
# Check all binaries in a directory
checksec --dir=/usr/bin| Protection | Good value | Concern |
|---|---|---|
| RELRO | Full RELRO | Partial / No RELRO |
| Stack Canary | Canary found | No canary |
| NX | NX enabled | NX disabled |
| PIE | PIE enabled | No PIE |
| FORTIFY | Yes | No |
2. Hardening compiler and linker flags
# Full hardened build (GCC or Clang)
CFLAGS="-O2 -pipe \
-fstack-protector-strong \
-fstack-clash-protection \
-fcf-protection \
-D_FORTIFY_SOURCE=3 \
-D_GLIBCXX_ASSERTIONS \
-fPIE \
-Wformat -Wformat-security -Werror=format-security"
LDFLAGS="-pie \
-Wl,-z,relro \
-Wl,-z,now \
-Wl,-z,noexecstack \
-Wl,-z,separate-code"
gcc ${CFLAGS} -o prog main.c ${LDFLAGS}Flag reference:
| Flag | Protection | Notes |
|---|---|---|
-fstack-protector-strong | Stack canary | Stronger than -fstack-protector |
-fstack-clash-protection | Stack clash | Prevents huge stack allocations |
-fcf-protection | Intel CET (IBT+SHSTK) | x86 hardware CFI (kernel+CPU required) |
-D_FORTIFY_SOURCE=2 | Buffer overflow checks | Adds bounds checks to string/mem functions |
-D_FORTIFY_SOURCE=3 | Enhanced FORTIFY | GCC ≥12, Clang ≥12 |
-fPIE + -pie | PIE/ASLR | Position independent executable |
-Wl,-z,relro | Partial RELRO | Makes GOT read-only before main |
-Wl,-z,now | Full RELRO | Resolves all PLT at startup → GOT fully RO |
-Wl,-z,noexecstack | NX stack | Marks stack non-executable |
3. Control Flow Integrity (CFI)
Clang's CFI prevents calling virtual functions through wrong types (vtable CFI) and indirect calls to mismatched functions:
# Clang CFI — requires LTO and visibility
clang -fsanitize=cfi -fvisibility=hidden -flto \
-O2 -fPIE -pie main.cpp -o prog
# Specific CFI checks
clang -fsanitize=cfi-vcall # virtual call type check
clang -fsanitize=cfi-icall # indirect call type check
clang -fsanitize=cfi-derived-cast # derived-to-base cast
clang -fsanitize=cfi-unrelated-cast # unrelated type cast
# Cross-DSO CFI (across shared libraries — more complex)
clang -fsanitize=cfi -fsanitize-cfi-cross-dso -flto -fPIC -shared# Microsoft CFG (Windows equivalent)
cl /guard:cf prog.c
link /guard:cf prog.obj4. Stack canaries in depth
# GCC canary options
-fno-stack-protector # disabled
-fstack-protector # protect functions with alloca or buffers > 8 bytes
-fstack-protector-strong # protect functions with local arrays/addresses taken
-fstack-protector-all # protect all functions (slowest, most complete)
# Verify canary presence
objdump -d prog | grep -A5 "__stack_chk"
readelf -s prog | grep "stack_chk"5. FORTIFY_SOURCE
FORTIFY_SOURCE wraps unsafe libc functions (memcpy, strcpy, sprintf) with bounds-checked versions when the buffer size can be determined at compile time:
# Level 2 (GCC/Clang default for hardened builds)
-D_FORTIFY_SOURCE=2
# Runtime check: abort() on overflow
# Level 3 (GCC ≥12, catches more cases)
-D_FORTIFY_SOURCE=3
# Adds dynamic buffer size tracking for more coverage
# Check FORTIFY coverage
objdump -d prog | grep "__.*_chk" # fortified variants
checksec --file=prog | grep FORTIFY6. seccomp-bpf syscall filtering
#include <seccomp.h>
void apply_seccomp_filter(void) {
scmp_filter_ctx ctx;
// Default: kill process on any non-allowlisted syscall
ctx = seccomp_init(SCMP_ACT_KILL_PROCESS);
// Allowlist needed syscalls
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
// Apply filter (irreversible after this point)
seccomp_load(ctx);
seccomp_release(ctx);
}
// Call early in main(), after all setup
int main(void) {
// ... initialization ...
apply_seccomp_filter();
// ... restricted operation ...
}# Test seccomp filter with strace
strace -e trace=all ./prog 2>&1 | grep "killed by SIGSYS"
# Profile syscalls to build allowlist
strace -c ./prog # count all syscalls used7. Shadow stack (Intel CET / Hardware SHSTK)
# Enable on supported x86 hardware (Intel Tiger Lake+, kernel ≥6.6)
# -fcf-protection=full enables both IBT and SHSTK
clang -fcf-protection=full -O2 -o prog main.c
# Check CET support in binary
readelf -n prog | grep "NT_GNU_PROPERTY"
objdump -d prog | grep "endbr64" # IBT end-branch instructions
# Kernel support
cat /proc/cpuinfo | grep shstk # CPU supportFor the full hardening flags reference, see references/hardening-flags.md.
Related skills
- Use
skills/runtimes/sanitizersfor ASan/UBSan during development - Use
skills/observability/ebpffor seccomp-bpf program writing with libbpf - Use
skills/rust/rust-securityfor Rust's memory-safety hardening approach - Use
skills/binaries/elf-inspectionto verify mitigations in ELF binaries
Binary Hardening Flags Reference
Source: https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++
Complete Hardened Build Commands
GCC (Linux)
CFLAGS="-O2 -pipe \
-Wall -Wformat -Wformat-security -Werror=format-security \
-fstack-protector-strong \
-fstack-clash-protection \
-fcf-protection \
-D_FORTIFY_SOURCE=3 \
-D_GLIBCXX_ASSERTIONS \
-fPIE"
CXXFLAGS="${CFLAGS} -D_GLIBCXX_ASSERTIONS"
LDFLAGS="-pie \
-Wl,-z,relro \
-Wl,-z,now \
-Wl,-z,noexecstack \
-Wl,-z,nodlopen \
-Wl,-z,nodump \
-Wl,--as-needed"
gcc ${CFLAGS} ${LDFLAGS} -o prog main.cClang (Linux)
CFLAGS="-O2 \
-fstack-protector-strong \
-fstack-clash-protection \
-D_FORTIFY_SOURCE=3 \
-fPIE \
-fsanitize=safe-stack"
LDFLAGS="-pie \
-Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack \
-fsanitize=safe-stack"
clang ${CFLAGS} ${LDFLAGS} -o prog main.cShared Libraries
# Shared library hardening (note: -fPIC not -fPIE)
CFLAGS="-O2 -fPIC -fstack-protector-strong -D_FORTIFY_SOURCE=2"
LDFLAGS="-shared -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack"
gcc ${CFLAGS} ${LDFLAGS} -o libfoo.so foo.cFlag Reference Table
| Flag | Compiler | Linker | Effect |
|---|---|---|---|
-fstack-protector-strong | GCC/Clang | — | Stack canary on at-risk functions |
-fstack-protector-all | GCC/Clang | — | Stack canary on ALL functions (slow) |
-fstack-clash-protection | GCC ≥8/Clang ≥11 | — | Prevents stack-heap collision |
-fcf-protection | GCC ≥8/Clang | — | Intel CET: IBT + shadow stack |
-D_FORTIFY_SOURCE=2 | GCC/Clang | — | Checked libc wrappers (level 2) |
-D_FORTIFY_SOURCE=3 | GCC ≥12/Clang ≥12 | — | More thorough FORTIFY |
-fPIE | GCC/Clang | — | Compile as PIE (requires -pie to link) |
-pie | — | GCC/Clang | Link as position-independent executable |
-Wl,-z,relro | — | GCC/Clang (ld) | Mark GOT read-only after relocation |
-Wl,-z,now | — | GCC/Clang (ld) | Eager binding → Full RELRO |
-Wl,-z,noexecstack | — | GCC/Clang (ld) | Non-executable stack (NX) |
-Wl,-z,separate-code | — | GCC/Clang (ld) | Separate code/data PT_LOAD segments |
-fsanitize=cfi | Clang + LTO | — | Control flow integrity (needs -flto) |
-fsanitize=safe-stack | Clang | — | SafeStack (separate unsafe stack) |
-Wformat -Wformat-security | GCC/Clang | — | Warn on format string issues |
-Werror=format-security | GCC/Clang | — | Error on dangerous format strings |
Distribution Defaults
| Distro | Default hardening |
|---|---|
| Debian/Ubuntu | PIE, RELRO, canary, FORTIFY=2 |
| Fedora/RHEL | PIE, Full RELRO, canary, FORTIFY=3 (Fedora 38+) |
| Alpine | PIE, RELRO, canary (musl-based) |
| Arch Linux | PIE, RELRO, canary, FORTIFY=2 |
Check what your distro uses:
dpkg-buildflags --query # Debian/Ubuntu
rpm --eval "%{build_cflags}" # Fedora/RHEL