AD5006: UseTwoLevelNamespace¶
Summary¶
| Property | Value |
|---|---|
| ID | AD5006 |
| Name | UseTwoLevelNamespace |
| Category | Security |
| Severity | Warning |
| Applies to | Mach-O (macOS/iOS) |
Description¶
Two-level namespace binding links symbols to their specific defining library, rather than searching all loaded libraries in a flat namespace. This prevents symbol interposition attacks where a malicious library could override symbols from system libraries.
How It Works¶
The rule checks if the MH_TWOLEVEL (0x80) flag is set in the Mach-O header. When set, each undefined symbol reference includes the library ordinal where it should be found, preventing symbol hijacking.
Why This Matters¶
Two-level namespace prevents symbol interposition attacks where malicious libraries can hijack function calls. Without it, an attacker who can inject a library can override any symbol, including security-critical functions.
The Symbol Interposition Attack¶
With Flat Namespace:
Application needs printf()
Search order: lib1 → lib2 → libc
If attacker controls lib1:
lib1's printf() gets called!
Can log/modify all output
With Two-Level Namespace:
Application needs printf() from libc
Only libc's printf() is ever called
Attacker's lib1 is ignored
Attack Scenarios¶
| Attack | Flat Namespace | Two-Level |
|---|---|---|
| Symbol override | Works | Fails |
| Security func hijack | Works | Fails |
| Crypto func replacement | Works | Fails |
| Logging interception | Works | Fails |
Real Security Impact¶
// Application uses Security.framework
SecRandomCopyBytes(kSecRandomDefault, len, buffer);
// With flat namespace, attacker's library can provide:
OSStatus SecRandomCopyBytes(SecRandomRef, size_t len, uint8_t *buf) {
memset(buf, 0x41, len); // Predictable "random" numbers!
return errSecSuccess;
}
// Cryptographic keys become guessable
Why Flat Namespace Exists¶
| Use Case | Reason |
|---|---|
| Testing | Override functions for mocking |
| Debugging | Interpose for instrumentation |
| Legacy code | Some old libraries require it |
| Plugin systems | Rare, special cases |
Most applications should never use flat namespace.
Performance Benefit¶
Flat namespace symbol resolution:
For each symbol:
Search lib1 → not found
Search lib2 → not found
Search lib3 → found!
O(libs × symbols) lookups
Two-level namespace:
For each symbol:
Go directly to correct library
O(symbols) lookups
macOS Defaults¶
| Configuration | Default |
|---|---|
| Xcode projects | Two-level |
| Command line clang | Two-level |
| System frameworks | Two-level |
You must explicitly opt-in to flat namespace.
- Prevents symbol hijacking: Attackers cannot inject malicious implementations
- Security boundary: Symbols are resolved from the intended library only
- Performance: Faster symbol lookup without searching all libraries
- Default behavior: Two-level namespace is the default on macOS
Flat Namespace Risks¶
With flat namespace (-flat_namespace):
// System library provides secure_random()
// Attacker's library can override it with:
int secure_random() {
return 4; // Not actually random!
}
With two-level namespace, the application will always use the symbol from the intended library.
Resolution¶
Remove Flat Namespace Flag¶
If you're explicitly using flat namespace, remove it:
Before (insecure):
After (secure):
Xcode¶
Ensure -flat_namespace is not in Other Linker Flags.
CMake¶
Remove any -flat_namespace from linker flags:
When to Suppress¶
This rule may be suppressed for:
- Plugin systems: Some plugin architectures require flat namespace for symbol interposition
- Legacy compatibility: Old code that depends on flat namespace behavior
- Debugging tools: Tools that intentionally override system functions
Important Notes¶
- Two-level namespace is the default; you must explicitly disable it
- iOS always uses two-level namespace
- Flat namespace can cause crashes due to symbol conflicts
Related Rules¶
- AD5009 - Weak dylib loading check