### [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.