Skip to content

AD3024: RestrictDlopen

Summary

Property Value
ID AD3024
Name RestrictDlopen
Category Security
Severity Note
Applies to ELF (Linux/Unix)

Description

The -Wl,-z,nodlopen linker option marks shared objects as not available to dlopen(3) calls. This can help reduce an attacker's ability to load and manipulate shared objects as part of an attack chain.

How It Works

The rule checks for the DF_1_NOOPEN flag (0x40) in the DT_FLAGS_1 dynamic section entry:

  • Flag present: Shared object cannot be loaded via dlopen()
  • Flag absent: Shared object can be dynamically loaded

Why This Matters

Dynamic library loading via dlopen() is a powerful capability that attackers can abuse. Restricting dlopen access reduces the attack surface by preventing exploitation techniques that rely on dynamically loading and executing code from shared objects.

Attack Surface from dlopen

Attackers can abuse dlopen in various ways:

1. Code Injection:
   - Attacker creates malicious .so file
   - Tricks program into dlopen()ing it
   - Attacker's code runs in process context

2. Library Confusion:
   - Attacker controls library search path
   - dlopen() loads attacker's library instead
   - Code substitution attack

3. ROP to dlopen:
   - After gaining code execution
   - ROP chain calls dlopen("malicious.so")
   - Full attacker code loads into process

Protection Mechanisms

Flag Effect
DF_1_NOOPEN Library cannot be dlopen'd
DF_1_NODUMP Library cannot be dumped (ptrace)
DF_1_NODEFLIB Don't search default library paths

When NOOPEN Helps

Scenario Benefit
Sensitive libraries Can't be loaded into attacker-controlled process
Internal libraries Only available through normal linking
Exploit chains Breaks ROP→dlopen patterns
Sandboxed code Limits loading capabilities

NOOPEN Limitations

NOOPEN prevents:
  ✓ dlopen("libsensitive.so", RTLD_LAZY)
  ✓ Dynamic loading attacks

 NOOPEN does NOT prevent:
  ✗ Normal linking (ld.so loads at startup)
  ✗ Direct mmap of library file
  ✗ Other exploitation techniques

It's one layer of defense, not a complete solution.

OpenSSF Recommendation

The OpenSSF Compiler Hardening Guide recommends nodlopen for:

  • Libraries containing sensitive operations
  • Libraries that should only be statically linked
  • Defense-in-depth for all libraries

Compatibility Considerations

Use Case NOOPEN Compatible
Plugin systems No (plugins need dlopen)
Normal libraries Yes
System libraries Usually no
Application-specific Yes
  • Attack surface reduction: Prevents attackers from loading the library dynamically
  • Defense in depth: Makes exploitation chains more difficult
  • Library isolation: Ensures libraries are only loaded through normal linking
  • OpenSSF recommended: Part of the OpenSSF Compiler Hardening Guide

Resolution

GCC/Clang

Add the nodlopen flag when linking shared libraries:

gcc -shared -Wl,-z,nodlopen -o libexample.so source.o

CMake

# For shared libraries
add_library(mylib SHARED source.c)
target_link_options(mylib PRIVATE -Wl,-z,nodlopen)

Makefile

LDFLAGS += -Wl,-z,nodlopen

libexample.so: source.o
    $(CC) -shared $(LDFLAGS) -o $@ $^

When to Suppress

This rule may be suppressed for:

  • Plugin libraries: Libraries designed to be loaded via dlopen()
  • Loadable modules: Intentionally dynamic components
  • Optional dependencies: Libraries that may or may not be loaded at runtime
  • Optimized library loading: Libraries using delayed loading patterns

Caveats

  • This flag only affects dlopen() calls, not normal dynamic linking
  • The flag is enforced by glibc, not the kernel
  • Attackers with code execution could bypass this check
  • Not all build systems support this flag by default

References