SHCTF-WEB-WP-[Week2]-[部分]-[未完待续]

没时间而且太菜了,week2差了两道题没写qwq

[Week2]自助查询

[Week2]登录验证

考点 `JWT(JSON Web Token)`

思路: 爆破私钥 / none攻击

<font style="color:rgb(51, 51, 51);">JWT</font>由三部分组成,分别是<font style="color:rgb(51, 51, 51);">Header</font><font style="color:rgb(51, 51, 51);">Payload</font><font style="color:rgb(51, 51, 51);">Signature</font>

该三部分均为<font style="color:rgba(0, 0, 0, 0.75);">Base64Url</font>编码

拓展,JWT不是base64编码,而是BASE64URL编码

BASE64URL编码的流程:先base编码,再去除尾部=,把”+”替换成”-“ ,把”/“替换成”_”

随手一输admin/admin 提示 你不是真正的admin 看数据包发现

token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNDA4MjIsImlhdCI6MTcyOTEzMzYyMiwibmJmIjoxNzI5MTMzNjIyLCJyb2xlIjoidXNlciJ9.gR1jjFXhYj2uah0JKPogKlq5nK_AJbeadaVx9OhAVg0

疑似JWT拿到jwt.io解一下

很好,是HS256对称加密(RS256 ES256 都是非对称加密)一旦我们拿到密钥,就可以伪造签名了

下面进行密钥爆破

成功得到key 下面进行伪造

即可

[Week2]guess_the_number

F12查看到`/s0urce`路由,下载源码`app.py`
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
import flask
import random
from flask import Flask, request, render_template, send_file

app = Flask(__name__)

@app.route('/')
def index():
return render_template('index.html', first_num = first_num)

@app.route('/s0urce')
def get_source():
file_path = "app.py"
return send_file(file_path, as_attachment=True)

@app.route('/first')
def get_first_number():
return str(first_num)

@app.route('/guess')
def verify_seed():
num = request.args.get('num')
if num == str(second_num):
with open("/flag", "r") as file:
return file.read()
return "nonono"

def init():
global seed, first_num, second_num
seed = random.randint(1000000,9999999)
random.seed(seed)
first_num = random.randint(1000000000,9999999999)
second_num = random.randint(1000000000,9999999999)

init()
app.run(debug=True)

发现是一个猜数字的问题,必须让第一个数等于第二个数,涉及伪随机数问题,由于没学过于是开始疯狂学习

<font style="color:rgb(31, 35, 40);">random</font>是python的一个随机数库,可用于生成伪随机数

<font style="color:rgb(31, 35, 40);">random.random()</font>随机生成一个0到1之前的浮点数

1
2
import random
print(random.random())

<font style="color:rgb(31, 35, 40);">random.randint(1,100)</font> 随机生成一个1到100之间的整数

1
2
import random
print(random.randint(1,100))

设置随机数种子random.seed(10)

1
2
3
4
5
6
7
import random
random.seed(1)
print(random.randint(1,100))
print(random.randint(1,100))
print(random.randint(1,100))
print(random.randint(1,100))
print(random.randint(1,100))

一旦随机数种子被确认,那么输出的随机数 生成序列 也就确认了,我们可以根据随机数种子,推出生成的随机数是什么

在伪随机数生成器(PRNG)中,一个种子并不只生成一个随机数。相反,它生成的是一个伪随机数序列。每次调用 random.randint() 或其他类似方法时,伪随机数生成器会根据当前的内部状态生成下一个随机数。因此,给定相同的种子,可以生成一系列相同的随机数

问题是,尽管不同的种子输出的生成序列不同,但是不同的种子可能输出的第一个值相同,但是这种可能性比较小

综上,现在我们已知种子的范围,只需要写一个程序爆破即可

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

# 已知的 first_num
known_first_num = 6296819177

# 定义种子的范围
seed_min = 1000000
seed_max = 9999999

def find_seed(known_first_num, seed_min, seed_max):
for seed in range(seed_min, seed_max + 1):
# 用当前种子设置随机数生成器
random.seed(seed)

# 生成一个随机数
first_num = random.randint(1000000000, 9999999999)

# 检查生成的随机数是否与已知的相同
if first_num == known_first_num:
print(f"找到了种子: {seed}")
return seed

print("未找到匹配的种子")
return None

# 调用函数查找种子
found_seed = find_seed(known_first_num, seed_min, seed_max)

找到了种子1364726

1
2
3
4
import random
random.seed(1364726)
print(random.randint(1000000000, 9999999999))
print(random.randint(1000000000, 9999999999))

得到结果8239362449

参考资料

https://research.qianxin.com/archives/1357

https://github.com/MisakiKata/python_code_audit/blob/master/%E4%BC%AA%E9%9A%8F%E6%9C%BA%E6%95%B0.md

[Week2]入侵者禁入

知识点:Python Flask Session伪造+Ssti模板注入

总结

自己三方面的能力不足
  1. 知识面、技术栈太局限,应该拓展一下,了解更多的漏洞
  2. 编写脚本的能力、开发能力不足,线下不出网环境就寄了
  3. 完善武器库

攻击面收敛

JWT相关
  1. 敏感信息泄露
  2. none 无签名
  3. 弱密钥