
第五题 passcode
参考链接
这个链接写的应该算比较详细了,但是它讲小端序那存在一点问题
学习知识点:
看了上面的链接还是有一些地方不明白,再加上这个链接就完全懂了,点击查看链接
代码如下:
本文漏洞利用的点在于在红框标记的地方:
对于scanf函数,在读取单个字符或者int类型的数据时,应该传的是地址指针,而这个题目则缺少&,就会直接修改passcode1和passcode2指向的地址。
scanf读取字符串时才不需要&符号。
因此这里无法通过直接输入passcode1和passcode2的值满足比较条件。
passcode1和name的地址如下图所示,通过观察可以发现,name的地址结尾为:ebp-0x70+100=ebp-0xc
而passcode1的初始地址为ebp-0x10,passcode1有4个字节覆盖了name的值,而scanf缺少指针输入导致在进行读取时实际上修改的是passcode1这个地址存放的值的地址,如下所示:
scanf读取passcode1时是将输入的passcode1写入xxx所在的位置,而非写入passcode1这个位置,因此在这里就实现了一个任意写操作。
因此可以通过name的最后四个字节来修改地址指向。
由于got表为了避免重复,对于一些常用函数都指定一个地址来直接存储该函数的调用,如下所示:
查看got表
objdump -R passcode
得到如下查询结果
由于printf函数被多次调用,因此这里可以直接使用printf函数的地址,在输入name时将存放printf的地址写入name的最后四个字节,当进行passcode输入时将该地址的内容修改为system的函数调用,则在接下来函数的执行中再次调用printf实际执行过程如下:
调用printf函数-》找到printf函数地址(该地址修改后实际存放的是system的函数调用)-》调用该地址所在的函数
此时就可以直接执行system函数。
注意:这里还可以替换成其他后面会调用的函数的地址,如fflush等
使用disas login查看system指令所在地址如下:
因此passcode1的值可以确定为0x080485e3,由于scanf读passcode1读入的是十进制数据,因此这里需要转换成十进制数据来读:
0x080485e3=134514147
构造答案例如:
python -c "print('b'*96 + '\x00\xa0\x04\x08'+'\n'+'134514147'+'\n')" | ./passcode
这里的’\x00\xa0\x04\x08’是printf函数入口地址的小端序0x0804a000。
fflush()函数
该函数有两个参数,
fflush(stdin):该函数功能是清空输入缓冲区,通常是为了确保不影响后面的数据读取(例如在读完一个字符串后紧接着又要读取一个字符,此时应该先执行fflush(stdin)。
fflush(stdout):刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上
同时还需要注意此函数仅适用于部分编译器(如VC6),但是并非所有编译器都要支持这个功能(如gcc3.2)。这是一个对C标准的扩充。
接下来直接运行passcode,然而却直接报错:
Segmentation fault (core dumped)
这里百度说这个错误是内存读入的问题,也就是实际上如果想要通过scanf读取int类型的数据应该是使用以下语句,而它少了一个&
scanf("%d",&passcode1)
而当前passcode1在读取前存放的数据所在的地址可能无法放入数据导致的。
方法2
这里在passcode1实现了任意写以后实际上还有一个方法:
这里可以看到fflush存在一个跳转函数,这里可以直接把跳转地址的内容修改为system所在的位置即可:
构造答案如下:
python -c “print(‘b’*96 + ‘\x04\xa0\x04\x08’+‘\n’+‘134514147’+‘\n’)” | ./passcode
答案
可以看到两种方法都可以得到答案。
第六题 random
代码如下:
这道题突然一下又变得很简单了呀,只需要两个知识点:
- a ^ b ^ a=a ^ a ^ b=b (关于异或的运算)
- random=rand(),其中rand函数如果没有种子的赋值,那么它产生的随机数是固定不变的。也就是这里产生的是一个固定不变的伪随机数。
- 利用以上两点答案就很简单了,key=伪随机数 ^ 0xdeadbeef
使用gdb调试查看伪随机数的值:
可以看到rax中存放的就是产生的伪随机数的值:1804289383
答案也就是:3039230856