DIR-823G Equipment Analysis

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.

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

......
#boa

goahead &

......
if [ "$MODE" = "HW_FACTORY_MODE=1" ];then
telnetd&
fi
#telnetd&
ifconfig wlan1-va3 down
speedcheck&
#wiair_client&

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; // $v0
int v2; // [sp+28h] [+28h]
int v3; // [sp+2Ch] [+2Ch]
int v4[4]; // [sp+30h] [+30h] BYREF
char v5[128]; // [sp+40h] [+40h] BYREF
char v6[128]; // [sp+C0h] [+C0h] BYREF

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; // [sp+34h] [+34h]
int v17; // [sp+38h] [+38h]
int v18; // [sp+40h] [+40h]
char v19[104]; // [sp+4Ch] [+4Ch] BYREF
int v20; // [sp+B4h] [+B4h] BYREF
char v21[5000]; // [sp+B8h] [+B8h] BYREF

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); // $a0
int v13; // [sp+1Ch] [+1Ch]
const char *v14; // [sp+1Ch] [+1Ch]
_BYTE *v15; // [sp+20h] [+20h]
int v16; // [sp+24h] [+24h]
char v17; // [sp+28h] [+28h] BYREF
_BYTE v18[3]; // [sp+29h] [+29h] BYREF

++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; // $v0
int v13; // $v0
int v14; // [sp+24h] [+24h]
int v15; // [sp+28h] [+28h]
int v16; // [sp+2Ch] [+2Ch]
int v17; // [sp+30h] [+30h]
int v18; // [sp+30h] [+30h]
_DWORD *k; // [sp+34h] [+34h]
int v20; // [sp+38h] [+38h]
_DWORD *v21; // [sp+3Ch] [+3Ch]
int v22; // [sp+40h] [+40h]
_BYTE *v23; // [sp+44h] [+44h]
_BYTE *v24; // [sp+44h] [+44h]
int i; // [sp+44h] [+44h]
int v26; // [sp+48h] [+48h]
int v27; // [sp+4Ch] [+4Ch]
int j; // [sp+50h] [+50h]
_DWORD *v29; // [sp+54h] [+54h]
char v30; // [sp+58h] [+58h] BYREF
_BYTE v31[3]; // [sp+59h] [+59h] BYREF
char v32[256]; // [sp+158h] [+158h] BYREF
int v33; // [sp+258h] [+258h] BYREF
char v34[20]; // [sp+260h] [+260h] BYREF
int v35; // [sp+274h] [+274h]

++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; // $a1
int v10; // $a2
int v12; // [sp+18h] [+18h]
int v13; // [sp+1Ch] [+1Ch]
int v14; // [sp+20h] [+20h]
int v15; // [sp+24h] [+24h]
int v16; // [sp+28h] [+28h]

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

  • it will return an error

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; // $a1
int v2; // $a2
int v3; // $v0
int v4; // $a1
int v5; // $a2
int v6; // $a1
int v7; // $a2
const char *v9; // [sp+1Ch] [+1Ch]
int v10; // [sp+20h] [+20h]
int v11; // [sp+24h] [+24h]
int v12; // [sp+28h] [+28h]
int i; // [sp+2Ch] [+2Ch]
char v14[104]; // [sp+30h] [+30h] BYREF

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; // $a1
int v4; // $a2
int v6; // $a1
int v7; // $a2
int v8; // [sp+18h] [+18h]
int v9; // [sp+1Ch] [+1Ch]

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; // [sp+20h] [+20h]
unsigned int i; // [sp+24h] [+24h]
unsigned int j; // [sp+24h] [+24h]
unsigned int v6; // [sp+28h] [+28h]
char v7[8]; // [sp+2Ch] [+2Ch] BYREF

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.