CVE-2022-3236 _ Sophos Firewall Code Injection
💉

CVE-2022-3236 _ Sophos Firewall Code Injection

📅 [ Archival Date ]
Oct 21, 2022 5:21 PM
🏷️ [ Tags ]
SophosJSONCVE-2022-3236
✍️ [ Author ]

Trend Micro Research Team

In this excerpt of a Trend Micro Vulnerability Research Service vulnerability report, Guy Lederfein and Dusan Stevanovic of the Trend Micro Research Team detail a recently patched code injection vulnerability in the Sophos Firewall. The bug is due to improper validation of JSON keys submitted in the “JSON” parameter sent to the Controller endpoint. Successful exploitation of this vulnerability could result in remote code execution with the privileges of the root user. The following is a portion of their write-up covering CVE-2022-3236, with a few minimal modifications.

Sophos recently patched a code injection vulnerability in Sophos Firewall v19.0 MR1 (19.0.1) and previous. This vulnerability is due to improper validation of JSON keys submitted in the “json” parameter sent to the Controller endpoint. A remote, unauthenticated attacker could exploit this vulnerability by sending a specially crafted request to an affected server. Successfully exploiting this vulnerability could result in remote code execution with the privileges of the root user.

The Vulnerability

Sophos Firewall is a network security solution, which can be deployed on purpose-built devices, on a cloud network (AWS/Azure), on a virtual device, or as a software appliance on x86 Intel hardware. The firewall application supports multiple network security features, including application-aware routing, TLS inspection, deep packet inspection, remote access VPN, logging, and reporting. Sophos Firewall exposes a web admin console for managing the device’s configuration via HTTPS on TCP port 4444. In addition, Sophos Firewall exposes a user portal for updating a user’s details or downloading authentication clients via HTTPS on TCP port 443.

HTTP is a request/response protocol described in RFCs 7230 - 7237 and other RFCs. A request is sent by a client to a server, which in turn sends a response back to the client. An HTTP request consists of a request line, various headers, an empty line, and an optional message body:

Request = Request-Line headers CRLF [message-body]
Request-Line = Method SP Request-URI SP HTTP-Version CRLF
Headers = *[Header]
Header = Field-Name “:” Field-Value CRLF

where CRLF represents the new line sequence Carriage Return (CR) followed by Line Feed (LF) and SP represents a space character. Parameters can be passed from the client to the server as name-value pairs in either the Request-URI or in the message-body, depending on the Method used and the Content-Type header. For example, a simple HTTP request passing a parameter named “param” with value “1”, using the GET method might look like this:

GET /my_webapp/mypaget.htm?param=1 HTTP/1.1
Host: www.myhost.com

A corresponding HTTP request using the POST method might look like this:

POST /my_webapp/mypage.htm HTTP/1.1
Host: www.myhost.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 7

param=1

JavaScript Object Notation (JSON) is a data-interchange format used for creating machine parseable, human-readable output. A JSON object has the following syntax:

  • An object is enclosed in curly braces {}.
  • An object is comprised of zero or more items delimited by a comma (“,”) character.
  • An item is comprised of a key and a value. A key is delimited from its value by a colon (“:”) character.
  • A key must be a string enclosed in quotes.
  • A value must be a valid type. JSON defines seven value types: string, number, object, array, true, false, and null.
  • An array is an object enclosed in square braces [].
  • An array is comprised of zero or more strings, numbers, JSON objects, arrays, boolean, or null type-objects delimited by a comma (“,”) character.

An example JSON object is as follows:

{“name”:”bob”, “age”:30}

The web admin and user portal web interfaces exposed by Sophos Firewall both run on a Jetty server behind an Apache httpd server. Configuration and diagnostic requests issued through these interfaces are submitted via requests to the Controller servlet. This servlet retrieves the appropriate EventBean object based on the mode HTTP request parameter. For example, if the mode HTTP request parameter is set to 151 or 451, the WebAdminAuth or UserPortalAuth classes are called to process the request, respectively. In each of these classes, the generateAndSendAjaxEvent() method of the CSCClient class is called. This method parses the json HTTP request parameter and modifies specific fields from the parsed object. The send() method is then called, which in turn calls _send(), which adds the mode JSON parameter according to the mode of the associated EventBean object. It then sends the created JSON object as a string, including appropriate headers, to the local CSC server over TCP or UDP port 299.

When the CSC server receives the JSON object submitted by the Jetty server, it validates the JSON object submitted via the validateJSON() method in the Perl script CyberAPIArch.pm. If the JSON object contains a key named _discriminator, the getValidationHash() method is called. This method iterates over each key of the _discriminator Perl hash, representing field names. Each field name is associated with a JSON object describing the mapping between field values and object names. Some Perl objects resolved when parsing the JSON object received (natrule, securitypolicy, hotspot, etc.) contain a template with a _discriminator key containing such a mapping for specific field names between field values and their associated objects. The method iterates over the field values to check whether the submitted JSON object contains a field with the referenced name and value. If so, it retrieves the associated object name and attempts to resolve it to an object using the eval function.

