Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
opnsense-captive-lastseen-roam — OPNsense 26.7.6 - High - Remote - Network-access bypass - captive lastseen roam | Kitploit
Tools/GitHubGitHub/abraxas/opnsense-captive-lastseen-roam
Authentication & AuthorizationVulnerability AnalysisExploitationWeb Application ExploitationNetwork SecurityPenetration TestingLabs & Practice
GitHub

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
abraxas/opnsense-captive-lastseen-roam

opnsense-captive-lastseen-roam

OPNsense 26.7.6 - High - Remote - Network-access bypass - captive lastseen roam

View Repository
2 days agoNot yet reviewed

Abraxas Labs - opnsense-captive-lastseen-roam

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  opnsense-captive-lastseen-roam

opnsense-captive-lastseen-roam

Class: Network-access bypass Reach: Remote

OPNsense core 26.7.6 (a842fe48) - opnsense.org

Captive-portal roaming still inherits a session for the newest hostwatch IPv4 on a MAC, plus every IPv6. GHSA-864r (26.7.3) stopped "add every IPv4." The leftover IPv4 is list_hosts.py ORDER BY last_seen DESC. Primary IP is then assigned from unfiltered current_ips[0]. Gratuitous ARP attacker_IP is-at victim_MAC updates last_seen. After 26.7.3 that IPv4 is not one of many; it is the IPv4 the filter keeps if it is newer than the victim's own ARP. IPv6 NDP was never capped. Roaming and hostwatch default on.

This GitHub pack stays private until coordinated disclosure.

IDno CVE yet
ClassNetwork-access bypass
ReachRemote (L2-adjacent, captive-portal zone)
CWECWE-345 / CWE-863
CVSSHigh: 7.4 CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N
ProductOPNsense core
Affected26.7.6 (a842fe48c21288d11a422cb944678e8eb308becf) captive portal roaming
AuthUnauth L2-adjacent host on a captive-portal zone; roaming on
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

I pinned tag 26.7.6 peel a842fe48. The lab is a Python gadget of the pinned _filter_addresses / list_hosts.py / arp.py lines, loopback wait on 127.0.0.1:18820.

What an attacker can do

MethodSinkPrivilege
Newest hostwatch IPv4 for a MAC with roaming=1pin _filter_addresses / list_hosts.py ORDER BY last_seen DESC; primary from unfiltered current_ips[0]Integrity (lab-proven): attacker 192.0.2.99 kept, victim 192.0.2.10 dropped, primary=192.0.2.99. Lab: attacker-ipv4-kept=192.0.2.99 victim-ipv4-dropped=yes roaming=1.
Same MAC, roaming=0_filter_addresses returns [addresses[0]]Negative (lab-proven): first IPv4 192.0.2.10 kept.
IPv6 on the same MAC, roaming=1_filter_addresses keeps every non-v4Integrity (lab-proven): all IPv6 kept (2001:db8::10 and 2001:db8::99).
Forged GARP / NDP that hostwatch stores under the victim MAClive PF.add_to_table / IPFW.add_accounting on __captiveportal_zone_NIntegrity (inferred): attacker source IP matches the portal pass table. Session username and quota stay the victim's. Live GARP on the wire and live zone-table pass are not run in this gadget.
Same table addipfw accounting rule number per sessionIntegrity (inferred): attacker bytes count on the victim session.
Victim IPv4 no longer first_remove_client / pf delete of addresses not in list_session_ipsAvailability (inferred): victim IPv4 can drop off the zone table while the attacker IPv4 stays.

Lab-proven vs inferred: last_seen IPv4 keep/drop, roaming=0 first-IP, and IPv6-all are lab SUCCESS. Forged GARP on the wire, live pf/ipfw pass, and accounting smear are inferred. Gadget, not a full OPNsense FreeBSD VM.

Requires: L2-adjacent unauth host on a captive-portal zone. Roaming on (default). Hostwatch on (default). One already-authorized session on that zone.

Adjacent surface that stays closed

SurfaceLab
GHSA-864r add-every-IPv4closed on 26.7.3; this leftover is the last_seen IPv4 plus all IPv6
roaming=0first IPv4 kept; attacker IPv4 not selected
XFF / X-Real-Ip spoof of a different logon IPzone proxy appends; not a clean pf CIDR
Unauth logon of the caller IPdocumented portal login of getClientIp()
Voucher brute on default 8+10 CSPRNGnot this leftover
Template zip-sliprealpath plus admin upload
Portal splash XSS to WebGUI origindifferent port / chroot
WAN-unauthL2-adjacent on the zone
WebGUI / root / voucher mintstays closed

No unauth WebGUI. No root. No WAN skip of the portal.

Lab

cd lab
./run.sh

Loopback wait 127.0.0.1:18820. Compose project opnsense-captive-lastseen-roam. Image tag from that compose. Witness OPNSENSE-CAPTIVE-LASTSEEN-ROAM-WITNESS. Last line:

SUCCESS opnsense-captive-lastseen-roam attacker-ipv4-kept=192.0.2.99 victim-ipv4-dropped=yes roaming=1 OPNSENSE-CAPTIVE-LASTSEEN-ROAM-WITNESS

Root launcher: python3 opnsense-captive-lastseen-roam-Abraxas-Labs.py.

The fix

Pick the IPv4 the session already authorized, not the newest hostwatch row for that MAC. Cap IPv6 the same way, or require NDP that the kernel still holds. Do not assign primary from unfiltered current_ips[0] when the filter already chose a different address. Treat a GARP that only updates last_seen as untrusted observation, which is the GHSA-864r invariant.

References

  • github.com/opnsense/core a842fe48c21288d11a422cb944678e8eb308becf
  • cp-background-process.py _filter_addresses and primary-IP rewrite
  • list_hosts.py ORDER BY last_seen DESC
  • lib/arp.py get_all_addresses_by_mac
  • Sibling (add-all-IPv4, not this leftover): GHSA-864r
  • CWE-345 / CWE-863
Download Tool