### [Product]:D-LINK DIR-815
### [Firmware Version]:1.01
### [Vulnerability Type]:buffer overflow
### [Vulnerability Summary]:
The software component /htdocs/web/captcha.cgi suffers from a buffer overflow vulnerability due to an unsafe implementation of the strncpy() function. When a user-supplied input string (a2) that is exactly 64 characters long (or longer) without a NULL termination character is provided, the strncpy(a1 + 8, a2, 0x40u); operation fails to append a NULL character at the end of the copied string. This oversight leads to a potential buffer overflow condition.
### [Discoverer]:
Jian-Xian Li, Yen-Jen Lin, Hung-Min Sun / Laboratory of Information Security, National Tsing Hua University
### [Crash Result]:
![](https://hackmd.io/_uploads/SJGQI6Nvn.png)
![](https://hackmd.io/_uploads/rys-pNlth.png)
---
<!-- Normal Execution Result:
![](https://hackmd.io/_uploads/HyjMpVlF3.png)
Crash Execution Result:
![](https://hackmd.io/_uploads/SJb4pNlY3.png) -->
### [Vulnerbility Detail]:
Function_name (Problematic Code)
- main (v8 = (int (*)())&captchacgi_main;)
```=c
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v3; // $s0
char *v6; // $v0
int (*v8)(); // $t9
int v9; // $a0
v3 = *argv;
v6 = strrchr(*argv, 47);
if ( v6 )
v3 = v6 + 1;
if ( !strcmp(v3, "phpcgi") )
{
v8 = phpcgi_main;
v9 = argc;
return ((int (__fastcall *)(int, const char **, const char **))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "dlcfg.cgi") )
{
v8 = dlcfg_main;
v9 = argc;
return ((int (__fastcall *)(int, const char **, const char **))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "seama.cgi") )
{
v8 = seamacgi_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "fwup.cgi") )
{
v8 = fwup_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "fwupdater") )
{
v8 = (int (*)())fwupdater_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "session.cgi") )
{
v8 = (int (*)())&sessioncgi_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "captcha.cgi") )
{
v8 = (int (*)())&captchacgi_main; //Execute captcha.cgi, enter the corresponding function.
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "hedwig.cgi") )
{
v8 = hedwigcgi_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "pigwidgeon.cgi") )
{
v8 = (int (*)())&pigwidgeoncgi_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "service.cgi") )
{
v8 = (int (*)())&servicecgi_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "ssdpcgi") )
{
v8 = ssdpcgi_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "soap.cgi") )
{
v8 = soapcgi_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "gena.cgi") )
{
v8 = genacgi_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "conntrack.cgi") )
{
v8 = (int (*)())&conntrackcgi_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
if ( !strcmp(v3, "hnap") )
{
v8 = (int (*)())&hnap_main;
v9 = argc;
return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp);
}
printf("CGI.BIN, unknown command %s\n", v3);
return 1;
}
```
- captchacgi_main (captcha = sess_generate_captcha((int)v9);)
```=c
int captchacgi_main()
{
char *v0; // $v0
const char *v1; // $s0
const char *v2; // $a2
int (*v3)(); // $a0
int v4; // $a2
int captcha; // $v0
int v6; // $s1
int v7; // $s0
char v9[216]; // [sp+18h] [-1ECh] BYREF
char v10[16]; // [sp+F0h] [-114h] BYREF
char v11[260]; // [sp+100h] [-104h] BYREF
v0 = getenv("REQUEST_METHOD");
v1 = v0;
if ( !v0 )
{
v2 = "no REQUEST";
LABEL_10:
cgibin_print_http_status(400, "", v2);
v7 = -1;
goto LABEL_12;
}
if ( !strcasecmp(v0, "GET") )
{
v3 = sub_408CC4;
v4 = 64;
}
else
{
if ( strcasecmp(v1, "POST") )
{
v2 = "unsupported HTTP request";
goto LABEL_10;
}
v3 = sub_408C10;
v4 = 1024;
}
cgibin_parse_request(v3, 0, v4);
captcha = sess_generate_captcha((int)v9); //generate captcha
v6 = captcha;
if ( captcha )
{
sprintf(v11, "rndimage -f /htdocs/web/docs/captcha_%d.jpeg -p /usr/sbin/fonts -w 180 -t 40 %s", captcha, v10);
v7 = 0;
system(v11);
printf(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/xml\r\n"
"\r\n"
"<?xml version=\"1.0\" encoding=\"utf-8\"?><captcha><result>OK</result><message>/docs/captcha_%d.jpeg</message></captcha>",
v6);
}
else
{
printf(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/xml\r\n"
"\r\n"
"<?xml version=\"1.0\" encoding=\"utf-8\"?><captcha>\n"
"\t<result>FAIL</result><message>NO SESSION</message>\n"
"</captcha>");
v7 = 0;
}
LABEL_12:
sub_4089C0();
return v7;
}
```
- sess_generate_captcha (sess_get_uid(v3);、v2 = sub_40795C(a1, string);)
```=c
int __fastcall sess_generate_captcha(int a1)
{
int v2; // $s1
int v3; // $s2
int string; // $v0
int i; // $s0
unsigned int v6; // $v0
int v7; // $v1
int v9[4]; // [sp+18h] [-70h] BYREF
char v10[28]; // [sp+28h] [-60h] BYREF
struct sysinfo seed; // [sp+44h] [-44h] BYREF
strcpy(v10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
v2 = 0;
v3 = sobj_new();
sub_40787C();
sub_4072D0(v9);
if ( v3 )
{
sess_get_uid(v3); //It does not limit the cookie value input by the user, thus being a point of attack.
string = sobj_get_string(v3); //The provided value is converted to a string, but the size of the v3 value is not limited either.
v2 = sub_40795C(a1, string); //The value of the string will be very large and long, which is then passed into the sub_40795C function.
if ( v2 )
{
sysinfo(&seed);
srandom(seed.uptime);
for ( i = 0; i != 6; ++i )
{
v6 = random();
v7 = a1 + i;
*(_BYTE *)(v7 + 216) = *((_BYTE *)&v9[4] + v6 % 0x1A);
}
*(_DWORD *)(a1 + 4) = -1;
*(_BYTE *)(a1 + 222) = 0;
sysinfo(&seed);
*(_DWORD *)a1 = seed.uptime + v9[0];
sub_407660(a1, v2, 1);
}
sobj_del(v3);
}
return v2;
}
```
- sess_get_uid
```=c
int __fastcall sess_get_uid(int a1)
{
int v2; // $s2
char *v3; // $v0
int v4; // $s3
char *v5; // $s4
int v6; // $s1
int v7; // $s0
char *string; // $v0
int result; // $v0
v2 = sobj_new();
v4 = sobj_new();
v3 = getenv("HTTP_COOKIE"); //After getenv("HTTP_COOKIE"); reads the cookie value, it does not restrict the size and length of this value.
if ( !v2 )
goto LABEL_27;
if ( !v4 )
goto LABEL_27;
v5 = v3;
if ( !v3 )
goto LABEL_27;
v6 = 0;
while ( 1 )
{
v7 = *v5;
if ( !*v5 )
break;
if ( v6 == 1 )
goto LABEL_11;
if ( v6 < 2 )
{
if ( v7 == 32 )
goto LABEL_18;
sobj_free(v2);
sobj_free(v4);
LABEL_11:
if ( v7 == 59 )
{
v6 = 0;
}
else
{
v6 = 2;
if ( v7 != 61 )
{
sobj_add_char(v2, v7);
v6 = 1;
}
}
goto LABEL_18;
}
if ( v6 == 2 )
{
if ( v7 == 59 )
{
v6 = 3;
goto LABEL_18;
}
sobj_add_char(v4, *v5++);
}
else
{
v6 = 0;
if ( !sobj_strcmp(v2, "uid") )
goto LABEL_21;
LABEL_18:
++v5;
}
}
if ( !sobj_strcmp(v2, "uid") )
{
LABEL_21:
string = (char *)sobj_get_string(v4);
goto LABEL_22;
}
LABEL_27:
string = getenv("REMOTE_ADDR");
LABEL_22:
result = sobj_add_string(a1, string);
if ( v2 )
result = sobj_del(v2);
if ( v4 )
return sobj_del(v4);
return result;
}
```
- sub_40795C (strncpy(a1 + 8, a2, 0x40u);)
```=c
int __fastcall sub_40795C(char *a1, const char *a2)
{
int i; // $s1
unsigned int v5; // $s0
unsigned int v6; // $s0
int v8; // [sp+18h] [-54h] BYREF
int v9; // [sp+1Ch] [-50h]
struct sysinfo v10; // [sp+28h] [-44h] BYREF
if ( !a1 )
return 0;
i = 0;
if ( a2 && *a2 )
{
sub_4072D0(&v8);
memset(a1, 0, 0xE8u);
for ( i = 1; v9 >= i; ++i )
{
if ( !sub_407660(a1, i, 0) )
{
v5 = *(_DWORD *)a1;
sysinfo(&v10);
if ( v5 >= v10.uptime && !strcmp(a1 + 8, a2) )
return i;
}
}
i = 1;
while ( v9 >= i )
{
if ( sub_407660(a1, i, 0) )
goto LABEL_14;
v6 = *(_DWORD *)a1;
sysinfo(&v10);
++i;
if ( v6 < v10.uptime )
{
--i;
LABEL_14:
memset(a1, 0, 0xE8u);
sysinfo(&v10);
*(_DWORD *)a1 = v10.uptime + v8;
*((_DWORD *)a1 + 1) = -1;
strncpy(a1 + 8, a2, 0x40u); //Vulnerbility code
sub_407660(a1, i, 1);
return i;
}
}
return 0;
}
return i;
}
```
The line **strncpy(a1 + 8, a2, 0x40u);** in the program creates a buffer overflow vulnerability due to improper usage of the strncpy() function.
However, if a2 is 64 characters or longer and does not contain a NULL termination character ('\0'), strncpy() will not append a NULL character at the end of the target string a1. This is where the vulnerability lies. Since the NULL character is used to mark the end of a string in C, the absence of this character can lead to a buffer overflow condition.