by Henry Huang, CyCraft Senior Security Researcher
In 2019, I discovered multiple vulnerabilities in QNAP Photo Station and CGI programs. These vulnerabilities can be chained into a pre-auth root RCE, which means an attacker could run code as root remotely without logging in. CyCraft was able to find this bug by giving its researchers 10% of their work time to bug hunting and bounties to keep their skills sharp and relevant.
All QNAP NAS models are vulnerable, and there are ~312K vulnerable QNAS NAS instances on the Internet (see statistical prediction below). These vulnerabilities have been responsibly reported, fixed, and assigned the following CVEs:
- CVE-2019–7192 (CVSS 9.8)
- CVE-2019–7193 (CVSS 9.8)
- CVE-2019–7194 (CVSS 9.8)
- CVE-2019–7195 (CVSS 9.8).
This article is the first public disclosure, focusing on only three of the vulnerabilities as they’re enough to achieve pre-auth root RCE.
The following Shodan search reveals 564,000 QNAP instances on the Internet. Among those, 590 of 1065 randomly chosen instances have Photo Station enabled. (Checked via GET /photo/slideshow.php responding with Invalid album selection) Statistically speaking, with 95% confidence and a 3% margin of error, there should be approx. 312K instances with Photo Station enabled, which were all vulnerable at the time of discovery (2019).564K QNAP Instances as Found on Shodan
Affected Photo Station Versions
All downloadable versions before the fixed ones (6.0.3, 5.2.11, 5.4.9) were affected.
Visit QNAP’s Security Advisory for details concerning version info and how to fix the vulnerabilities.
Now, let’s take a look at the 3 vulnerabilities that will later be chained to make a pre-auth root RCE.
Vulnerability 1: Pre-Auth Local File Disclosure (Effectively a Login Bypass)
This vulnerability enables an attacker to read arbitrary files on the Photo Station server WITHOUT authentication.
The vulnerable code is in /p/api/video.php:Local File Vulnerability Disclosure
exportFile simply outputs the file contents of $source_file, whose suffix is fully controllable by the GET/POST parameter filename. Therefore, we can read arbitrary files by specifying, say, filename=./../etc/passwd, making $source_file become /share/./../etc/passwd, which is equivalent to /etc/passwd.
However, to reach the above vulnerable code, we need to pass the check CHECK_ACCESS_CODE:Access Code Check Before the Vulnerability
Function definition:Function Definition of CHECK_ACCESS_CODE
We need to avoid stepping into exit(). So we need to:
- Get an album ID and access code of a publicly accessible album
- Load that album’s access code into $_SESSION['access_code']
- Get the value of $_SESSION['access_code']
Luckily, we can do all the above very easily WITHOUT any authentication!
Step 1 to Bypass CHECK_ACCESS_CODE: Album ID & Access Code
The following request creates a sample album and returns its album ID. This API is meant for sample albums, so it’s publicly accessible, and it doesn’t require authentication:
The response contains the album ID, and it looks like:
Step 2, 3 to Bypass CHECK_ACCESS_CODE: Setting and Getting
The following sets $_SESSION['access_code'] to the access code of the album we specify (?album=qxAPdD)
POC: Pre-Auth Local File Disclosure
With the album ID and access code from the above, we can bypass CHECK_ACCESS_CODE and read arbitrary files without authentication:
Upgrading the Pre-Auth Local File Disclosure to Privilege Escalation (Login Bypass)
We can use this pre-auth local file disclosure to read a magic file that contains a login token, which we can use to authenticate as a valid builtin user appuser.
Magic file /etc/config/.app_token:
token_ex = [email protected]=
- The file content won’t change after factory reset
- The file is generated when /authLogin.cgi?app=xxx&sid=yyy succeeds for the first time
- token_ex is encrypted
- Photo Station caches a plaintext version of token_ex in /share/Multimedia/[email protected]__thumb/ps.app.token
$ cat /share/Multimedia/[email protected]__thumb/ps.app.token
Therefore, we can use vulnerability 1 to read the cached plaintext token to bypass the login and authenticate as appuser:
With this trick, vulnerability 1 is actually an authentication bypass.
- Use the sample album feature to create and retrieve a public album ID, along with its access code ($_SESSION[‘access_code’])
- Use the album ID and access code to bypass CHECK_ACCESS_CODE and trigger the LFD (Local File Disclosure) vulnerability to read arbitrary files
- Use the LFD to read /share/Multimedia/[email protected]__thumb/ps.app.token and use it to authenticate as appuser
Vulnerability 2: Authenticated Session Tampering — Writing PHP Code to Session
Being authenticated as appuser gives us access to the SMTP setting, which has improper filtering in the email string. By setting an email to, for example, <?=`$_POST[c]`?>@evil.com, an authenticated attacker can inject arbitrary PHP code into the session. This can be chained to the next vulnerability, or to other file inclusion vulnerabilities (e.g. include '/path/to/sess_xxx').
POC: Authenticated Session Tampering
Vulnerability 3: (Pre-Auth) Writing Session to Arbitrary Location
This vulnerability enables an unauthenticated attacker to write session contents (serialized $_SESSION) to an arbitrary location on the server.
Vulnerable code:Vulnerability: Arbitrary Session Write
The session_id() is fully controllable via cookie QMS_SID. Therefore, the highlighted line would write an encoded (serialized) session into the file we specify.
POC: Writing Session to Arbitrary Location
The above works because $musicStationSessionPath . ‘/sess_' . session_id() will become /share/photostation/session/qts/sess_xxxxx/../../../../../mnt/ext/opt/photostation2/a.php, a publicly accessible file via the URL path /photo/a.php. (Thanks to tsrm_realpath, because it will normalize sess_xxxxx/../bbb into bbb, even if sess_xxxxx doesn’t exist. This also caused phpMyAdmin 4.8.0~4.8.1 RCE that I found in 2018.)
Chaining for Pre-Auth Root RCE
- Use vulnerability 1 to bypass authentication and authenticate as appuser
- Use vulnerability 2 to put PHP code (via SMTP email) in PHP session ($_SESSION)
- Use vulnerability 3 to write the polluted PHP session to Photo Station’s web directory to make a webshell
Now, you might ask: So where is that root permission? We’re only appuser, right?
I was also surprised when I found out: The web server runs as root! Therefore, you can actually read /etc/shadow using vulnerability 1.
A pity though: vulnerability 3 could’ve been a one-shot pre-auth root RCE. However, I couldn’t find a way to inject PHP code into $_SESSION without authentication.
- 2019/06/14: Reported technical details to QNAP
- 2019/12/16: Vendor fixed all 4 vulnerabilities, offered to provide a bounty (the amount is concealed due to the bounty terms)
- 2019/12/31: Got bounty
Three vulnerabilities are chained to get this pre-auth root RCE in QNAP Photo Station, and it works on all QNAP’s NAS models. Several tricks for exploiting QNAP products are also disclosed. Hopefully, QNAP fixes running the web server as root; otherwise, I’m pretty sure there will be more high-CVSS CVEs coming up.
QNAP probably needs to conduct a thorough security auditing of their code base and configuration, as their CGI programs and PHP code contain lots of instances of string concatenation without proper sanitization, which is a textbook example of injection that every hacker learns the first day they start hacking.
PHP is an excellent language full of features that make programming easier, but it can sometimes cause unexpected behavior of which many PHP users aren’t aware of, e.g. file stream wrappers, tsrm_realpath, etc. The latter part is what made PHP notorious for security issues: Convenience often contradicts security.
Other Vulnerabilities Discovered by CyCraft:
Our research team jokingly refer to their 10% bug hunting time as our “Bug Bounty Hunting Happy Hour.” Other vulnerabilities discovered during “happy hour” include:
- UPGRADE YOUR QNAP NAS NOW, if you haven’t already
- Turn any file disclosure into authentication bypass by reading the magic file /share/Multimedia/[email protected]__thumb/ps.app.token
- There is a way to decrypt /etc/config/.app_token, but I’ll leave it as homework for the reader
- QNAP’s web server runs as root
- QMS_SID might give you some more 0days
When you join CyCraft, you will be in good company. CyCraft secures government agencies, Fortune Global 500 firms, top banks and financial institutions, critical infrastructure, airlines, telecommunications, hi-tech firms, and SMEs.
We power SOCs with our proprietary and award-winning AI-driven MDR (managed detection and response), SOC (security operations center) operations software, TI (threat intelligence), Health Check, automated forensics, and IR (incident response), and Secure From Home services.
Additional Related Resources
- CyCraft CEO, Benson Wu, and CyCraft Global Project Manager, Chad Duffy, speak on the latest MITRE ATT&CK Evaluations. Read their thoughts on our results and the philosophy powering CyCraft.
- Learn how we detected and defeated a foreign APT targeting Taiwan’s high-tech ecosystem. Read our full analysis and malware reversal.
- Has your organization recently shifted to a Work From Home environment? Learn how to receive three free months of our Secure From Home service.
- Our Enterprise Health Check drops your mean dwell time down from 197 days to under 1 day without false positives or false negatives. Know with confidence if hackers have penetrated your enterprise.