Dlink固件:

DIR-2640_REVA_FIRMWARE_1.11B02

binwalk提取固件后将prog.cgi拖入ida分析:

image
在函数sub_481FC8中,可以看到一个变量count,来控制for循环的次数,循环中,使用strcpy函数把haystack复制到ptr中,而count并没有经过验证,所以当count足够大时,可以造成堆溢出。

checksec查看保护:

image
发现啥保护都没开,先在qemu用户态下测试一下,运行prog.cgi
image
大概是有些设备没挂载,所以我们先启动一个chroot容器,运行一下rcS

1
2
chroot . ./qemu-mipsel-static ./bin/ash
cd /etc/init.d && ./rcS

image

虽然有些问题,但还是挂载了一些盘上去,再运行prog.cgi试试

image

报错,经过调试,发现程序在函数sub_42D2DC退出,该函数是一个注册表函数,其在调用函数trace时,会触发ioctl的报错,所以我们把它patch掉,成功进入主循环

image

后来发现这个程序并不是通过网络来通信,而是利用getenv函数,通过环境变量来与web应用通信。而启动整个web应用,作者做过诸多尝试,但还是不行,所以得换个思路。

关于如何让程序崩溃:

需要攻击的代码如下

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
ptr = malloc(0xFFF0u);
if ( ptr )
{
memset(ptr, 0, 2184 * v45);
Count = webGetCount(a1, "/SetScheduleSettings/ScheduleInfoLists#");
if ( !Count )
Count = 0;
for ( i = 0; i < Count; ++i )
{
memset(v50, 0, sizeof(v50));
snprintf(v50, 0x200u, "/SetScheduleSettings/ScheduleInfoLists:%d/%s", i, "ScheduleName");
haystack = (char *)webGetVarString(a1, (int)v50);
if ( !haystack )
{
v26 = '\v';
goto LABEL_87;
}
if ( !*haystack )
{
sub_4812CC(v45);
goto LABEL_78;
}
strcpy((char *)ptr + 0x888 * i + 4, haystack);
...}
}
...

我们需要让循环跑到strcpy那里,需要满足两个约束:webGetVarString返回有效值*haystack不为空,只要满足这两个条件,就会在循环中调用strcpy函数,将haystack中0x888长度的内容copy到ptr中;除此之外,我们要让循环继续执行下去,往下看看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
memset(v50, 0, sizeof(v50));
snprintf(v50, 0x200u, "/SetScheduleSettings/ScheduleInfoLists:%d/ScheduleInfo#", i);
v46 = webGetCount(a1, v50);
if ( strstr(haystack, "-UH1xLTJa7O") )
{
v37 = 0;
v52[0] = ++v36 + 1;
v35 = 1;
strncpy((char *)&v52[761 * v36 + 2], haystack, 0x10u);
v52[761 * v36 + 1] = 0;
}
else
{
v35 = 0;
}
if ( v46 ){
...
}
...

为了简化模型,我们控制v46为0,不进入if分支,后续进入else后程序会进入下一次循环,这样我们的目的就达到了。

调试方法:

将qemu-mipsel-static复制到固件根目录下,chroot启动容器,运行以下命令

1
2
3
4
5
cp /usr/bin/qemu-mipsel-static .
chroot . ./qemu-mipsel-static ./bin/ash
cd /etc/init.d && ./rcS
cd / && ./qemu-mipsel-static -g 1234./bin/prog.cgi

再另起一个终端,启动gdb-multiarch

1
2
3
4
5
6
gdb-multiarch
#注意,作者使用了gdb的插件pwndbg,使用gef应该也可以
pwndbg>set architecture mips
pwndbg>file ./bin/prog.cgi
pwndbg>target remote localhost:1234
#然后断点打到main函数里面

hook到sub_481FC8函数:

编写一个main_hook.so,利用libc_start_main函数,hook到sub_481FC8函数,随意编写一个输入,count定为64,以下是seeds.bin:

1
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

io钩子函数如下
image
64个大写的A,先把LD_PRELOAD设为main_hook.so,运行以下指令:

1
2
chroot . ./qemu-mipsel-static -L . -E LD_PRELOAD=/lib/libdl.so.0:./stack_hook.so -g 1234 ./bin/prog-cgi seeds.b
in > log.txt

往下调试发现报了一个段错误:
image
检查发现是haystack这个字符串把下面的ptr给覆盖了,所以在解引用ptr时,出现了报错,简单来说,这里有个缓冲区溢出,并且能够实现任意地址写的原语。
栈结构如下:
image