
OPNsense 26.7.6 - Élevé - À distance - Contournement d'accès réseau - captive lastseen roam
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · opnsense-captive-lastseen-roam
Classe : Contournement de contrôle d'accès réseau Portée : Distant
OPNsense core 26.7.6 (a842fe48) - opnsense.org
L'itinérance du portail captif hérite encore d'une session pour l'IPv4 hostwatch la plus récente sur une MAC, plus toutes les IPv6. GHSA-864r (26.7.3) a mis fin à « ajouter chaque IPv4 ». L'IPv4 résiduelle provient de list_hosts.py ORDER BY last_seen DESC. L'IP primaire est ensuite attribuée à partir de current_ips[0] non filtré. Un ARP gratuit attacker_IP is-at victim_MAC met à jour last_seen. Après 26.7.3, cette IPv4 n'est plus l'une parmi tant d'autres ; c'est l'IPv4 que le filtre conserve si elle est plus récente que l'ARP de la victime elle-même. Le NDP IPv6 n'a jamais été plafonné. Itinérance et hostwatch activés par défaut.
Ce pack GitHub reste privé jusqu'à la divulgation coordonnée.
| ID | pas encore de CVE |
| Classe | Contournement de contrôle d'accès réseau |
| Portée | Distant (adjacent L2, zone de portail captif) |
| CWE | CWE-345 / CWE-863 |
| CVSS | Élevé : 7.4 CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N |
| Produit | OPNsense core |
| Affecté | 26.7.6 (a842fe48c21288d11a422cb944678e8eb308becf) itinérance du portail captif |
| Auth | Hôte non authentifié adjacent L2 sur une zone de portail captif ; itinérance activée |
| Licence | GNU Affero GPL v3.0 |
| Labo | 127.0.0.1 uniquement |
J'ai épinglé le tag 26.7.6 peel a842fe48. Le labo est un gadget Python des lignes épinglées _filter_addresses / list_hosts.py / arp.py, attente loopback sur 127.0.0.1:18820.
| Méthode | Cible | Privilège |
|---|---|---|
| IPv4 hostwatch la plus récente pour une MAC avec roaming=1 | épingler _filter_addresses / list_hosts.py ORDER BY last_seen DESC ; primaire depuis current_ips[0] non filtré | Intégrité (prouvé en labo) : attaquant 192.0.2.99 conservé, victime 192.0.2.10 écartée, primaire=192.0.2.99. Labo : attacker-ipv4-kept=192.0.2.99 victim-ipv4-dropped=yes roaming=1. |
| Même MAC, roaming=0 | _filter_addresses renvoie [addresses[0]] | Négatif (prouvé en labo) : première IPv4 192.0.2.10 conservée. |
| IPv6 sur la même MAC, roaming=1 | _filter_addresses conserve tout ce qui n'est pas v4 | Intégrité (prouvé en labo) : toutes les IPv6 conservées (2001:db8::10 et 2001:db8::99). |
| GARP / NDP forgé que hostwatch stocke sous la MAC de la victime | PF.add_to_table / IPFW.add_accounting en direct sur __captiveportal_zone_N | Intégrité (déduit) : l'IP source de l'attaquant correspond à la table de passage du portail. Le nom d'utilisateur de session et le quota restent ceux de la victime. Le GARP en direct sur le réseau et le passage en direct dans la table de zone ne sont pas exécutés dans ce gadget. |
| Même ajout de table | numéro de règle de comptabilité ipfw par session | Intégrité (déduit) : les octets de l'attaquant sont comptés sur la session de la victime. |
| IPv4 de la victime plus en première position | _remove_client / suppression pf des adresses absentes de list_session_ips | Disponibilité (déduit) : l'IPv4 de la victime peut disparaître de la table de zone tandis que l'IPv4 de l'attaquant reste. |
Prouvé en labo vs déduit : conservation/suppression de l'IPv4 last_seen, première IP avec roaming=0, et toutes les IPv6 sont des SUCCÈS en labo. Le GARP forgé sur le réseau, le passage pf/ipfw en direct et la contamination de la comptabilité sont déduits. Gadget, pas une VM FreeBSD OPNsense complète.
Nécessite : hôte non authentifié adjacent L2 sur une zone de portail captif. Itinérance activée (par défaut). Hostwatch activé (par défaut). Une session déjà autorisée sur cette zone.
| Surface | Labo |
|---|---|
| GHSA-864r ajout-de-chaque-IPv4 | fermé en 26.7.3 ; ce résidu est l'IPv4 last_seen plus toutes les IPv6 |
| roaming=0 | première IPv4 conservée ; IPv4 de l'attaquant non sélectionnée |
Usurpation XFF / X-Real-Ip d'une IP de connexion différente | le proxy de zone ajoute ; pas un CIDR pf propre |
logon non authentifié de l'IP de l'appelant | connexion au portail documentée de getClientIp() |
| Force brute de voucher sur CSPRNG par défaut 8+10 | pas ce résidu |
| Zip-slip de template | realpath plus upload admin |
| XSS du splash du portail vers l'origine WebGUI | port / chroot différent |
| WAN non authentifié | adjacent L2 sur la zone |
| WebGUI / root / émission de voucher | reste fermé |
Pas de WebGUI non authentifié. Pas de root. Pas de contournement du portail depuis le WAN.
cd lab
./run.sh
Attente loopback 127.0.0.1:18820. Projet Compose opnsense-captive-lastseen-roam. Tag d'image issu de ce compose. Témoin OPNSENSE-CAPTIVE-LASTSEEN-ROAM-WITNESS. Dernière ligne :
SUCCESS opnsense-captive-lastseen-roam attacker-ipv4-kept=192.0.2.99 victim-ipv4-dropped=yes roaming=1 OPNSENSE-CAPTIVE-LASTSEEN-ROAM-WITNESS
Lanceur root : python3 opnsense-captive-lastseen-roam-Abraxas-Labs.py.
Choisir l'IPv4 que la session a déjà autorisée, pas la ligne hostwatch la plus récente pour cette MAC. Plafonner l'IPv6 de la même manière, ou exiger un NDP que le noyau détient encore. Ne pas attribuer la primaire depuis current_ips[0] non filtré lorsque le filtre a déjà choisi une adresse différente. Traiter un GARP qui ne met à jour que last_seen comme une observation non fiable, ce qui est l'invariant de GHSA-864r.
a842fe48c21288d11a422cb944678e8eb308becfcp-background-process.py _filter_addresses et réécriture de l'IP primairelist_hosts.py ORDER BY last_seen DESClib/arp.py get_all_addresses_by_mac