1.题目获取
题目下载地址:点此下载
2.查保护
3.分析程序
IDA载入,查看main函数
这个函数读取一个名字,最长32个字节。
作者名被读到data段
sub_A89()打印程序功能,并获取用户输入的序号
sub_F55()的功能是添加书籍信息,在这个函数中可以分析到存储这些数据的结构体。
我们可以看到这里的v4是存放在off_202010+v3处,其他的参数又存在v4中。
从这里我们可以得知v2是description的长度,v6是description,ptr是书名;unk_202024没有找到相关的,需要动态调试,但是现在关键问题是如何定位v4这个结构体,我们可以发现v4是放在off_202010 + v3处,off_202010在我们的作者名存放处off_202018前面。所以可以通过搜索字符串定位。
定位到作者名在0x555555756040处
第一个红框框的是作者名,第二个红框中的值应该就是v4那个结构体了。
*((_DWORD *)v4 + 6) = v2;
*((_QWORD *)off_202010 + v3) = v4;
*((_QWORD *)v4 + 2) = v6;
*((_QWORD *)v4 + 1) = ptr;
*(_DWORD *)v4 = ++unk_202024;
猜测unk_202024是书的id,unk_202024指向bss段,这个地址应该是被初始化为0,然后这里++,所以第一个书的id为1;
ptr是书名;v6是description;v2是description的长度;
之前我看一位大佬写的wp,他提出了个疑问,我正好遇到,就写下;大佬在文章中写道
这里有一点疑问,为什么存的时候是*((_DWORD *)v4 + 6) = v2;而真正的内存里面却是把0x0000000000000014放到了+3处。
这里需要注意v4前面的_DWORD和_QWORD;_DWORD代表4字节,_QWORD代表8个字节;
*((_DWORD *)v4 + 6) = v2;前面是_DWORD,四个字节
至此,程序中的结构体分析完毕。
4.地址泄露
程序编写的获取用户输入函数存在 null byte off-by-one 漏洞,仔细观察这个获取用户输入函数可以发现对于边界的考虑是不当的。
第一个红框框的是作者名,第二个红框是v4那个结构体;获取用户输入函数对于边界的考虑是不当的,造成越界,会把字符串截断字符\x00越界写入到0x555555756060处,当我们使用程序添加书籍的功能的时候会把book1结构体的指针写到0x555555756060处,会覆盖\x00这个截断符,当我们打印作者名的时候可以泄露出book1结构体位置的指针。也可以二次修改作者名,把0x555555756060处再次覆盖为\x00,这样会修改程序的book1结构体位置的指针的值。
泄露地址
修改地址
第一个字节被覆盖为\x00。
5.伪造结构体
重新调试
假设二次修改作者名,book1结构体位置的指针的第一个字节覆盖为\x00时0x0000