某招标投标平台滑块逆向

某通信工程建设项目招标投标管理信息平台滑块逆向

地址链接aHR0cHM6Ly90eHpicXkubWlpdC5nb3YuY24vIy9nYXRld2F5L2xpc3Q=
目前为最简单的逆向滑块,适合初级入门这上手
  • 触发条件:如图所示点击下一页或者点击页数触发滑块

  • 请求200参数:滑块通过请求列表页参数如图所示

  • 如上图所看到的一样,code参数是滑块成功通过所返回的值,而token值是请求滑块图片的接口https://txzbqy.miit.gov.cn/zbtb/captcha/slideCaptcha所返回的nonceStr 值,而且所返回的值中滑块的缺口图片的值是加密过的,需要逆向解密,如图所示

逆向步骤

  • 全局搜索code并没有确定出具体的位置,所以code为动态参数,需要跟栈确定位置,还有技巧方法通过解密关键字搜索AES.encrypt确定位置,如图我们看到并无没魔改,没混淆AES的JS算法,我们直接断点看传参,如图所示

  • 再通过canvasSrc关键字看图片的js解密过程,如图所示

整个过程就是:

  • 1、首先请求了滑块图片的url接口返回了数据(滑块图片背景图 blockSrc、滑块图片加密值 canvasSrc、nonceStr的值)
  • 2、使用AES解密算u(i.canvasSrc, i.nonceStr)得到滑块图片
  • 3、使用`ocr获取滑块缺口的距离 distance
  • 4、再使AES用加密算法d(distance, nonceStr)得到code的值

代码实现

#!/usr/bin/env python
# coding=utf-8

from Crypto.Cipher import AES
import base64
from Crypto.Util.Padding import unpad, pad
import ddddocr
import requests
import json


class TxzbqyMiitSlider:

    def __init__(self):
        self.det = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
        self.session = requests.session()
        self.headers = {
            "Accept": "application/json, text/plain, */*",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Connection": "keep-alive",
            "Content-Type": "application/json;charset=UTF-8",
            "Origin": "https://txzbqy.miit.gov.cn",
            "Referer": "https://txzbqy.miit.gov.cn/",
            "Sec-Fetch-Dest": "empty",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Site": "same-origin",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
            "sec-ch-ua": "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\""
        }
        self.cookies = {
            "jsessionid": "rBQxShroZP7e6hCSvAaYpEgHr4JRnH6gNcUA",
            "wzws_sessionid": "gjFiM2ZlM4AxMTcuNjEuMjEuMjEwgTJjZTFmZKBk/t7g"
        }

    @staticmethod
    def decrypt_aes(ciphertext, key):
        ciphertext = base64.b64decode(ciphertext)                           # 使用base64解码密文
        cipher = AES.new(key.encode(), AES.MODE_ECB)                        # 创建AES对象并指定使用ECB模式和PKCS7填充
        plaintext = cipher.decrypt(ciphertext)                              # 解密密文
        plaintext = unpad(plaintext, AES.block_size)                        # 去除填充数据
        return plaintext.decode('utf-8')

    @staticmethod
    def encrypt_aes(plaintext, key):
        cipher = AES.new(key.encode(), AES.MODE_ECB)                        # 创建AES对象并指定使用ECB模式和PKCS7填充
        padded_plaintext = pad(plaintext.encode(), AES.block_size)          # 对明文进行填充
        ciphertext = cipher.encrypt(padded_plaintext)                       # 加密明文
        encrypted_text = base64.b64encode(ciphertext).decode()              # 使用base64进行编码
        return encrypted_text

    def captcha_request(self):                                              # 请求图片验证数据接口
        url = "https://txzbqy.miit.gov.cn/zbtb/captcha/slideCaptcha"
        data = {
            "canvasWidth": 320, "canvasHeight": 155, "blockWidth": "65", "blockHeight": "55", "blockRadius": 10
        }
        data = json.dumps(data, separators=(',', ':'))
        response = self.session.post(url, headers=self.headers, cookies=self.cookies, data=data)
        res_data = json.loads(response.text)
        return res_data

    def img_info(self, response_data):
        block_img = response_data['data']['blockSrc']                       # 保存背景图片
        block_img = base64.b64decode(block_img.split(',')[-1])
        with open('./block.png', 'wb') as f:
            f.write(block_img)
        canvas_src = response_data['data']['canvasSrc']                     # 加密的滑块文本
        nonce_str = response_data['data']['nonceStr']
        canvas_img = self.decrypt_aes(canvas_src, nonce_str)                # 解密滑块文本
        canvas_img = base64.b64decode(canvas_img.split(',')[-1])            # 保存滑块图片
        with open('./canvas.png', 'wb') as f:
            f.write(canvas_img)

    def get_distance(self):                                                 # 使用ocr识别滑块距离
        with open('./block.png', 'rb') as f:
            target_bytes = f.read()
        with open('./canvas.png', 'rb') as f:
            background_bytes = f.read()
        res = self.det.slide_match(target_bytes, background_bytes, simple_target=True)  # target 的四个值就是缺口位置的左上角和右下角的左边位置
        distance = res['target'][0]
        return distance

    def get_code(self, distance, nonce_str):                                # 得到 code
        code = self.encrypt_aes(str(distance), nonce_str)
        return code

    def verify_function(self):
        res_data = self.captcha_request()
        nonce_str = res_data['data']['nonceStr']
        self.img_info(response_data=res_data)
        distance = self.get_distance()
        code = self.get_code(distance=distance, nonce_str=nonce_str)
        return {'token': nonce_str, 'code': code}


if __name__ == '__main__':
    TxzbqyMiitSlider().verify_function()

测试代码及结果

from txzbqy_miit_slider import TxzbqyMiitSlider
import requests
import json


headers = {
    "Accept": "application/json, text/plain, */*",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Connection": "keep-alive",
    "Content-Type": "application/json;charset=UTF-8",
    "Origin": "https://txzbqy.miit.gov.cn",
    "Referer": "https://txzbqy.miit.gov.cn/",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-origin",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
    "sec-ch-ua": "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\""
}
cookies = {
    "jsessionid": "rBQl5RroZQEZ1zVgT0ecpkm3umIwl-AbpQcA",
    "wzws_sessionid": "gjFiM2ZlM4AxMTcuNjEuMjEuMjEwgTJjZTFmZKBk/t7g"
}
url = "https://txzbqy.miit.gov.cn/zbtb/gateway/gatewayExpert/getBidInformationList"

verify_data = TxzbqyMiitSlider().verify_function()
print("验证参数:", verify_data)

data = {
    "totalSize": 0,
    "page": 2,
    "limit": 15,
    "bidProjectName": "",
    "supervisorName": [],
    "occupationBeginDate": "",
    "occupationEndDate": "",
    "nationFlag": None,
    "bidType": None,
    "bidSubtype": None,
    "acceptanceBidder": "",
    "unitRestrict": [],
    "bidAcceptanceNotificaiton": [],
    "flag": 2,
    "isPublic": "0",
    "code": f"{verify_data['code']}",
    "token": f"{verify_data['token']}"
}
data = json.dumps(data, separators=(',', ':'))
response = requests.post(url, headers=headers, cookies=cookies, data=data)

print(response.text)
  • 结果如图所示

posted @ 2023-09-13 15:45  愺様  阅读(75)  评论(0编辑  收藏  举报