2015年第一帖。
以往的CTF的题目质量还是很高的,所以打算稍微练一练这些题目。
这道题目,存在一处format string attack。主要是在strncpy
后添加结尾的null
字符时,如果长度足够,就会把之后用到的格式字符串地址最低byte设为0,而那里正好指向我们提供的内容,于是接下来的sprintf
造成漏洞。
但是,在开始时会先检查我们提供的内容是否包括字符n
,这就使得%n
这种方式不可用了。于是format string就只能用来读内存。另一方面,由于sprintf
的目标地址在栈上,所以这里可以溢出覆盖返回地址。由于有canary保护,我们需要读canary的内容,以用来后面溢出能通过检查。
这道题有一个与以往不同之处,就是可执行文件是PIE的,即程序自身的代码地址也会变化,而且以往那样直接在GOT固定位置得到函数地址的方法行不通了。比如说,通过调试发现,call mmap
在我的机子上会成为调用相对eip
偏移。
于是我们还需要读内存,得到.text
的基地址。幸运的是,在栈上发现了指向某条指令的地址,于是通过读那里的内容,就可以获得.text
段的地址了。进一步,通过读取call mmap
指令处的偏移,就可以得到函数mmap
的地址。
获得这些需要的地址后,接下来需要溢出改返回地址了。而不幸的是,发现canary的最低byte总是0,这就导致strncpy
时会截断,格式字符串不完整了。后来发现,由于在ret
之前有好几轮sprintf
,于是可以先使用错误的canary,即最低byte非0而其他位正确,然后利用sprintf
会把结尾的byte设为0,再一次溢出并只修改canary的最低位成为0。这样在最后的ret
时canary就符合要求了。
同样地,在后面调用mmap
时,参数也有很多null
字符,这里也用和canary一样的思路写0到栈。具体调用时,尝试发现有些参数里不包括0也是可以的,比如flag
。如下的调用
mmap(0x11111000, 0x01011000, 0x01010107, 0x01010122, -1, 0x01011000);
就会映射出
0x11111000 0x12122000 rwxp mapped
综上,大概思路就是,先得到一段rwx
的指定区域,然后read
把shellcode读到那里,最后跳到那里执行。
我还考虑过这道题能不能利用内存泄露用pwntools得到system
函数的地址,然后调用的方式。但那样的话还需要把/bin/sh
放到指定的某处,而且限制16次可能不够找system
,还需要再返回main来一遍。后来就没有试了。