TL;DR
We discovered a path traversal vulnerability in ZendTo versions 6.15-7 and prior. This vulnerability allows malicious actors to bypass the security controls of the service to access or modify potentially sensitive information of other users. This issue is patched in 6.15-8, and we encourage all users to upgrade as soon as possible.
Background
ZendTo is a web-based file transfer application used to send and receive large files or collections of files via a secure dropoff/pickup point. It is commonly used by organizations in higher education, government, and healthcare, among other industries.
As we’ve seen in recent years, file transfer applications have become common targets for malicious actors, such as ransomware groups. For some examples, see the fallout from vulnerabilities exploited in applications such as Progress’s MOVEit Transfer, Accellion’s File Transfer Appliance, and Fortra’s GoAnywhere MFT.
As such, when we came across ZendTo, we decided to take a closer look.
ZendTo Overview
ZendTo is designed as a sort of dropbox utility where anyone is free to register and upload files to be shared with another user of the system. By design, this means that anyone is free to register and begin uploading files.
Path Traversal Vulnerability
During the process of uploading files destined for another user (a dropoff), users specify a variety of information about their uploads, such as filenames, file contents, descriptions, who to send it to, etc. The backend parsing routine for some of this information contains a path traversal vulnerability that allows an attacker to access/modify sensitive data of other users or the host system.
When parsing “dropoff” requests, chunkName
values are read in order to determine which stored file uploads should be associated with this dropoff request. While generally created via client-side javascript, there’s nothing preventing an end user from specifying their own values manually. That said, these chunkName
values are sanitized to ensure they are alphanumeric.
As you can see in the code snippet, the chunkName
is appended to a chunkPath
, which points to the temporary directory that all pending uploads are stored in. What happens if a user-specified chunkName
doesn’t contain ANY alphanumeric characters, though? Well, we simply have a chunkPath
that points to the root upload directory rather than to a specific file.
Later in the dropoff routine, another user-controlled variable (tmp_name
) is appended to chunkPath
in order to move files from the uploads directory into the desired dropoff directory. tmp_name
is not properly sanitized and allows users to specify arbitrary files, which is where the path traversal issue actually occurs.
Finally, when files are moved to the desired dropoff directory, ZendTo will copy the file at the user supplied location to the dropoff directory, which can then be retrieved by the attacker. It should be noted that in default installations, only files accessible by the www-root user are affected, which includes all user uploaded files by default.
A proof of concept request is as follows:
In this request, an arbitrary user has submitted a dropoff request with a chunkName
of “.” and a tmp_name
of “/../../log/zendto/zendto.log.” This results in a chunkPath
of “/var/zendto/incoming/./../../log/zendto/zendto.log.” This results in the zendto.log file being moved to the newly created dropoff location, which reveals the contents to the attacker once downloaded.
The logfile used in this example contains all of the dropoff claimIDs for the system, which then allows the attacker access to any other user uploaded content by referencing those dropoff locations in subsequent attacks. If the attacker chose to move something such as the internal ZendTo database, or critical source files, they could cause a denial of service to the entire ZendTo service.
This serves as a good reminder that just because a vulnerability requires authentication to exploit, that authentication isn’t always a particularly cumbersome obstacle.
Disclosure Timeline
Special thanks to ZendTo for being so responsive to this issue and proactive in their communications regarding the fix.
- April 15, 2025 – Requested security contact from ZendTo Support.
- April 21, 2025 – Second contact attempt sent to ZendTo Support.
- April 30, 2025 – Third and final contact attempt sent to ZendTo Support.
- May 1, 2025 – ZendTo responds.
- May 1, 2025 – Horizon3.ai sends disclosure details. 90-day disclosure deadline of July 30, 2025.
- May 22, 2025 – Horizon3.ai requests status update from ZendTo support.
- May 22, 2025 – ZendTo support responds with status update.
- June 9, 2025 – ZendTo sends status update.
- June 10, 2025 – ZendTo sends status update and releases patched version (6.15-8).
- June 10, 2025 – Horizon3.ai acknowledges and states they’ll send a follow-up soon.
- June 12, 2025 – Horizon3.ai requests CVE identifier from VulnCheck.
- June 13, 2025 – ZendTo sends follow-up clarifications.
- June 16, 2025 – Horizon3.ai provides CVE identifier to ZendTo.
Get a NodeZero Demo:
See NodeZero helps you uncover hidden vulnerabilities before attackers do—schedule a demo.