
OPNsense 26.7.6 - High - Remote - Config line injection - tunables loader
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · opnsense-tunables-loader
Class: Config line injection Reach: Remote
OPNsense core 26.7.6 (a842fe48) - opnsense.org
System: Advanced: Tunables still interpolates name/value into /boot/loader.conf. Leftover sibling of GHSA-fq94. Tunables.xml tunable / value are bare TextField (newlines on). system_login_configure() writes {$param}="{$value}". Tunables reconfigure also restarts login. Live sysctl(8) goes through shell_safe plus an OID filter, so that hop is not shell injection. The leftover is extra loader directives on the next boot. Privilege is page-system-advanced-sysctl, not page-all.
This GitHub pack stays private until coordinated disclosure.
| ID | no CVE yet |
| Class | Config line injection |
| Reach | Remote (authenticated WebGUI / API) |
| CWE | CWE-93 |
| CVSS | High: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| Product | OPNsense core |
| Affected | 26.7.6 (a842fe48c21288d11a422cb944678e8eb308becf) System: Advanced: Tunables |
| Auth | WebGUI session or API key with page-system-advanced-sysctl |
| License | GNU Affero GPL v3.0 |
| Lab | compose exec gadget (hunt slot 18850) |
I pinned tag 26.7.6 peel a842fe48. The lab is a PHP gadget of the pinned system_login_configure() interpolation. Compose project opnsense-tunables-loader. Hunt slot 18850 in the 18800-18860 gadget family; this compose file uses docker compose exec php, not a published HTTP bind.
| Method | Sink | Privilege |
|---|---|---|
Tunables value CR/LF (hw.usb.no_boot_wait plus "\n# witness) | pin system_login_configure() extra line in /boot/loader.conf | Integrity (lab-proven): extra # OPNSENSE-TUNABLES-LOADER-WITNESS on its own line after a closed assignment. Lab: newline-extra-line=yes. |
Clean value 1 | same writer | Negative (lab-proven): single assignment line, no witness. Lab: clean-value-single-line=yes. |
| Same extra loader lines on next boot | loader init_path / module_path / *_load / vfs.root.mountfrom / console | Integrity (inferred): next-boot loader honors attacker keys. Not rebooted in this gadget. |
Garbage loader lines / wrong vfs.root.mountfrom | /boot/loader.conf | Availability (inferred): boot fail or wrong root. |
POST /api/core/tunables/searchItem | tunables API | Recon (inferred): list tunables, including current/default/type. Intended for this ACL. |
Live sysctl -w of a valid OID | set_sysctl + OID filter | Intended page job. Extra loader lines are the leftover. |
Lab-proven vs inferred: newline extra loader line and clean-value single-line are lab SUCCESS. Next-boot loader apply is inferred. Gadget, not a full OPNsense FreeBSD VM.
Requires: signed-in user or that user's Local API key with page-system-advanced-sysctl and without page-all. POST /api/core/tunables/*. CSRF on session POST. Reconfigure restarts login; rc.reload_all and boot also call system_login_configure.
| Surface | Lab |
|---|---|
| Live sysctl shell injection | shell_safe plus OID filter |
| Clean value | single assignment line |
| Unauth | 401 |
User without page-system-advanced-sysctl | 403 |
user-config-readonly | save() throws |
page-all tunables admin setting a real OID | intended |
| GHSA-fq94 patched GUI text fields | different sinks; this field was not named |
No unauth. No live sysctl shell. No Diagnostics: Command.
cd lab
./run.sh
Compose project opnsense-tunables-loader. Witness OPNSENSE-TUNABLES-LOADER-WITNESS. Last line:
SUCCESS opnsense-tunables-loader newline-extra-line=yes clean-value-single-line=yes OPNSENSE-TUNABLES-LOADER-WITNESS
Root launcher: python3 opnsense-tunables-loader-Abraxas-Labs.py.
Strip CR/LF and " from tunables name and value, same class as GHSA-fq94. Type the name as an OID mask, not a bare TextField. Quote with a helper that cannot close the assignment. Do not write a tunable into /boot/loader.conf unless it is a known loader key.
a842fe48c21288d11a422cb944678e8eb308becfsystem.inc system_login_configure loader.conf interpolationTunables.xml bare TextField name/value