2019-08-15 | UNLOCK

2019-8-15-pwn静态编译题

2019-8-15

静态编译的解决-制作符号表并在ida中导入符号表

8月15号看同事打一个比赛,发现是一道没做过的静态编译题,就顺便做来学习一下

第一个难点:静态编译,无符号表,难以理解程序
解决方法:
将sig文件放到ida目录中的/sig/PC,然后在IDA中按ALT+(数字键)3能够添加符号表文件
符号表可以在github上下到,比如https://github.com/push0ebp/sig-database
但经过我实际体验,没太大作用,大部分函数还是找不到对应的符号,所以最好还是用工具自己做,ida一般在目录下会自带名字叫flair70.zip的包,如果没有可以自己下一个,在https://www.jianshu.com/p/7a1441e4f355(IDA 制作 sig文件 && gdb 导入符号表)就有下载链接,符号表的制作也能参考这篇文章。

ida添加sig

在途中func就表示匹配到了多少个函数,这样一来原本都是subxxxx显示的函数大部分被还原了,尤其是做堆题最重要的free和malloc

寻找漏洞点-off by one

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // edx
int *v5; // ecx

sub_8048AD1();
_IO_puts("You know all, Please input:");
while ( 1 )
{
while ( 1 )
{
v3 = read();//输入数字,选择功能
if ( v3 != 2 )
break;
sub_80489E6();//delete删除
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
sub_8048A53();//edit修改
}
else if ( v3 == 4 )
{
exit(v4, v5, 0);
}
}
else if ( v3 == 1 )
{
sub_804890B(v5, v4);//add添加
}
}
}

这个程序就三个功能,添加、修改、删除,漏洞点在修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int sub_8048A53()
{
int v0; // edx
int *v1; // ecx
char *v2; // edx
int result; // eax
int v4; // [esp+Ch] [ebp-Ch]

v4 = read();//输入数字,选择修改第几项
if ( !sub_80489B1(v4) )
exit(v0, v1, 0);
sub_804887C((int)(&s)[v4 + 16], (int)(&s)[v4]);//(&s)[v4]存储的是长度,(&s)[v4 + 16]是chunk的地址,这个函数是一个一个字节往(&s)[v4 + 16]指向的位置写入内容
v2 = (char *)strlen((&s)[v4 + 16]);//漏洞点,产生了off by one
result = v4;
(&s)[v4] = v2;
return result;
}

其中在修改表项的时候有一个off by one的漏洞,这是因为在修改内容的时候,如果chunk中context的内容是满的,就会与下一个chunk的size相连,这时候调用strlen,会把size那一位也算进去,导致最后长度加一,这样子再次调用sub_8048A53函数的时候就可以修改size的一个字节了。

overlap

能修改size位后,就能利用overlap,也就是让一个chunk的大小刚好覆盖自己本身和下一个chunk,这样再次调用sub_8048A53,就能直接修改下一个chunk的fd

fastbin有一个特性,在malloc的时候将fastbin中的chunk取出来,操作系统会检查它的fd,如果合法,就会把fd添加到fastbin的链表中,这样下次再malloc时,就能把fd指向的位置作为chunk。

我们在构造假fd时就需要一个有着合法size的地址,这时候&s第一位就是最合适的选择了,因为我们能控制它的大小(第一个表项的大小),而且它所在的4个字节除了size位都是空的,能作为一个合法的假chunk。

但是我随后发现这样很不方便,因为我们接下来的目的是要把假chunk设到表项中,从而调用sub_8048A53修改表项

修改freehook

表项

&s就是0x80eba00,可以看到前64个字节存储的是16个长度(那些0x61是通过上一步的overlap设置好假chunk后调用sub_8048A53修改的),红色框里是我修改的,它是freehook的地址

在libc中的free()函数会先检查__free_hook参数,如果__free_hook参数为0,则执行free,如果__free_hook不为0,则return __free_hook

freehook

在这道题中,freehook就是固定在0x80eb4f0,所以我们修改这里的值,就能控制程序流了

shellcode

最后我们写入shellcode,把freehook的值改成shellcode所在位置然后调用free就能获得shell了。

IO_FILE

除了修改freehook的方法,还能修改IO_FILE,具体还在学习,下次专门做一道IO_FILE的题目再具体学习吧。在pwndbg中可以查看IO_FILE结构题:

1
2
3
p *(_IO_FILE *)stdin
p *(struct _IO_FILE_plus *) stdin
p *((struct _IO_FILE_plus *) stdin).vtable

远程gdb调试技巧

另外为了利用我闲置已久的阿里云服务器,学了下纯命令行下gdb远程调试技巧

写python脚本的时候使用pwntool的context模块
attach的时候会在tmux中打开新窗口

1
2
context(os='linux',arch='amd64')
context.terminal = ['tmux', 'splitw', '-h']

评论加载中