A code injection vulnerability has been reported for Sophos Firewall. This vulnerability is due to improper validation of JSON keys submitted in the “json” parameter sent to the Controller endpoint. Specifically, the _discriminator key can be set in the HTTP request. This JSON object is sent, after some modification, to the CSC server. However, the _discriminator key set is sent unmodified. Once the validateJSON() method of the Perl script CyberAPIArch.pm is called, it will detect this key and call getValidationHash(). The Perl method iterates over the $hashObj hash object, which contains a key named _discriminator whose value is a nested hash with a value key set with the value set for the _discriminator key in the original request sent. This value key is treated as a nested hash containing field values mapped to object names. Therefore, if the originally submitted JSON object contains a key named value with its value set to one of the values in this nested hash, the associated object name will be resolved using the eval function. Since the _discriminator key in the original request can be set to a JSON object with an arbitrary mapping between values and object names, a malicious object can be submitted to be concatenated into the eval function, resulting in code injection.

Sophos attempts to perform input validation on request parameters, including the JSON object, using the RequestCheckFilter Java object. This filter detects request parameters and JSON keys with non-ASCII printable characters. However, arbitrary request parameters and JSON keys containing ASCII printable characters can still be submitted, including the _discriminator key.

A remote, unauthenticated attacker could exploit this vulnerability by sending a crafted request to the target server. Successfully exploiting this vulnerability could result in arbitrary Perl commands being run on the server, resulting in remote code execution with the privileges of the root user.

Source Code Walkthrough

The following code snippet was taken from Sophos Firewall version 19.0.1 with additional comments added by Trend Micro.

From the decompiled Java class cyberoam.sessionmanagement.RequestCheckFilter:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
  HttpServletRequest httpRequest = (HttpServletRequest)request;
  HttpServletResponse httpResponse = (HttpServletResponse)response;
  CyberoamLogger.info(“RequestCheckFilter”, “URI: “ + httpRequest.getRequestURI());
  try {

[... Truncated for readability ...]
    Map<String, String[]> requestParameterMap = (Map)new HashMap<>(request.getParameterMap());
    for (String key : requestParameterMap.keySet()) {
if (!isValidRequestParam(key)) { // check request parameter redirectToLogin(httpRequest, httpResponse);
return;
} }
    JSONObject jsonObject = null;
    try {
if (request.getParameter(“json”) != null) {
jsonObject = new JSONObject(httpRequest.getParameter(“json”)); if (!isvalidJSONKeys(jsonObject)) { //check JSON keys
          redirectToLogin(httpRequest, httpResponse);
return; }
      } else {
        CyberoamLogger.debug(“RequestCheckFilter”, “Request parameter json not found in request payload”);
      }
    } catch (JSONException je) {
      CyberoamLogger.error(“RequestCheckFilter”, “JSON parsing exception in doFilter: “, (Exception)je);
    }
    chain.doFilter((ServletRequest)httpRequest, (ServletResponse)httpResponse);
  } catch (Exception e) {
    CyberoamLogger.error(“RequestCheckFilter”, “Exception in doFilter: “, e);
  }
}
private boolean isValidRequestParam(String key) {
if (!isAsciiPrintable(key)) { // check parameter key is ASCII printable
    CyberoamLogger.info(“RequestCheckFilter”, “Request param key with non-ASCII printable characters! key=“ + key);
    return false;
  }
  return true;
}
private boolean isvalidJSONKeys(JSONObject jsonObject) {
  Iterator<?> jsonkeys = jsonObject.keys();
  while (jsonkeys.hasNext()) {
String key = (String)jsonkeys.next();
if (!isAsciiPrintable(key)) { // check JSON key is ASCII printable
      CyberoamLogger.info(“RequestCheckFilter”, “JSON key with non-ASCII printable characters! key=“ + key);
      return false;
    }
}
  return true;
}

From the Perl script CyberAPIArch.pm:

