
OPNsense 26.7.6 - High - Remote - Privilege leftover - custom firmware mirror sed-skips pkg fingerprints
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · opnsense-firmware-mirror-sed
Class: Signature skip Reach: Remote
OPNsense core 26.7.6 (a842fe48) - opnsense.org
System: Firmware set POSTs a custom mirror / flavour. Core FILTER_SANITIZE_URL keeps \|#;". opnsense-update -m / -n interpolates that string into sed on OPNsense.conf and can flip signature_type from "fingerprints" to "none". Then pkg install as root from the attacker URL. The settings page offers a custom mirror of still-signed OPNsense packages. It has no "disable signatures" control. Privilege is page-system-firmware-manualupdate, not page-all.
Sed lives in out-of-pin opnsense-update. Core on this pin is the unsanitized field plus the -m pass. The lab re-runs that s||| program text with GNU sed.
This GitHub pack stays private until coordinated disclosure.
| ID | no CVE yet |
| Class | Signature skip |
| Reach | Remote (authenticated WebGUI / API) |
| CWE | CWE-74 / CWE-347 |
| CVSS | High: 7.2 CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H |
| Product | OPNsense core |
| Affected | 26.7.6 (a842fe48c21288d11a422cb944678e8eb308becf) Firmware set custom mirror |
| Auth | WebGUI session or API key with page-system-firmware-manualupdate |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
I pinned tag 26.7.6 peel a842fe48. The lab is a PHP gadget of FILTER_SANITIZE_URL plus the opnsense-update mod_mirrorurl sed, loopback HTTP on 127.0.0.1:18860. PR:H because the firmware page is already allowed to install signed packages. Score 8.8 if that page is treated as PR:L.
| Method | Sink | Privilege |
|---|---|---|
FILTER_SANITIZE_URL on a custom mirror with | then opnsense-update-style sed | rewrite of OPNsense.conf | Integrity (lab-proven): signature_type "none". Control mirror stays "fingerprints". Lab: sanitize-keeps-pipe=yes signature_type=none fingerprints-control=yes. |
| Same set, plugins field | setAction discards plugins | Negative (lab-proven): plugins discarded before save. |
Then POST /api/core/firmware/install/<pkg> | pkg install -y as root from the attacker URL | Integrity (inferred): unsigned package as root. Firmware page already installs signed packages; this leftover is the verify skip. No unsigned payload in the lab. |
Then POST /api/core/firmware/update or /upgrade | opnsense-update -pt / -u as root | Integrity (inferred): base/kernel sets with verify skipped (DO_INSECURE). |
GET /api/core/firmware/info, /running, /upgradestatus | firmware API | Recon (inferred): package lists, update log, mirror options. Intended for this ACL. |
POST /api/core/firmware/reboot, /poweroff, /remove/<pkg> | configd firmware actions | Availability (inferred): reboot/halt/remove. Intended for this ACL, not the leftover. |
Lab-proven vs inferred: sanitize keeps | and signature_type none are lab SUCCESS. Live pkg install of an unsigned package is inferred. Gadget, not a full OPNsense FreeBSD VM. No unsigned package payload.
Requires: signed-in user or that user's Local API key with page-system-firmware-manualupdate and without page-all. CSRF on session POST. Not unauth. Not dashboard-only.
| Surface | Lab |
|---|---|
| Unauth firmware | 401 |
| Dashboard-only | 403 |
Control mirror https://pkg.opnsense.org | signature_type stays "fingerprints" |
| Plugin-name traversal | filter_pkgname |
| Changelog fetch | still opnsense-verify |
user-config-readonly on save() | throwReadOnly() |
| Stock HTTP mirrors without a verify skip | transport MITM; fingerprints still required |
| XMLRPC firmware | version strings only |
No unauth. No dashboard-only. Changelog still signed.
cd lab
./run.sh
Loopback 127.0.0.1:18860. Compose project opnsense-firmware-mirror-sed. Witness OPNSENSE-FIRMWARE-MIRROR-SED-WITNESS. Last line:
SUCCESS opnsense-firmware-mirror-sed sanitize-keeps-pipe=yes signature_type=none fingerprints-control=yes OPNSENSE-FIRMWARE-MIRROR-SED-WITNESS
Root launcher: python3 opnsense-firmware-mirror-sed-Abraxas-Labs.py.
Attack mirror shape used in the lab (witness only, no unsigned pkg):
https://example.invalid/|;/signature_type/s/"fingerprints"/"none"/;#
Reject a custom mirror that is not a URL. Strip sed metacharacters (|, ;, #, ") before opnsense-update -m / -n, or pass the URL as a file so sed never sees it as program text. Keep signature_type: "fingerprints" unless a separate, audited control turns verification off. Do not let Firmware set rewrite fingerprints to "none".
a842fe48c21288d11a422cb944678e8eb308becfFirmwareController.php setAction FILTER_SANITIZE_URLscripts/firmware/repos/OPNsense.php opnsense-update -sd -mOPNsense.conf.shadow.in stock signature_type: "fingerprints"opnsense-update mod_mirrorurl (s\|\| on the url: line)