de1ctf Mimic_Note
前言
第二题做的我又自闭了,想想七哥和姚老板比赛当日拿这么难的方法抢到了三血真是太厉害了,后来看官方的wp说程序的mimic_server并不是要求同时跑通64和32,而是只要输出一致即可,所以程序考察的是不泄露libc的ROP,可以用re2dl_resolve做,但是学长的exp是直接实现了一个拟态的exp,里面用到了很多我找都没找到的gadgets。。最重要的是拟态题的数据错位输入和一些同步触发的要求,这题自己也做不出,只能看着学长的exp慢慢调试,感觉收获了很多。
程序逻辑
libc2.23,程序可以new、edit、free、其中edit有一个off-by-one,固定多输入一个零字符。got表可写。
漏洞利用
基本思路是unlink之后修改atoi为某个gadgets触发栈迁移,之后把write@got改成syscall。具体实现过程中要注意修改atoi的操作要32和64同步进行,顺序修改的时候会引发异常(比如修改32的atoi再修改64的aoti在32位运行时第二次修改已经不能正常使用edit)。
为了解决同时修改atoi:32和64在heap_list各占据一块位置,unlink后32位在heap_list_0处放p32(atoi_got_32),64位在heap_list_0处方p64(atoi_got_64-8),这样一次Edit(p32(gadgets_32)+p32(0)+p64(gadgets_64))即可同步修改两个atoi。
另一个问题是如何在一次输入数据时候触发两个ROP(顺序触发ROP还是之前说的问题,atoi不能正常使用之后第二次的edit是不能用的)。这个在stkof里我们有经验,要利用数据的错位,比如32位里可以直接pop ebp;leave ret;而64位可以把前8字节数据pop到rsi再pop rbp,leave ret;这样(即使这样,32位的栈迁移gadgets也没有找到)。
一些细节:因为64位要输入两次数据,32位输入一次数据,我们可以把32位的输入放后面并且在32的ROP里加入前两次输入,而64不需要管这些,后面拿到shell之后send一次垃圾数据也无所谓。
unlink之后覆写不要挑free的块写,因为在另一个arch下这里是不能写的。
还有ROP本身的问题,64bit ROP通过read设置rax,之后通过csu拿shell,但是32bit始终做不到ebx=binsh_addr的时候ecx=edx=0,看了17的exp发现并不是通过read设置的eax,而是通过先pop ebp设置ebp = 0x2c+0xb,后用lea eax,[ebp-0x2c]设置eax=0xb,在此之前read(0,0,0)将eax,ebx,ecx,edx全部清空,从而构造完成,注意之后还要call一次atoi,这时atoi已经被改为gadgets,因此要再迁回bss上,最后p_ebx再syscall即可。
关键gadgets:
32atoi->gadgets:(found by 17)
1
2
3
4
5
6
7
8
9
10
11text:080489EE add esp, 10h
.text:080489F1 cmp edi, esi
.text:080489F3 jnz short loc_80489D8
.text:080489F5
.text:080489F5 loc_80489F5: ; CODE XREF: __libc_csu_init+2E↑j
.text:080489F5 add esp, 0Ch
.text:080489F8 pop ebx
.text:080489F9 pop esi
.text:080489FA pop edi
.text:080489FB pop ebp
.text:080489FC retn64atoi->gadgets:
1
0x0000000000400c2f : pop rbp ; pop r14 ; pop r15 ; ret
32栈迁移gadgets->leave_ret_32
64栈迁移gadgets:(注意pop rsp之后就迁移到bss了)
1
0x0000000000400c2d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
32设置eax
1
2
3.text:08048907 lea eax, [ebp-2Ch]
.text:0804890A push eax ; nptr
.text:0804890B call _atoi
exp.py
1 | #coding=utf-8 |
后言
花了两天时间才差不多搞懂,要再膜一些17学长和姚老板,路漫漫其修远其,吾将上下而求索。