sub validateJSON{
    my $self=shift;
    my $data=shift; ## object of validation package
    my $request=shift;
    my $refDetails=shift;
    my $entityJSON=$data;
    my @errorKeylist;
    my @errorKeyStatuslist;
    if (!stat FHLOG){
        open(FHLOG,”>>/log/validationError.log”);
    }else{
        open(FHLOG,”>>/log/validationError.log”);
    }
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
    my $mon=($mon + 1 );
    my $year=($year + 1900 );
    my $refname=ref($entityJSON);
    print FHLOG “\n********** Entity json validation log:$mday-$mon-$year
                 $hour:$min:$sec”.”Z Objectname=$refname\n”;
    #print “\n********** Entity json validation log:$mday-$mon-$year  $hour:$min:$sec Objectname=$refname\n”;
    // check for _discriminator JSON key
if(defined $entityJSON->{_discriminator}){
     #print “\n\n Discriminator Called”;
     // call getValidationHash on JSON object
     $entityJSON=CyberAPIArch->getValidationHash($entityJSON,$request,$refDetails);
    }

[... Truncated for readability ...]

sub getValidationHash{
    my $obj=shift;
    my $hashObj=shift;
    my $request=shift;
    my $refDetails=shift;
    my $secondHashObj = { %$hashObj };
    #Replacing Hash Objects with sub objects
    foreach my $key ( keys % {$hashObj})  {
        if($key eq “_discriminator”){
            # iterate over hash in _discriminator value
            foreach my $curKeyDisc ( keys % {$hashObj->{$key}})  {
                $curDischash={};
                $curDischash=$hashObj->{$key}->{$curKeyDisc};
                #print “\n\n _discriminator Key name $key”;
                #This is for value loop:::
                # iterate over hash associated with field name
                foreach my $curvalue ( keys % {$curDischash})  {
                    # get object name associated with value
                    $objNameToFetch=$curDischash->{$curvalue};
                    if($curvalue eq $hashObj->{$curKeyDisc}{value}){
                        my $Packagei=““;
                        if(defined $refDetails && $refDetails ne ''){
                            $Packagei=$refDetails;
                        }else{
                            $Packagei=ref($hashObj);
                        }
                        eval “use $Packagei”;
                        # object name concatenated into eval, resulting in code injection
                        my $subObj=eval “\$$Packagei”.”::”.”$objNameToFetch”;

[... Truncated for readability ...]

Detecting Attacks

To detect an attack attempting to exploit this vulnerability, the detection device must monitor and parse traffic on ports 443/TCP and 4444/TCP. Note that the traffic is encrypted with SSL/TLS. The detection device must decrypt the traffic before proceeding through the next steps.

Based on the TCP ports monitored, the detection device must inspect HTTP GET and POST requests to a Request- URI containing the following string:

TCP Port   Request-URI
--------   ------------
4444       /webconsole/Controller
443        /userportal/Controller

Requests to the Webadmin Controller endpoint (TCP port 4444) can use the multipart/form-data encoding.

Multipart/form-data is made up of multiple parts, each of which contains a Content-Disposition header. Each part is separated by a string of characters. The string of characters separating the parts is defined by the boundary keyword found on the Content-Type header line, which is set to “multipart/form-data”. The Content-Disposition header contains a name parameter describing the form element being returned. Additional header lines may be present in each part. Each line is separated by a new line sequence. The header is terminated by two consecutive new lines. The form element's data follows. The filename parameter provides a suggested filename to be used if the entity is detached and stored in a separate file. The following is a sample Content-Disposition header for a file item, where the boundary keyword is “TMSR”:

--TMSR
Content-Disposition: form-data; name=“someparameter”; filename=“somefile.txt”CRLF
CRLF
<file contents>

If a request to one of the above endpoints is found, the detection device must parse its parameters according to the encoding described in the Content-Type header and search for the json parameter. If found, the detection device must inspect the json parameter value using any suitable methods below.

Method 1: If the detection device is capable of parsing JSON, it must parse the jsonparameter as a JSON object. The detection device must parse any items (potentially nested) whose key is the string “_discriminator”. If found, the traffic should be considered malicious as an attack exploiting this vulnerability is likely underway.

Method 2: If the detection device is not capable of parsing JSON, it must inspect the jsonparameter as a string and check if it contains the string “_discriminator”. If found, the traffic should be considered malicious as an attack exploiting this vulnerability is likely underway.

Additional things to note:

  • Parameter names and values may be URL-encoded and must be decoded before applying the above detection guidance.
  • String matching for the URI and parameter names (“json”) and values (“_discriminator”) must be performed in a case-sensitive manner.
  • String matching for HTTP header names (i.e., “Content-Type”) and values (i.e., “multipart/form-data”) must be performed in a case-insensitive manner.

Conclusion

There are reports that this vulnerability is being used to target a small group of organizations and that Sophos has “informed each of these [affected] organizations directly.” This bug is also similar to a previous vulnerability, CVE-2022-1040, that that Volexity and The Record claim was used by a Chinese APT crew in March 2022. Regardless, this bug has a CVSS base score of 9.8 and should be remediated immediately. Sohpos users who don’t have hotfixes enabled or are on legacy versions should patch immediately as outlined in Sophos’s advisory. You should also consider blocking the affected ports from external network access if it is not required.

Special thanks to Guy Lederfein and Dusan Stevanovic of the Trend Micro Research Team for providing such a thorough analysis of this vulnerability. For an overview of Trend Micro Research services please visit http://go.trendmicro.com/tis/.

The threat research team will be back with other great vulnerability analysis reports in the future. Until then, follow the team on Twitter or Instagram for the latest in exploit techniques and security patches.