首页
论坛
课程
招聘
[原创]看雪.京东 2018CTF 第九题 PWN-羞耻player Writeup
2018-7-4 03:22 1570

[原创]看雪.京东 2018CTF 第九题 PWN-羞耻player Writeup

2018-7-4 03:22
1570

后面有时间再修改更新下,里面可能有错误。

这是一个堆漏洞的利用题,c++编写。漏洞点有三处:一个逻辑错误导致的UAF:新申请的堆先赋值给了指针,再释放指针指向的旧堆,实际是释放了新申请的堆,释放与赋值的顺序颠倒出现了悬挂指针,UAF得以实现,但是由于PIE,不能单独直接利用;二是没有检查输入的表示大小的数为负数,导致heap overfolw;三是打印函数多打印一个字节,可以按字节泄露信息。

 

##程序分析
先checksec:

[*] '/usr/lib/x86_64-linux-gnu/libstdc++.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

程序运行情况如下:

#    #                     #     #                               ####   #######  ####### 
 #   #                       #   #                               #    #     #     #       
 #  #      ######  # ####     # #    #     #   #####            #           #     #       
 ###      #     #  ##    #     #     #     #  #     #           #           #     #####   
 #  #     #     #  #     #    # #    #     #  #######           #           #     #    
 #   #    #    ##  #     #   #   #   #    ##  #                  #    #     #     #       
 #    #    #### #  #     #  #     #   #### #   #####              ####      #     #       
Welcome to KanXue CTF!
Please enter your Recording Name?
pediy 
1. Add Clip
2. Edit Clip
3. Play Clip
4. Remove Clip
>>> 1
Clip Adding
1. Video Clip
2. Audio Clip
3. Subtitle Clip
4. Metadata Clip
>>>

程序模拟了音视频播放器,包括添加、修改、播放、删除包括视频、音频及其相关信息的功能,而且都是通过类完成编写,实际功能类都继承于同一个父类,父类定义大致如下:

class Clip{
    public:
        virtual void clip_add(){ return;}
        virtual void clip_edit(){return;}
        virtual void clip_remove(){return;}
        virtual void clip_play(){return;}
};

然后定义了4个子类Metadata,SubClip,AudioClipVideoClipvtable在偏移203BD0处。

 

漏洞点在VideoClipclip_edit函数中:

  if ( a1->frame_num > 0x400u )
    a1->frame_num = 1024;
  pheap = (char *)operator new[]((unsigned int)a1->frame_num);
  if ( !pheap )
    exit(1);
  a1->p_video_data = pheap;                     // uaf
  if ( a1->p_video_data )
    operator delete[](a1->p_video_data);
  std::operator<<<std::char_traits<char>>(&std::cout, "Video Data : ");
  a1->frame_num = read(0, a1->p_video_data, (unsigned int)a1->frame_num);

信息泄露可以利用播放功能。VideoClipAudioClipclip_play函数能打印经异或编码后的出信息:

v1 = std::operator<<<std::char_traits<char>>(&std::cout, "Playing video...");
std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
for ( i = 0; a1->frame_num >= i; ++i )
std::operator<<<std::char_traits<char>>(&std::cout, (unsigned int)(char)(a1->p_video_data[i] ^ 0xCC));
return std::ostream::operator<<(&std::cout, &std::endl<char,std::char_traits<char>>);



for ( i = 0; a1->length >= i; ++i )
std::ostream::operator<<(&std::cout, (unsigned __int8)(a1->p_audio_data[i] ^ 0x55));
return std::ostream::operator<<(&std::cout, &std::endl<char,std::char_traits<char>>);

play这里,也有问题,多打印了一个字节。

 

程序用到的数据结构如下:

00000000 struc_video     struc ; (sizeof=0x50, align=0x8, mappedto_7)
00000000 p_method        dq ?                    ; offset
00000008 Resolution      dq ?
00000010 fps             dd ?
00000014 frame_num       dd ?
00000018 p_video_data    dq ?                    ; offset
00000020 description     db 48 dup(?)
00000050 struc_video     ends
00000050
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 struc_audio     struc ; (sizeof=0x48, align=0x8, mappedto_8)
00000000 p_method        dq ?                    ; offset
00000008 bitrate         dd ?
0000000C length          dd ?
00000010 p_audio_data    dq ?                    ; offset
00000018 description     db 48 dup(?)
00000048 struc_audio     ends
00000048
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 struc_subtitle  struc ; (sizeof=0x18, align=0x8, mappedto_9)
00000000 p_method        dq ?
00000008 lang            dd ?
0000000C length          dd ?
00000010 p_subtitle      dq ?
00000018 struc_subtitle  ends
00000018
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 struc_meta      struc ; (sizeof=0x48, align=0x8, mappedto_10)
00000000 p_method        dq ?
00000008 date            db 32 dup(?)
00000028 owner           db 32 dup(?)
00000048 struc_meta      ends

其中的p_method指向各自的vtable

 

另外程序开始还申请了256随机大小的堆并进行了随机释放。

利用思路

先跑空bins,然后利用fastbin进行堆地址的泄露,再利用small bin进行libc基址的泄露。
然后进行堆喷射将one_gadget地址有效覆盖堆。

 

再结合SubClipclip_add功能改写vtable指针。SubClipclip_add功能中,除了能读取负数的大小外,其还有复制已经存在的subtitle内容的功能。代码如下:

 if ( a1->p_subtitle )
  {
    len = 0;
    dest = 0LL;
    std::operator<<<std::char_traits<char>>(&std::cout, "Subtitle Length : ");
    if ( read(0, &len, 4uLL) <= 0 )
      exit(1);
    if ( a1->length + len > 0x400 )
      len = 1024 - a1->length;
    dest = (void *)operator new[](a1->length + len);
    if ( !dest )
      exit(1);
    std::operator<<<std::char_traits<char>>(&std::cout, "Add Subtitle : ");
    memcpy(dest, (const void *)a1->p_subtitle, (unsigned int)a1->length);
    if ( read(0, (char *)dest + (unsigned int)a1->length, len) <= 0 )
      exit(1);
    if ( a1->p_subtitle )
      operator delete[]((void *)a1->p_subtitle);
    a1->p_subtitle = dest;
  }
  else
  {
    std::operator<<<std::char_traits<char>>(&std::cout, "Subtitle Language : ");
    if ( read(0, &a1->lang, 4uLL) <= 0 )
      exit(1);
    std::operator<<<std::char_traits<char>>(&std::cout, "Subtitle Length : ");
    if ( read(0, &a1->length, 4uLL) <= 0 )
      exit(1);
    if ( a1->length > 0x400u )
      a1->length = 1024;
    a1->p_subtitle = operator new[]((unsigned int)a1->length);
    if ( !a1->p_subtitle )
      exit(1);
    std::operator<<<std::char_traits<char>>(&std::cout, "Add Subtitle : ");
    if ( read(0, (void *)a1->p_subtitle, (unsigned int)a1->length) <= 0 )
      exit(1);
  }

这两个功能配合就能使得堆溢出,从而覆写vtable,最后get shell。


看雪社区年底排行榜,查查你的排名?

最后于 2018-7-4 12:00 被poyoten编辑 ,原因: 前后多次写得有出入
收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回