尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【网络安全】Mysql注入应用:GET_LOCK锁机制绕过WAF的实战复现

【网络安全】Mysql注入应用:GET_LOCK锁机制绕过WAF的实战复现 1. MySQL 注入里被忽略的延时手法GET_LOCK 锁机制到底是什么如果你做过 SQL 注入的时间盲注大概率用过sleep()、BENCHMARK()、笛卡尔积这几类延时手段。它们原理直白、上手快但问题也很明显主流 WAF 早就把这些函数名写进了规则库payload 一发出去就被拦。我在本地靶场复现时发现MySQL 里还有一个几乎没人认真用的延时函数——GET_LOCK()它靠的是用户级命名锁的会话竞争来制造等待很多 WAF 的默认规则里根本没有覆盖它。GET_LOCK()是 MySQL 提供的一个用户级锁函数用来在应用层实现跨会话的锁机制。它的完整签名是GET_LOCK(str lock_name, int timeout)第一个参数是锁名称最大 64 字符且区分大小写第二个参数是等待超时时间单位秒传 0 表示立即返回传负数表示无限等待MySQL 5.7.5 之后支持。返回值有三种返回 1 表示成功拿到锁返回 0 表示在超时时间内没抢到锁返回 NULL 表示参数非法或内存不足之类的错误。配套的释放函数是RELEASE_LOCK(str)返回 1 表示当前会话持有该锁并成功释放返回 0 表示当前会话并不持有这个锁返回 NULL 表示锁压根不存在。这里有个容易踩的坑同一个会话里重复调用GET_LOCK(x, 10)不会阻塞会直接返回 1因为锁已经被这个会话自己持有了。真正产生等待的前提是不同会话去抢同一个锁名。锁的释放规则也值得记一下。显式释放靠RELEASE_LOCK()隐式释放发生在会话终止、连接关闭的时候另外还受wait_timeout参数控制。它和 InnoDB 的行锁不一样不会随事务结束自动释放。你可以用SHOW VARIABLES LIKE wait_timeout;查看当前会话超时时间也能用SET SESSION wait_timeout 600;临时改大。我本地这套环境默认是 120 秒网上有人说默认 8 小时实际跟 MySQL 版本和发行版配置有关别照搬。理解了这个机制就能明白它在注入场景里的价值只要能让两个不同会话去竞争同一个锁名其中一个会话持有锁不释放另一个会话就会卡在等待上形成可观测的延时。这个延时不是靠 CPU 空转也不是靠sleep那种显式休眠而是靠锁等待行为特征和常规延时函数完全不同这正是它能绕过一部分 WAF 检测逻辑的原因。接下来我会把本地靶场的复现步骤、可复制的 payload、以及和雷池 WAF 的拦截对比完整走一遍。2. 复现前的环境准备TaoToken 接入与靶场数据库配置要在本地把 GET_LOCK 延时注入跑通你需要两样东西一个能发请求的注入点和一个能观察会话与锁状态的数据库环境。注入点我用 sqli-labs 和 MRCMS 两个靶场组合数据库用 MySQL 5.7 以上版本。为了让整个调试过程更顺手我习惯把模型对话和代码辅助接到 TaoToken 上边复现边让它帮我解释报错、生成 payload 变体省去来回查文档的时间。TaoToken 是一个大模型 API 聚合平台兼容 OpenAI 风格的接口协议你拿到的 Key 可以直接用在支持自定义 Base URL 的客户端里。对做安全复现的人来说它的用处在于遇到 WAF 拦截、SQL 语法报错、锁等待不生效这类问题时可以把报错原文丢给模型让它帮你分析是语法问题、会话问题还是规则命中问题。下面是我实际用的接入配置。先到官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册账号然后在控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。API 的基础地址是 https://taotoken.net/api 注意这个地址不带任何查询参数。如果你用的是 Cline、Continue 这类支持 OpenAI 兼容协议的插件配置项就三项Base URL 填https://taotoken.net/apiAPI Key 填你创建的那串Model ID 填你在模型列表里选中的模型名。这三件套缺一不可尤其是 Model ID填错了会直接返回 404 或者 model not found。想先验证模型通不通可以用模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 发一条测试消息能正常返回就说明 Key 和地址没问题。数据库这边确认你的 MySQL 版本在 5.7.5 以上因为负数超时无限等待是那个版本之后才支持的。然后检查wait_timeoutSHOW VARIABLES LIKE wait_timeout; SHOW VARIABLES LIKE max_connections;max_connections决定了你能同时开多少个会话做锁竞争测试时至少要保证两个独立连接。sqli-labs 用 phpstudy 搭起来默认就是短会话模式每个请求新建连接、请求完就关。MRCMS-3.1.2 是长会话模式整个应用共享一个持久连接。这两个组合起来正好构成多 Web 共用数据库 长会话 短会话的利用场景也是后面能稳定触发锁等待的关键。3. 可复制的 GET_LOCK 注入 Payload 与配置片段这一节是整篇的核心我把实际跑通的 payload、数据库配置和会话观察方法都列出来你可以直接复制到自己的靶场里改。先明确利用条件只有两条同时满足才会产生延时第一必须是不同会话去竞争同一个锁名第二持有锁的那个会话要长时间不释放。短会话模式下A 会话拿到锁后请求一结束连接就关了锁自动释放B 会话还没来得及等就没了所以单靠短会话靶场是打不出延时的。先看最基础的锁获取和释放在 MySQL 客户端里手动验证一遍机制-- 会话1获取名为 testlock 的锁超时 10 秒 SELECT GET_LOCK(testlock, 10); -- 返回 1表示获取成功 -- 会话2另开一个连接尝试获取同名锁 SELECT GET_LOCK(testlock, 5); -- 卡住 5 秒后返回 0表示超时未获取到 -- 会话1释放锁 SELECT RELEASE_LOCK(testlock); -- 返回 1 -- 会话1再次释放 SELECT RELEASE_LOCK(testlock); -- 返回 NULL锁已不存在确认机制没问题后进入注入场景。核心 payload 是把GET_LOCK拼进注入点让数据库执行它并等待-- 基础形式直接调用 GET_LOCK 制造锁等待 ?id1|| GET_LOCK(1,5) || -- 嵌套 if 形式条件为真时才触发锁等待更适合盲注判断 ?id1|| if(1,GET_LOCK(1,5),1) || -- 配合布尔判断把锁等待当作真值信号 ?id1 AND IF(ASCII(SUBSTRING(database(),1,1))100, GET_LOCK(1,5), 0) --这里GET_LOCK(1,5)的第一个参数是锁名用数字 1 也行MySQL 会把它当字符串处理。第二个参数 5 是超时秒数实际延时大约就是 5 秒。如果你想要更长的延时把 5 改成 10 或 15但别太大否则请求容易超时断开反而观察不到结果。关键的一步是让长会话靶场先持有锁。用 MRCMS-3.1.2 的漏洞接口发一次注入让它的持久连接拿到锁且不释放-- 通过 MRCMS 漏洞点注入让长会话持有名为 1 的锁 -- 该会话是持久连接不会主动释放锁一直挂着 1 AND GET_LOCK(1,100) --长会话拿到锁后再用 sqli-labs 的短会话注入点去抢同一个锁名就会卡住?id1|| GET_LOCK(1,5) ||短会话请求进来尝试获取锁名 1发现已被长会话占用于是等待 5 秒后超时返回。这 5 秒就是可观测的延时信号。整个过程不涉及sleep、BENCHMARK这些被重点监控的函数WAF 的规则库里如果没有专门针对GET_LOCK的条目就不会拦截。如果你用 Cline 或 Claude Code 这类工具辅助调试配置片段可以写成这样以 OpenAI 兼容格式为例{ base_url: https://taotoken.net/api, api_key: 你的_API_KEY, model: 你的_MODEL_ID }把报错原文、payload、数据库返回贴给模型让它帮你判断是会话没对上、锁名不一致还是 WAF 规则命中。实测下来这种边打边问的方式比自己翻文档快很多尤其是排查锁等待不生效的时候。4. 验证请求与成功结果从数据库监听看锁等待payload 写好了怎么确认延时真的来自锁等待而不是网络抖动或者别的原因我一般从三个层面交叉验证HTTP 响应时间、数据库会话状态、以及 WAF 后台日志。先看 HTTP 层面。用 Burp Suite 或者 curl 发请求记录响应时间。正常请求几十毫秒返回触发锁等待的请求会稳定多出 5 秒左右# 正常请求观察基线响应时间 curl -o /dev/null -s -w time_total: %{time_total}s\n http://靶场地址/sqli-labs/Less-1/?id1 # 触发锁等待的请求 curl -o /dev/null -s -w time_total: %{time_total}s\n http://靶场地址/sqli-labs/Less-1/?id1||GET_LOCK(1,5)||如果第二条稳定比第一条多 5 秒说明延时生效了。但光看时间还不够得确认是锁造成的。这时候打开数据库监听工具比如 MySQL 的SHOW PROCESSLIST或者用抓包工具看连接状态SHOW PROCESSLIST;你会看到两个不同Id的连接一个处于User sleep或者等待状态另一个持有锁。长会话那个连接的Time字段会持续增长说明它一直挂着没释放。短会话那个连接在等待锁超时。再进一步可以直接查锁的持有情况。MySQL 5.7 之后可以用 performance_schema 里的 metadata_locks 表或者简单点在另一个会话里试着手动抢同名锁看会不会卡-- 在第三个会话里执行如果卡住说明锁确实被占着 SELECT GET_LOCK(1, 3);如果这个也卡 3 秒返回 0就坐实了锁名 1 被长会话持有。这一步很关键很多人以为延时生效了其实是别的原因用这个方法能排除掉。最后看 WAF 后台。雷池 WAF 默认配置下sleep和BENCHMARK的 payload 会被直接拦截日志里能看到命中规则。而GET_LOCK的 payload 在后台能看到被检测到但默认不拦截只记录。我复现时对比过发sleep(5)的请求直接返回拦截页面发GET_LOCK(1,5)的请求正常返回且延时 5 秒。这个对比最能说明问题——不是 WAF 没看见而是它的默认规则没把锁函数当成高危延时手段。嵌套 if 的形式也一样能过?id1|| if(1,GET_LOCK(1,5),1) ||响应时间同样多 5 秒WAF 后台依旧只记录不拦截。到这一步整个链路就验证完了长会话持锁 → 短会话抢锁 → 锁等待 → 延时 → WAF 不拦。5. 常见报错排查401、local proxy failed、reading choices 与 OAuth复现过程中最容易卡住的不是 payload 本身而是环境和工具链的报错。我把几个高频问题和排查思路列出来对照着看能省不少时间。401 Unauthorized这个基本都出在 API 接入环节。如果你用 Cline 或 Claude Code 调模型时返回 401先检查三件套——Base URL 是不是https://taotoken.net/api注意不要多加斜杠或者路径API Key 是不是复制完整前后别有空格Model ID 是不是当前账号有权限的模型。三个里错一个都会 401 或者 404。我踩过的坑是 Key 复制时带了个换行排查了半天。local proxy failed这个报错通常出现在客户端配置了本地代理但代理没起来或者端口不对。如果你在 Cline 里看到这个检查设置里的代理选项把不需要的代理关掉直连https://taotoken.net/api。注意这里说的是客户端自身的网络配置问题不是让你去搞什么网络工具纯粹是本地端口没通。reading choices 相关报错这类报错一般出现在流式响应解析阶段比如error reading choices或者unexpected end of JSON。原因多半是模型返回被中途截断或者客户端超时设置太短。把客户端的超时时间调大比如从 30 秒改成 120 秒再试一次。如果还不行换模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 直接发请求看是不是模型侧的问题。OAuth 相关报错如果你用的是 Claude Code 这类带 OAuth 流程的工具报错里出现OAuth token expired或者invalid_grant说明授权过期了。重新走一遍授权流程或者在配置里改用 API Key 方式接入。Claude Code 的接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的配置步骤包括 Base URL、Key 和 Model ID 怎么填。锁等待不生效这个不算报错但比报错更让人头疼。排查顺序是先确认两个会话是不是真的不同SHOW PROCESSLIST看 Id 是否不一样再确认锁名是不是完全一致大小写敏感最后确认长会话有没有提前释放锁连接断了锁就没了。如果用的是短会话靶场单独打那永远打不出延时必须配一个长会话靶场先持锁。WAF 拦截了 GET_LOCK虽然默认配置下雷池不拦但有些 WAF 规则更严。如果被拦试试把锁名换成随机字符串或者用if嵌套把函数调用藏深一点。也可以把GET_LOCK拆成大小写混合或者加注释比如GET_/**/LOCK(1,5)看能不能绕过规则匹配。不过这类变形要看具体 WAF 的解析能力不一定都有效。6. 从复现到实战锁机制注入的边界与后续方向把整个流程跑通之后我对 GET_LOCK 这类锁机制注入的理解清晰了不少。它的核心优势不在于延时有多精准而在于行为特征和常规延时函数完全不同。sleep是显式休眠BENCHMARK是 CPU 空转这俩在流量特征和数据库行为上都很显眼。而锁等待表现为一个会话持锁、另一个会话阻塞从数据库角度看是正常的并发控制行为很多 WAF 的规则库压根没把它当成攻击特征。但它也有明确的边界。利用条件里不同会话和长时效应这两条决定了它只在特定部署模式下有效。短会话模式打不出来纯长会话模式也打不出来只有连接池模式、多 Web 共用数据库、长会话加短会话混合这些场景才有戏。所以实战中第一步不是急着发 payload而是先判断目标属于哪种连接模式。判断方法也简单多次请求看数据库连接的thread_id变不变变了就是短会话或连接池不变就是长会话。另一个值得注意的点是wait_timeout。锁的隐式释放受这个参数控制如果目标数据库的wait_timeout设得很短长会话可能在你发第二个请求之前就超时断开了锁也就释放了。复现时我本地默认是 120 秒够用。如果遇到锁等待时有时无先查这个参数。后续想深入的话可以研究几个方向一是不同 WAF 对GET_LOCK的检测策略差异看看哪些规则更严二是把锁机制和其他盲注技巧结合比如用锁等待做布尔判断的延时信号替代传统的sleep三是看看 MySQL 8.0 之后锁相关函数有没有变化比如GET_LOCK的行为调整。这些在本地靶场都能复现成本不高。如果你在复现时遇到模型调用或者配置上的问题可以到接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 查具体步骤或者用 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 重新生成 Key 试试。长期做安全研究和编码辅助的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 会更划算一些。先把本地这套环境跑通再慢慢扩展到更复杂的场景。
返回列表