DIR-823G 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.0.2B05
, and the download address is
http://www.dlink.com.cn/techsupport/ProductInfo.aspx?m=DIR-823G
Web Service Architecture Analysis We can directly use binwalk to unpack this firmware package.
1 2 3 4 5 6 14:39:22 z1r0@z1r0deMacBook-Pro.local 823G binwalk DIR823G_V1.0.2B05_20181207.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 10264 0x2818 LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 7053972 bytes 2057250 0x1F6422 Squashfs filesystem, little endian, version 4.0, compression:xz, size: 4168788 bytes, 953 inodes, blocksize: 131072 bytes, created: 2038-05-18 09:48:48
After unpacking, you can see following things.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 14:53:16 z1r0@z1r0deMacBook-Pro.local squashfs-root l total 0 drwxr-xr-x@ 17 z1r0 staff 544B Dec 4 2018 . drwxr-xr-x 9 z1r0 staff 288B Aug 7 14:43 .. drwxr-xr-x@ 184 z1r0 staff 5.8K Aug 7 14:43 bin drwxr-xr-x@ 8 z1r0 staff 256B Dec 4 2018 dev drwxr-xr-x@ 40 z1r0 staff 1.3K Dec 4 2018 etc drwxr-xr-x@ 2 z1r0 staff 64B Dec 4 2018 home lrwxr-xr-x@ 1 z1r0 staff 8B Dec 4 2018 init -> bin/init drwxr-xr-x@ 27 z1r0 staff 864B Dec 4 2018 lib drwxr-xr-x@ 2 z1r0 staff 64B Dec 4 2018 mnt drwxr-xr-x@ 2 z1r0 staff 64B Dec 4 2018 proc lrwxr-xr-x@ 1 z1r0 staff 9B Dec 4 2018 root -> /var/root drwxr-xr-x@ 2 z1r0 staff 64B Dec 4 2018 sys lrwxr-xr-x@ 1 z1r0 staff 8B Dec 4 2018 tmp -> /var/tmp drwxr-xr-x@ 3 z1r0 staff 96B Dec 4 2018 usr drwxr-xr-x@ 2 z1r0 staff 64B Dec 4 2018 var drwxr-xr-x@ 84 z1r0 staff 2.6K Dec 4 2018 web drwxr-xr-x@ 41 z1r0 staff 1.3K Dec 4 2018 web_mtn
I checked the rcS file first (./etc/init.d/rcS
), I found goahead web server, so I will analyze goahead
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #!/bin/sh ...... goahead & ...... if [ "$MODE " = "HW_FACTORY_MODE=1" ];then telnetd& fi ifconfig wlan1-va3 down speedcheck&
The goahead is a small web server. The relevant code is as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 int __cdecl main (int argc, const char **argv, const char **envp) { puts ("Debug: Goahead start" , argv, envp); if ( daemon(1 , 1 ) < 0 ) { perror("error daemon.../n" ); exit (1 ); } sub_4030F0(0 , 61440 , 1 ); signal(13 , 1 ); if ( !apmib_init() ) return -1 ; if ( sub_423CCC() < 0 ) return -1 ; if ( argc >= 2 ) off_5890B0 = "/tmp/web_mtn" ; sub_46EC40(); if ( sub_423F90() < 0 ) return -1 ; while ( !dword_58A6D0 ) { if ( sub_410294(-1 ) || sub_410410(-1 , 1000 ) ) sub_410944(-1 ); sub_40468C(); sub_4210F0(); } sub_4159C4(); sub_41BDA0(); sub_40F7C4(); sub_40322C(); return 0 ; }
This binary file has the symbol table stripped, in the sub_423CCC
function, the function will parse the requested URL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 int sub_423F90 () { int v1; int v2; int v3; int v4[4 ]; char v5[128 ]; char v6[128 ]; memset (v4, 0 , sizeof (v4)); sub_423E90((int )"br0" , (int )v4); sub_40F750(); sub_4158C0(); sub_416908("adm" , 7 , 3 , 0 ); if ( "admin" && aAdmin[0 ] && "1234" && a1234[0 ] ) { sub_415F5C("admin" , (int )"1234" , (int )"adm" ); sub_4172CC("/" , 3 , 0 , "adm" ); } else { error("goahead.c" , 502 , 2 , "gohead.c: Warning: empty administrator account or password" ); } v3 = inet_addr(v4); if ( v3 == -1 ) { error("goahead.c" , 531 , 2 , "initWebs: failed to convert %s to binary ip data" , (const char *)v4); return -1 ; } else { strcpy (v5, off_5890B0); sub_40542C(v5); v2 = inet_ntoa(v3); if ( (unsigned int )(strlen (v2) + 1 ) >= 0x80 ) v1 = 128 ; else v1 = strlen (v2) + 1 ; sub_40D104(v6, v2, v1); sub_4205C0((int )v6); sub_42051C(v6); sub_4053C4("default.asp" ); sub_411D4C(off_5890B4); sub_41BC40(dword_5890B8, dword_5890BC); websUrlHandlerDefine(&dword_4A3C4C, 0 , 0 , sub_4110F4); websUrlHandlerDefine("/HNAP1" , 0 , 0 , websHNAPHandler); websUrlHandlerDefine("/goform" , 0 , 0 , websFormHandler); websUrlHandlerDefine("/cgi-bin" , 0 , 0 , websCgiHandler); websUrlHandlerDefine("/EXCU_SHELL" , 0 , 0 , sub_4234CC); websUrlHandlerDefine(&dword_4A3C4C, 0 , 0 , sub_404940); sub_4110B4(); websUrlHandlerDefine("/" , 0 , 0 , sub_424320); return 0 ; } }
websHNAPHandler
function will handle the /HNAP1
request.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 int __fastcall websHNAPHandler ( int a1, int a2, int a3, int a4, int arg0a, int arg4, int arg8, int argC, int a5, int a6, int a7, int a8, int a9, int a10, const char *a11) { int v16; int v17; int v18; char v19[104 ]; int v20; char v21[5000 ]; v18 = 0 ; strcpy ( v19, "HTTP/1.0 200 OK\\r\\nContent-Type: text/html; charset=utf-8\\r\\nConnection: close\\r\\nCache-Control: private\\r\\n\\r\\n" ); v17 = 0 ; v20 = 0 ; dword_58A6C0 = a1; v16 = malloc (10240 ); if ( v16 ) { memset (v16, 0 , 10240 ); v17 = malloc (51200 ); if ( v17 ) { memset (v17, 0 , 51200 ); if ( *(_DWORD *)(a1 + 1316 ) ) { apmib_get(7011 , &v20); for ( hnap_func_ptr = (int )&HnapFunc; *(_DWORD *)hnap_func_ptr; hnap_func_ptr += 8 ) { if ( strstr (*(_DWORD *)(a1 + 1316 ), *(_DWORD *)hnap_func_ptr) ) { memset (v21, 0 , sizeof (v21)); snprintf (v21, 4999 , "echo '%s' >/var/hnaplog" , (const char *)a7); system(v21); printf ("wp->hnapfunc===========>%s\\n" , *(const char **)(a1 + 1316 )); if ( !strncmp (*(_DWORD *)hnap_func_ptr, "GetLocalMac" , 11 ) ) { memset (&qword_58A6A0, 0 , 32 ); strncpy (&qword_58A6A0, a1 + 48 , 32 ); } if ( (*(int (__fastcall **)(int ))(hnap_func_ptr + 4 ))(a7) ) break ; } } } else { sub_432FA8(a7); } } else { printf ("websHNAPFuncHandler: not enough memory (1)\\n!" ); v18 = -1 ; } } else { printf ("websHNAPFuncHandler: not enough memory (0)\\n!" ); v18 = -1 ; } free (v16); free (v17); return v18; }
To obtain the HNAP function name from the request parameters.
Traverse the array of HnapFunc function pointers, match the hnap function name.
If it matched, call the corresponding HnapFunc function pointer.
Next, analyze the websFormHandler
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 int __fastcall websFormHandler ( int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11) { void (__fastcall *v12)(int , const char *, int ); int v13; const char *v14; _BYTE *v15; int v16; char v17; _BYTE v18[3 ]; ++dword_58AF74; strncpy (&v17, a10, 254 ); v13 = strchr (v18, '/' ); if ( v13 ) { v14 = (const char *)(v13 + 1 ); v15 = (_BYTE *)strchr (v14, '/' ); if ( v15 ) *v15 = 0 ; v16 = sub_412360(dword_588AA0, v14); if ( v16 ) { v12 = *(void (__fastcall **)(int , const char *, int ))(v16 + 18 ); if ( v12 ) v12(a1, v14, a11); } else { sub_41F454(a1, 200 , "Form %s is not defined" , v14); } return 1 ; } else { sub_41F454(a1, 200 , "Missing form name" ); return 1 ; } }
Call the sub_412360
function to find the form entered by user, if find the form, call the corresponding handler function.
continue to analyze websCgiHandler
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 int __fastcall websCgiHandler ( _DWORD *a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11) { int v12; int v13; int v14; int v15; int v16; int v17; int v18; _DWORD *k; int v20; _DWORD *v21; int v22; _BYTE *v23; _BYTE *v24; int i; int v26; int v27; int j; _DWORD *v29; char v30; _BYTE v31[3 ]; char v32[256 ]; int v33; char v34[20 ]; int v35; ++dword_58AF78; strncpy (&v30, a10, 254 ); v22 = strchr (v31, '/' ); if ( v22 ) { v23 = (_BYTE *)strchr (v22 + 1 , '/' ); if ( v23 ) *v23 = 0 ; v12 = sub_40539C(); sub_40BBEC(&v33, 254 , "%s/%s/%s" , v12); if ( !stat(v33, v34) && (v35 & 0x8000 ) != 0 ) { if ( access(v33, 1 ) ) { sub_41F454(a1, 200 , "CGI process file is not executable" ); sub_403588(v33); return 1 ; } else { getcwd(v32, 254 ); v24 = (_BYTE *)strrchr (v33, '/' ); if ( v24 ) { *v24 = 0 ; chdir(v33); *v24 = '/' ; } v15 = 10 ; v21 = (_DWORD *)sub_4032A8(40 ); *v21 = v33; v17 = 1 ; if ( !strchr (a11, '=' ) ) { v13 = strlen (a11); sub_41F8A8(a11, a11, v13); for ( i = strtok(a11, " " ); i; i = strtok(0 , " " ) ) { v21[v17++] = i; if ( v17 >= v15 ) { v15 *= 2 ; v21 = (_DWORD *)sub_403754(v21, 4 * v15); } } } v21[v17] = 0 ; v16 = 64 ; v20 = sub_4032A8(256 ); sub_40BBEC(v20, 254 , "%s=%s" , "PATH_TRANSLATED" ); sub_40BBEC(v20 + 4 , 254 , "%s=%s/%s" , "SCRIPT_NAME" ); sub_40BBEC(v20 + 8 , 254 , "%s=%s" , "REMOTE_USER" ); sub_40BBEC(v20 + 12 , 254 , "%s=%s" , "AUTH_TYPE" ); v18 = 4 ; for ( j = sub_41211C(a1[8 ]); j; j = sub_412230(a1[8 ]) ) { if ( *(_BYTE *)(j + 30 ) ) { if ( *(_DWORD *)(j + 26 ) == 10 ) { if ( strcmp (*(_DWORD *)(j + 4 ), "REMOTE_HOST" ) ) { if ( strcmp (*(_DWORD *)(j + 4 ), "HTTP_AUTHORIZATION" ) ) { sub_40BBEC(v20 + 4 * v18++, 254 , "%s=%s" , *(_DWORD *)(j + 4 )); if ( v18 >= v16 ) { v16 *= 2 ; v20 = sub_403754(v20, 4 * v16); } } } } } } if ( (a1[62 ] & 0x80000 ) != 0 ) sub_40BBEC(v20 + 4 * v18++, 254 , "%s=%s" , "UPLOAD_FILENAME" ); *(_DWORD *)(v20 + 4 * v18) = 0 ; if ( !a1[66 ] ) a1[66 ] = sub_42464C(a1); v27 = a1[66 ]; v26 = sub_42464C(a1); v14 = sub_4246B4(v33, v21, v20, v27); if ( v14 == -1 ) { sub_41F454(a1, 200 , "failed to spawn CGI task" ); for ( k = (_DWORD *)v20; *k; ++k ) sub_403680(*k); sub_403680(v33); sub_403680(v21); sub_403680(v20); sub_403680(v26); } else { v29 = *(_DWORD **)(dword_589980 + 4 * sub_40AF88(&dword_589980, &dword_589984, 32 )); v29[6 ] = v14; v29[1 ] = v27; v29[2 ] = v26; v29[3 ] = v33; v29[4 ] = v21; v29[5 ] = v20; *v29 = a1; v29[7 ] = 0 ; sub_41ED14(a1); } chdir(v32); return 1 ; } } else { sub_41F454(a1, 200 , "CGI process file does not exist" ); sub_403588(v33); return 1 ; } } else { sub_41F454(a1, 200 , "Missing CGI name" ); return 1 ; } }
Parsing the CGI name from the requested url, and check if this file exists.
If it exists
then fork a child process to execute the cgi file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 int __fastcall sub_4246B4 (int a1, _DWORD *a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) { int v9; int v10; int v12; int v13; int v14; int v15; int v16; v12 = -1 ; v13 = -1 ; v14 = -1 ; v15 = -1 ; v16 = open(a4, 258 , 438 ); if ( v16 >= 0 ) { v15 = open(a9, 258 , 438 ); if ( v15 >= 0 ) { v14 = dup(0 ); if ( v14 != -1 ) { v13 = dup(1 ); if ( v13 != -1 && dup2(v16, 0 ) != -1 && dup2(v15, 1 ) != -1 ) { v12 = fork(); if ( !v12 ) { if ( strstr (a1, "GetDownLoadSyslog.sh" ) || strstr (a1, "ExportSettings.sh" ) ) { a2[1 ] = "D-Link" ; a2[2 ] = "DIR-823G" ; a2[3 ] = 0 ; } if ( execve(a1, a2, a3) == -1 ) puts ("content-type: text/html\\n\\nExecution of cgi process failed" , v9, v10); exit (0 ); } } } } } if ( v13 >= 0 ) { dup2(v13, 1 ); close(v13); } if ( v14 >= 0 ) { dup2(v14, 0 ); close(v14); } if ( v15 >= 0 ) close(v15); if ( v16 >= 0 ) close(v16); return v12; }
If it does not exists
Finally, analyze the websExcuteShellHandler
function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 int __fastcall sub_4234CC (int a1) { int v1; int v2; int v3; int v4; int v5; int v6; int v7; const char *v9; int v10; int v11; int v12; int i; char v14[104 ]; v12 = 0 ; v11 = 1 ; strcpy ( v14, "HTTP/1.0 200 OK\\r\\nContent-Type: text/html; charset=utf-8\\r\\nConnection: close\\r\\nCache-Control: private\\r\\n\\r\\n" ); v10 = 0 ; v9 = (const char *)malloc (10240 ); if ( v9 ) { memset (v9, 0 , 10240 ); v10 = malloc (51200 ); if ( v10 ) { memset (v10, 0 , 51200 ); for ( i = 0 ; i < 128 ; ++i ) { if ( *(_DWORD *)(a1 + 8 * (i + 36 )) && *(_DWORD *)(a1 + 8 * (i + 36 ) + 4 ) ) { memset (v9, 0 , 10240 ); strcpy (v9, *(_DWORD *)(a1 + 8 * (i + 36 ))); if ( sub_4233B0(v9) ) { printf ("ParseCMD error cmdlines:%s\\n" , v9); v11 = -1 ; goto LABEL_18; } if ( strstr (v9, "FillMacCloneMac" ) ) { strcat (v9, " " ); strcat (v9, a1 + 48 ); } printf ("cmd%d:%s\\n" , i, v9); v12 += sub_423280(v9, v10 + v12, 51200 ); } } if ( v12 > 0 ) { v3 = strlen (v14); sub_41F734(a1, v14, v3); sub_41F734(a1, v10, v12); } puts ("---------------------websdone start" , v1, v2); sub_41FBA4(a1, 200 ); puts ("---------------------websdone end" , v4, v5); } else { printf ("websExcuteShellHandler: not enough memory (1)\\n!" ); v11 = -1 ; } } else { printf ("websExcuteShellHandler: not enough memory (0)\\n!" ); v11 = -1 ; } LABEL_18: free (v9); free (v10); puts ("---------------------free end" , v6, v7); return v11; }
Check if the request parameters are valid, if valid, it will retrieve the command specified in the request.
If the command exists, execute the command directly.
sub_404940
function is websDefaultHandler
, handle default URL request, including asp pages.
Vulnerability Command Injection The fist Command Injection vulnerability is as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int __fastcall sub_42383C ( ...... { ...... for ( dword_58A6C4 = (int )&off_588D80; *(_DWORD *)dword_58A6C4; dword_58A6C4 += 8 ) { if ( strstr (*(_DWORD *)(a1 + 1316 ), *(_DWORD *)dword_58A6C4) ) { memset (v17, 0 , sizeof (v17)); snprintf (v17, 4999 , "echo '%s' >/var/hnaplog" , a11); system(v17); printf ("wp->hnapfunc===========>%s\\n" , *(const char **)(a1 + 1316 )); if ( !strncmp (*(_DWORD *)dword_58A6C4, "GetLocalMac" , 11 ) ) { memset (&qword_58A6A0, 0 , 32 ); strncpy (&qword_58A6A0, a1 + 48 , 32 ); } ....... }
In the previous HNAP handing function, there was a logging implementation that utilized echo to store POST data in /var/hnaplog
, but there was no restriction on the POST data which led to a command injection vulnerability.
The second Command Injection vulnerability is as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int __fastcall sub_4234CC (int a1) { ...... for ( i = 0 ; i < 128 ; ++i ) { if ( *(_DWORD *)(a1 + 8 * (i + 36 )) && *(_DWORD *)(a1 + 8 * (i + 36 ) + 4 ) ) { memset (v9, 0 , 10240 ); strcpy (v9, *(_DWORD *)(a1 + 8 * (i + 36 ))); if ( sub_4233B0(v9) ) { printf ("ParseCMD error cmdlines:%s\\n" , v9); v11 = -1 ; goto LABEL_18; } if ( strstr (v9, "FillMacCloneMac" ) ) { strcat (v9, " " ); strcat (v9, a1 + 48 ); } printf ("cmd%d:%s\\n" , i, v9); v12 += sub_423280(v9, v10 + v12, 51200 ); ...... }
In the EXCU_SHELL
handing function, command can be passed in, and the commands will be executed by the sub_423280
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 int __fastcall sub_423280 (int a1, int a2, unsigned int a3) { int v3; int v4; int v6; int v7; int v8; int v9; if ( strstr (a1, "apply" ) ) { sub_421468(a1); return 1 ; } else if ( a3 < 0xC801 ) { v8 = popen(a1, "r" ); if ( v8 ) { v9 = fread(a2, 1 , a3, v8); pclose(v8); if ( v9 > 0 ) *(_BYTE *)(a2 + v9 - 1 ) = 10 ; return v9; } else { puts ("error" , v6, v7); return 0 ; } } else { puts ("Error: Invalid length" , v3, v4); return 0 ; } }
Reset password vulnerability This vulnerability could allow resetting the password to empty, it occurs in the SetMultipleActions
function and SetPasswdSettings
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __fastcall sub_4339E8 (const char *a1) { ...... } mxmlNewText(v12, 0 , "OK" ); for ( dword_58AA80 = (int )&off_589200; *(_DWORD *)dword_58AA80; dword_58AA80 += 8 ) { if ( mxmlFindElement(String, String, *(_DWORD *)dword_58AA80, 0 ) ) { printf ("pHnapFunction->name=%s\\n" , *(const char **)dword_58AA80); (*(void (__fastcall **)(const char *))(dword_58AA80 + 4 ))(v21); if ( !strncmp (*(_DWORD *)dword_58AA80, "SetAccessCtlList" , 16 ) || !strncmp (*(_DWORD *)dword_58AA80, "SetAccessCtlSwitch" , 18 ) ) { v17 = 1 ; } } } ...... }
In the SetMultipleActions
function, parse the SOAP request xml, then traverse the function list, if the matching function is found, the corresponding function will be executed.
If I used the SetPasswdSettings
function, it would execute that function. The SetPasswdSettings
function code is as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 int __fastcall sub_43D518 (int a1) { ...... Element = mxmlFindElement(String, String, "NewPassword" , 0 ); if ( Element ) { Text = mxmlGetText(Element, 0 ); if ( Text ) { v26 = sub_45B320(Text); if ( v26 ) { sub_4268DC(v26, v31); if ( (unsigned int )strlen (v31) < 0x40 ) { if ( !apmib_set(183 , v31) ) goto LABEL_9; v25 = "OK" ; } else { puts ("user password length is error!" , v10, v11); v25 = "ERROR" ; } } else { if ( !apmib_set(183 , &dword_4A7540) ) { LABEL_9: puts ("apmib_set MIB_USER_PASSWORD is error" , v8, v9); v25 = "ERROR" ; goto LABEL_17; } ...... }
NewPassword will be passed to the sub_45B320
function, which is used for decryption. If decryption failed, apmib_set
will be used to set the password to empty.
Therefore, login with empty password is possible by triggering SetPasswdSettings
through SetMultipleActions
function or directly triggering SetPasswdSettings
function.
Stack Overflow In the SetPasswdSettings
function, NewPassword will be passed to the sub_45B320
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 char *__fastcall sub_45B320 (int a1) { ...... { memset (&byte_58ADEC, 0 , 128 ); sub_425ED0((int )"decrypt" , (int )&byte_58A770[33 * i], a1, (int )&byte_58ADEC); if ( byte_58ADEC ) { sub_4267F8(&byte_58ADEC, &unk_58AD6C); return (char *)&unk_58AD6C; } } } } } ...... }
Then call the sub_425ED0
function, with parameters passed to the sub_425CB0
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int __fastcall sub_425ED0 (int a1, int a2, int a3, int a4) { v12 = a1; v11 = a2; v10 = a3; ...... if ( strcmp (v12, "decrypt" ) ) { puts ("method error!" , v5, v6); return 1 ; } v9 = sub_425CB0(v11, v10, (int )v13); ...... }
In sub_425CB0
, it also passed to the sub_425830
function.
1 2 3 4 5 6 7 8 int __fastcall sub_425CB0 (int a1, int a2, int a3) { ...... memset (v7, 0 , sizeof (v7)); if ( sub_425830((const char *)a2, v6) == 64 ) { ...... }
In sub_425830
, there was no restriction on a1, which ultimately led to the occurrence of stack overflow.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 unsigned int __fastcall sub_425830 (const char *a1, char *a2) { char v3; unsigned int i; unsigned int j; unsigned int v6; char v7[8 ]; v6 = strlen (a1); if ( (v6 & 1 ) != 0 ) { fprintf (stderr , "[ERROR] %s:%u: input length error:%u\\n" , "dcipher.c" , 220 , v6); return 0 ; } else { for ( i = 0 ; i < v6; ++i ) { v3 = a1[i]; if ( v3 < 48 || v3 >= 58 && v3 < 65 || v3 >= 71 && v3 < 97 || v3 >= 103 ) { fprintf (stderr , "[ERROR] %s:%u: input string error: %s\\n" , "dcipher.c" , 232 , a1); return 0 ; } } for ( j = 0 ; j < v6; j += 2 ) { v7[0 ] = a1[j]; v7[1 ] = a1[j + 1 ]; v7[2 ] = 0 ; a2[j >> 1 ] = strtol(v7, 0 , 16 ); } return v6 >> 1 ; } }
summary There are so many other vulnerabilities similar to the one analyzed here, so I won’t analyze them further.