Attacker Value
(1 user assessed)
(1 user assessed)
User Interaction
Privileges Required
Attack Vector


Exploited in the Wild
Add MITRE ATT&CK tactics and techniques that apply to this CVE.
Initial Access


An authentication bypass vulnerability in the User Portal and Webadmin allows a remote attacker to execute code in Sophos Firewall version v18.5 MR3 and older.

Add Assessment

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 @@
+        	<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>

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))) || 
        Integer.parseInt(dataGridId))) {
      jsonObject = new JSONObject(httpRequest.getParameter("json"));
      if (!isvalidJSONKeys(jsonObject)) {
        redirectToLogin(httpRequest, httpResponse);
  } 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);
      if (!isAsciiPrintable(key)) {"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 '\{"🦞":"test"\}'
*   Trying
* Connected to ( 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;
*  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;
*  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:
> 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
* 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 '\{"🦞":"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 = 🦞


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.

General Information


  • Sophos


  • Sophos Firewall

Additional Info

Technical Analysis