DCS-960L Equipment Analysis
DCS-960L Equipment Analysis
Preliminary Analysis
I came into contact with this device during security research, and I took time to briefly study this device in depth.
Links
The firmware used in this analysis is 1.09
, and the download address is
https://www.dlinktw.com.tw/techsupport/ProductInfo.aspx?m=DCS-960L
Web Service Architecture Analysis
We can directly use binwalk to unpack this firmware package.
1 | DECIMAL HEXADECIMAL DESCRIPTION |
After unpacking, you can see following things.
1 | 17:53:04 z1r0@z1r0deMBP.lan squashfs-root l |
In etc/passwd_default
I found the default user and password. user name is admin, password is empty.
1 | admin::0:0:root:/:/bin/sh |
After analyzing the rcS startup scripts (etc/init.d/rcS
I found out that it starts the httpd program.
1 |
|
The httpd is a small web server, so I decided to analyze it, the relevant code is as follows.
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
First put httpd on port 80 for listening, then a series of signal processing mechanisms will be registered.
Call mmap()
function to map a memory space with a size of 0x6590 bytes, and assign the returned mapped address to the variable addr.
If assign success, It will read the SnapshotAuthentication and LivevideoAuthentication values from configuration file, and initialize the user authentication mode based on these two values.
1 | sub_402BF0(17); |
Call the AspInitial()
function to initialize the corresponding ASP, enter the do while loop to monitor whether someone is connected.
If someone connects, fork a child process to call the sub_403038
function to handle client requests.
Finally, call some function to close corresponding request. So then start analyzing sub_403038
function
1 | void __fastcall __noreturn sub_403038(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, const char *a9) |
The function is very long and important, it does a lot of things.
First call the reqInit()
function to initialize the request, then call the sub_401C2C()
function to handle requests, and do different processing according to the return value.
1 | int __fastcall sub_401C2C(int a1, const char **a2, int a3) |
If requested Content-Length ≥ 13631489, return 414.
If url is empty, return 404.
If it is a whitelist, return 0.
If file does not exist, return 404.
Different validations are determined according to the values of a1 and a3.
- If a1 == 0 && a3 ≠ 0
- Encrypt username and password with base64
- if a1 == 0 && a3 == 0
- return 0
If url is not whitelist.
- Authenticate
- return status code
Call the corresponding case according to the return status code.
If the status code is equal to 200.
if request is scheduled
- execute
sub_402040
function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15int __fastcall sub_402040(_DWORD *a1, int a2, int a3)
{
char *const *Env; // $s2
char *const *Arg; // $s3
Env = (char *const *)reqMakeEnv(*a1, fd, a2, a3);
Arg = (char *const *)reqMakeArg(*a1);
if ( dup(fd) || dup(fd) != 1 )
logx(3, "%s %s %d", "httpd.c", "runcgi", 823);
fcntl(fd, 2, 1);
execve(*(const char **)(*a1 + 28), Arg, Env);
sub_401950();
reqFree(*a1);
return logx(3, "%s %s %d", "httpd.c", "runcgi", 830);
}call the execve function to execute
- execute
if request is .asp
- execute asp function
During the response, the access_token
will be checked, if the access_token is wrong, it will be 401. Call checkToken_localrecording()
for access control.
In the above analysis, it was found that there is a hnap service.
hnap service is a very old protocol, d-link began to deprecate this protocol in 2016, so many bugs happen here.
Vulnerability
Unauthenticated Information Disclosure
In the above analysis, there is a very interesting code snippet.
1 | void __fastcall __noreturn sub_403038(int live, int authtype, int pid, int port) |
If url is /hnap/hnap_service
, it will go to LABEL_64, LABEL_64 call sub_402040
function, sub_402040
function use execve function to call hnap_service
cgi program.
But after execution is over, checkToken_localrecording()
will be called for access control. So hnap_service
can be used without authentication.
If you access hnap_service
with a GET request, the GetDeviceSettings
function will be called.
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
The GetDeviceSettings
function will output import informations such as FirmwareVersion/MacAddr
and so on.
1 | int GetDeviceSettings() |
Stack Overflow
In the Login function, I can enter Action, the Action will strcpy to v42, but program do not check size, resulting in stack overflow.
1 | int __fastcall Login(int a1) |
In the Login function, I can enter Username and LoginPassword, the username&password will strcpy to v36&v38, but program do not check the size, resulting in stack overflow.
1 | int __fastcall Login(int a1) |
Cookie Format String
In the Login function, the program use the getenv function to obtain COOKIE value, and use the snprintf function to put the value into v39, without %s, resulting format string vuln
1 | int __fastcall Login(int a1) |
Authentication Bypass
In the Login function, Enter the Username and LoginPassword, then call usrGetPass function to obtain the password corresponding to the Username.
challenge + challenge length + public_key+password + public_key length+password length
performs hmac_md5 function to get private key.
Then, challenge + challenge length + private_key + private_key length
performs hmac_md5 function to get login_password
.
If login_password
equal to save password, then login successful.
1 | int __fastcall Login(int a1) |
But in the usrGetPass
function, if the username I enter is not in the user list, v6=21
, result = -1
and a2 = null
. So I just need to get the challenge and public key, and then follow the above hmac_md5 calculation go get the correct passwod.
1 | int __fastcall usrGetPass(const char *a1, char *a2, size_t a3) |
Affected Models
I only analyzed a very small part, there may be other vulnerabilities