ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

sqlmap实战指南:PHP网站SQL注入检测、利用与防御全解析

sqlmap实战指南:PHP网站SQL注入检测、利用与防御全解析 接到一个授权的Web测试任务目标是个老PHP站点。我习惯先把sqlmap跑起来再用Burp Suite手工跟一遍请求双线并行效率最高。SQL注入这东西在PHP站点里实在太常见了尤其是早期没有框架、纯拼SQL字符串的老代码几乎一打一个准。这篇就当一次完整实操记录从信息收集、注入检测到数据提取最后再说清楚PHP侧到底怎么防适合刚接触sqlmap的测试人员也适合PHP开发想了解漏洞原理后做加固的参考。1. 前期准备先把靶场和目标环境搭起来1.1 为什么选PHP站点练手PHP在Web服务端语言里的存量依旧巨大很多中小站点、老业务系统、CMS二次开发项目都是PHP写的。SQL注入的核心问题不在于语言本身而在于开发习惯——直接用字符串拼接SQL语句、过滤不严格、错误信息回显给用户这几点在PHP项目里的出现概率尤其高。拿这套流程跑一遍真实环境你会看到SQL注入从检测、确认、利用到最终提取数据的完整闭环。练手的时候我建议用pikachu靶场、DVWA或者sqli-labs这几套环境都是开箱即用里面有专门为注入设计的页面比对着教科书看理论要直观得多。1.2 环境准备sqlmap安装与运行验证sqlmap是Kali Linux自带的如果本地不是Kali也可以用pip直接装pip install sqlmap sqlmap --version装完之后做个快速验证拿一个已知存在注入的靶场URL测一下确保工具本身可用。这里要强调一句sqlmap是一个攻击性很强的工具只能在目标授权范围内使用。靶场、CTF平台、自己搭建的测试环境都没问题拿公网站点尝试就是另外一回事了这个边界必须分清。1.3 目标分析思路拿到一个PHP站点我通常会先看这几个位置URL参数index.php?id1、list.php?cat_id2这类GET参数最常见登录表单POST方式提交用户名密码登录框如果存在注入往往就是万能密码的入口搜索框SQL的LIKE查询拼接很容易被%和单引号干扰Cookie或Header参数有些框架会把用户信息存进Cookie服务端拆出来拼SQL对这些位置做一个列表标记参数类型、请求方式、是否需要登录态后面测的时候按优先级逐个过。信息收集做得越细sqlmap的命中率就越高不要一上来就对着首页乱打。2. 注入点确认手工判断比自动扫描更靠谱2.1 先手工探一遍sqlmap虽然强大但我不建议一开始就甩给它一个URL硬测。先手工用Burp Suite或浏览器开发者工具观察响应差异很多细节在自动工具的输出里反而不容易看出来。最基础的三板斧在参数后面加单引号观察是否报错、是否返回异常内容用and 11和and 12对比正常页面和异常页面的内容差异在参数里加--注释符看SQL语法是否被改变比如目标URL是http://target.com/goods.php?id1依次尝试http://target.com/goods.php?id1 http://target.com/goods.php?id1 and 11 http://target.com/goods.php?id1 and 12 http://target.com/goods.php?id1--如果11时页面正常、12时页面空白或内容不同说明数字型注入存在。如果单引号报错说明字符型注入存在。这一步能帮你判断注入类型后面给sqlmap指定参数时心里就有数了。2.2 用sqlmap做基础检测手工确认有注入后再用sqlmap做系统的数据库信息探测sqlmap -u http://target.com/goods.php?id1 --batch--batch参数让sqlmap全程自动选择默认选项适合无人值守跑批。这个命令会做以下几件事测试参数id是否可注入判断注入类型是布尔盲注、时间盲注、报错注入还是联合查询识别后端数据库类型和版本输出里看到Parameter: id (GET)和Title: MySQL 5.0.12 AND time-based blind之类的内容就说明注入点已经确定了。如果是MySQL 5.0以上版本information_schema库会带来极大的信息提取便利。2.3 盲注场景的确认技巧有时候页面没有任何报错回显and 11和and 12返回的页面看起来完全一样这时候要靠时间盲注来判断。手工测试时可以在参数后加id1 and sleep(5)如果页面明显卡了5秒说明条件判断生效注入存在。sqlmap检测时间盲注的时候也是这个原理只是它会反复测试多个延迟值来减少网络波动带来的误判。这里有个小技巧如果目标网络延迟本身不稳定时间盲注检测容易误报可以配合--time-sec把延迟时间调大误报率会降很多。3. sqlmap实战从基础检测到数据提取3.1 获取数据库列表确认注入之后第一步拿库名sqlmap -u http://target.com/goods.php?id1 --dbs这一步会枚举出目标MySQL实例里所有的数据库。很多PHP站点用的是同一个数据库账号如果你拿到了root权限级别的连接连phpMyAdmin的配置库、其他业务的库都会一览无余这也是权限过大的风险所在。作为测试人员看到这种情况一定要写进报告里属于高危风险项。3.2 指定数据库并枚举表拿到库名后比如有一个叫shop的库接着枚举表名sqlmap -u http://target.com/goods.php?id1 -D shop --tables输出会列出这个库下所有数据表重点关注名字像user、admin、member、account这类表通常存放着敏感信息。如果表特别多sqlmap默认会中断询问是否继续用--batch模式跑完整个列表。3.3 枚举字段并导出数据锁定目标表比如admin_user然后看字段sqlmap -u http://target.com/goods.php?id1 -D shop -T admin_user --columns字段信息里如果看到username和password直接导出sqlmap -u http://target.com/goods.php?id1 -D shop -T admin_user --dumpsqlmap会尝试破解MySQL的哈希格式密码常见的是MD5破解不了也会把哈希值原样保存到本地文件。整个测试过程的输出默认会存在~/.local/share/sqlmap/output/目录下每次跑完的结果都会留痕方便复盘。3.4 参数调优level、risk和线程有的注入点藏得深比如参数在Cookie里、需要特定请求头才能触发、或者要登录之后才能访问。这时候默认的level 1就不够用了要加大探测深度sqlmap -u http://target.com/goods.php?id1 --level3 --risk2--level控制测试的范围level 1只测GET和POST参数level 2会加入Cookie测试level 3会测User-Agent、Referer等Header--risk控制测试的payload危险程度risk 2会加入OR条件的payloadSQL语句报错的概率更高但也更容易破坏数据这里提醒一下--risk3会使用基于OR的注入payload在真实业务库里执行有可能会修改数据测试环境无所谓生产环境一定要避免。如果你不确定后果risk保持在1或2就好。线程数也可以适度调整sqlmap -u http://target.com/goods.php?id1 --dbs --threads5--threads默认1调到5到10能明显加速布尔盲注的检测过程。但线程太高会产生大量并发请求很容易触发WAF拦截或干扰业务正常访问务实的选择是5。3.5 特殊场景POST和登录态很多PHP站点最有价值的数据在后台而后台是需要登录的。sqlmap处理这种情况也很简单先用Burp抓一个已登录的请求保存成文件然后直接用-r指定sqlmap -r request.txt --dbs如果是纯POST表单也可以用--datasqlmap -u http://target.com/login.php --datausernameadminpassword123456 --dbs登录后的Cookie可以用--cookie传进去sqlmap -u http://target.com/admin/info.php?id1 --cookiePHPSESSIDxxxxxxxxxx --dbs这套组合拳在真实测试里非常常用很多渗透测试的瓶颈不在技术而在于怎么进入一个有价值的注入点。3.6 遇到WAF时的绕过思路现在不少PHP站点前面会套一层云WAF或者主机层面的防护。sqlmap直接扫会被秒封IP或直接返回403。这时候先别急着上tamper脚本先确认WAF存在手工输入单引号看是否弹拦截页面用--identify-waf让sqlmap尝试识别WAF类型确认有WAF之后再考虑绕过。常见的手法有修改User-Agent--random-agent用--tamper加载混淆脚本比如space2comment把空格替换成注释符双写绕过关键字过滤比如union写成ununionion这和sqlmap的--tamperbetween或--tampermodsecurityversioned的某些逻辑类似用内联注释/*!50000 UNION*/的方式绕过过滤正则MySQL解析器会执行注释内的内容sqlmap -u http://target.com/goods.php?id1 --tamperspace2comment --random-agent --dbs说实话真实生产环境的WAF绕过不是一两个tamper就能解决的需要结合具体过滤规则手工构造payload。sqlmap的tamper脚本库给了很多思路但最终还是要手工调。这一块水很深作为测试人员要清楚绕WAF不是目的确认漏洞存在并给出加固建议才是目的。3.7 从攻击者视角看PHP注入高发位完整跑完一遍sqlmap后你大概率能在某个PHP站点的这几个位置命中注入商品列表和详情页的ID参数文章/公告页的分类ID用户中心里读取个人信息的参数搜索关键词这些位置的共同点是参数直接来自前端请求而后端代码没有做类型校验也没有参数化查询。攻击者不需要理解PHP代码只需要对着URL猜参数名就能展开探测。换句话说注入漏洞的暴露面比你想象的要大得多。4. 漏洞成因解剖为什么PHP网站容易中标4.1 字符串拼接SQL是万恶之源绝大多数PHP注入漏洞都长一个样先把用户输入直接拼进SQL语句再丢给数据库执行。比如登录代码写成这样$username $_POST[username]; $password md5($_POST[password]); $sql SELECT * FROM users WHERE username {$username} AND password {$password}; $result mysqli_query($conn, $sql);当用户在用户名框里输入admin --时SQL语句变成SELECT * FROM users WHERE username admin -- AND password xxx--把后面的密码判断注释掉了整个查询只判断用户名密码形同虚设。如果再输入 OR 11条件变成永真连用户名都不需要知道。这就是所谓的万能密码绕过。这种写法的致命问题在于用户输入被当成了SQL代码的一部分而不是纯数据。数据库引擎不会区分哪段是你写的、哪段是用户传的它只会老老实实执行整条语句。4.2 过滤函数为什么靠不住很多PHP开发者会在拼接之前加一层过滤比如用addslashes、mysqli_real_escape_string或者自己写一个正则把、、select、union这些关键字替换成空字符串。这种做法看起来合理但对抗思路是无穷的编码绕过URL编码、Unicode编码、HEX编码双写绕过ununionion、selselectect简单的正则替换可以被轻松绕过大小写绕过SeLeCt绕过不区分大小写但只匹配小写的正则注释符拆分UN/**/IONMySQL允许关键字中间夹注释符之前热词里有人搜sqlmap双写绕过怎么用实际上就是这类场景。过滤是基于黑名单的而攻击者只需要找到一个漏网的关键字符组合即可。4.3 PHP类型松散带来的边界问题PHP是弱类型语言比较运算符在类型不一致时会进行自动转换。比如用户输入1e0PHP会把它当作科学计数法的1这带来了一些SQL注入以外的逻辑绕过风险比如if ($user[role] $_POST[role]) { // 权限判断 }如果数据库里的role是整数0攻击者构造一个字符串让它和0相等就可以绕过权限判断。这类问题和SQL注入不直接相关但属于同一类思维对用户输入过于信任。安全测试的时候弱类型比较边界也值得顺手验证一下。4.4 报错信息泄露加剧了漏洞被利用的速度PHP报错回显是sqlmap快速定位注入点的重要辅助。You have an error in your SQL syntax这类MySQL错误信息如果直接输出到页面攻击者连盲注都不需要打直接把payload填进报错函数就能看到结果。比如updatexml报错注入id1 AND updatexml(1, concat(0x7e, (SELECT user())), 1)页面会直接显示出当前数据库用户名。一条请求就能拿到信息效率比时间盲注高太多。所以生产环境关闭display_errors并配置好日志记录是PHP安全加固里成本最低、收益最明显的一步。5. 安全防范测试的最终目的是让站点更硬5.1 第一道防线PDO预处理参数化查询PHP侧最有效的修复方案就是用PDO预处理语句替代字符串拼接。PDO的prepare会把SQL结构和数据分开传输数据库引擎在执行前就知道哪个位置是占位符用户输入永远只是数据不会变成代码逻辑。$stmt $pdo-prepare(SELECT * FROM users WHERE username ? AND password ?); $stmt-execute([$username, $password]);就这么几行整条SQL语句的结构就被固定住了。攻击者传入 OR 11PDO会把它当作用户名字符串去匹配而不是当作SQL代码执行。无论输入什么数据库都只会把它放在参数位置。mysqli同样支持预处理语法老的mysql_*函数已经在PHP 7里彻底移除了如果你还在维护老项目升级和改造是迟早的事。5.2 第二道防线输入校验和类型约束参数化查询解决了注入但输入校验依然是必要的纵深防御。每个参数都应该有明确预期ID参数必须是非负整数用filter_var($_GET[id], FILTER_VALIDATE_INT)强制转换邮箱、手机号、日期都必须有对应的格式校验枚举类型如状态码用白名单校验只允许在预设范围内取值$id filter_input(INPUT_GET, id, FILTER_VALIDATE_INT); if ($id false || $id 0) { http_response_code(400); exit(Invalid parameter); }这里有个常见误区很多教程喜欢用htmlspecialchars来防SQL注入但这是错的。htmlspecialchars是用来防XSS的它对单引号实体化后确实会影响SQL注入的构造但完全不能替代参数化查询。两者要同时用各管各的领域。5.3 第三道防线数据库权限收敛拿sqlmap打下来之后我最怕看到的就是PHP站点连接的数据库账号是root。一旦注入点存在攻击者可以直接读mysql.user表、尝试INTO OUTFILE写webshell甚至--os-shell直接拿服务器权限。正确的做法是权限最小化给应用创建一个独立账号只授权应用库的SELECT、INSERT、UPDATE、DELETE绝对不授予FILE、SUPER、GRANT权限多个业务使用多个独立账号互不交叉SQL注入的破坏力有多大很大程度上取决于数据库账号的权限有多高。你把SQL语句堵死了但权限管理没跟上风险依然存在。5.4 第四道防线框架和WAF兜底现代PHP框架Laravel、ThinkPHP、Symfony默认使用ORM和查询构造器内部已经处理了参数绑定。用框架开发的站点注入漏洞概率会低很多但也别掉以轻心——有些开发者会用DB::raw()或whereRaw()拼接自定义SQL一不留神又回到了老路上。WAF是最后的兜底手段通常部署在Web服务器之前用正则规则拦截注入特征。它的优势是无需改代码就能挡住大部分自动化攻击缺点也很明显规则滞后、容易误杀、绕过空间大。WAF应该作为临时缓解措施而不是长期替代代码修复的方案。5.5 上线前的自查清单把一次完整的测试经验总结成清单很有用每次发布前对照过一遍所有SQL语句是否都使用了PDO预处理或框架查询构造器是否有裸拼接SQL的地方全局搜索mysqli_query、$pdo-query和字符串拼接符号display_errors是否关闭日志级别是否配置合理生产环境是否保留了phpinfo页面、后台默认密码数据库账号权限是否最小化管理后台是否开启双因素认证敏感日志登录日志、操作日志是否记录完整照着清单过一遍能挡住九成常见的注入攻击。6. 踩坑记录与排查技巧实录6.1 目标站响应慢导致误判时间盲注检测经常出现误报尤其是目标跨地域或服务器负载高的时候。sqlmap基于响应延迟判断网络抖动会把正常请求变成“延迟响应”容易误判出注入点。解决办法用--time-sec 10把延迟阈值加大多次检测同一个注入点验证结果是否稳定在非高峰时段测试6.2 目标有WAF时先用手工确认再跑工具某些云WAF会把sqlmap的特征识别得很准一旦检测到就直接封IP。遇到这种情况可以先手工用绝对路径、注释符、编码变形构造几个payload试试确认WAF的过滤规则后再有针对性地配tamper。一上来就全速跑sqlmap大概率是把自己封出去。6.3 数据库配置了安全模式MySQL的secure_file_priv如果非空INTO OUTFILE和LOAD_FILE都会受限。sqlmap拿到高权限后想写webshell经常会遇到The MySQL server is running with the --secure-file-priv option这个错误。这说明底层做了安全加固是好事。遇到这种报错别硬绕回到业务层面找其他漏洞或者承认这个点打不穿。6.4 PHP环境常见的兼容坑测试老项目时遇到过目标服务器PHP版本过高导致老CMS直接白屏的情况。比如热词里有人搜fatal error: directive track_errors is no longer available in php这是PHP版本升级后旧配置项失效导致的问题。排查的时候可以先看PHP错误日志确认是应用代码兼容问题还是真的被攻击了别一看到报错就往漏洞上想。如果你在用Mac的M系列芯片跑phpStudy也遇到过找不到适合的PHP版本之类的问题可以先确认本地PHP二进制和扩展版本是否匹配再决定是换版本还是编译安装。6.5 常见问题速查表问题现象可能原因处理方式sqlmap检测不到注入点目标对参数做了过滤或注入点在非默认参数位置提高--level加入Cookie和Header测试扫到一半连接被重置WAF封IP或连接超时调低线程、加--random-agent、更换出口IP后重试扫描很慢网络延迟高目标使用时间盲注响应慢合并请求、调大--time-sec、使用--throttle控制请求频率报了注入但dump不出数据数据库用户权限不足确认数据库连接账号是否有读写目标表权限拿到密码哈希解不开使用了强哈希算法或加盐更换更大的密码词典或直接提交给离线破解环境处理PHP项目升级后数据库连不上PHP版本不兼容旧MySQL扩展替换为PDO或mysqli驱动升级相关依赖最后再分享一点我的体会做了这么多年SQL注入测试最深的感受是漏洞本身不可怕可怕的是不知道漏洞为什么存在。sqlmap帮你找到入口但想彻底看懂一个注入点还是得回到代码层面、回到SQL语句本身去分析。这也是我为什么每次都强调工具是放大器不是替代品——手工验证和代码审计的基本功才是安全测试的核心竞争力。如果你正在学sqlmap建议从靶场起步把它和PHP代码审计结合起来练。每打穿一个注入点就翻翻对应的源码想清楚是哪一行代码出了问题。这样反复几轮比纯刷工具对技术的理解要深得多。后面再遇到老代码光看URL就能猜个八九不离十测试效率自然就上来了。
返回列表