当前位置: 首页 > news >正文

sqli-labs通关笔记-第24关 SQL二次注入(单引号闭合)

目录

目录

一、二次注入

二、相关函数

1、limit函数

2、mysqli_real_escape_string

三、源码分析

1、注册源码

2、登录源码

3、修改密码源码

四、渗透实战

1、进入靶场

2、注入点分析

3、渗透实战

(1)注册新用户

(2)查看数据库

(3)用户登录

(4)修改密码


SQLI-LABS 是一个专门为学习和练习 SQL 注入技术而设计的开源靶场环境,本小节对第24关Less 24基于字符型的SQL二次注入关卡进行渗透实战。  

一、二次注入

SQL 注入指攻击者通过在输入字段中插入恶意 SQL 代码来改变原 SQL 语句的逻辑。字符型注入通常发生在SQL 语句使用单引号或者双引号等包裹字符串参数的场景中。攻击者通过闭合单引号或者双引号等符号并注入额外的 SQL 代码,破坏原有语句结构。

SQL 二次注入是指攻击者在第一次输入数据时,将恶意 SQL 语句隐藏在看似正常的数据中,这些数据被存储在数据库中。当应用程序在后续的操作中再次使用这些存储的数据进行 SQL 查询时,恶意语句被执行,从而导致数据库被攻击。

SQL二次注入可以分为两个步骤,具体如下所示。

  • 第一步:数据库中插入恶意数据(存储阶段):在向数据库插入数据时,对其中的特殊字符进行了转义处理(比如单引号'转义后变为\'),转义后的数据被存储到数据库。
  • 第二步:执行恶意SQL代码(执行截断):开发者默在从数据库中数据时,未对其进行再次转义而直接拼接到新的SQL语句中(存储时转义了单引号,但取出时未转义),在执行SQL语句时形成二次注入。

二、相关函数

1、limit函数

Limit是 SQL 中用于限制查询结果数量的子句,不是真正的函数。Limit通常有两种常见形式,具体如下所示。

  • 单参数形式LIMIT n

    • 返回前 n 条记录

    • 示例:LIMIT 5 返回前5条结果

  • 双参数形式LIMIT offset, count

    • offset:跳过的记录数(从0开始)

    • count:要返回的记录数

    • 示例:LIMIT 10, 5 跳过前10条,返回接下来的5条

举例:SQL语句“SELECT * FROM users WHERE id='$id' LIMIT 0,1”中的LIMIT 0,1"表示获取第一条匹配的记录",LIMIT0,1的具体含义如下所示,

  • 从第0条记录开始(即不跳过任何记录)

  • 只返回1条记录

2、mysqli_real_escape_string

mysqli_real_escape_string() 是 PHP 中用于防止 SQL 注入攻击 的核心函数之一,功能是对用户输入的字符串进行转义处理,确保字符串中的特殊字符(如 SQL 语法中的引号、注释符等)被正确转义,从而避免恶意输入破坏 SQL 语句的结构。

mysqli_real_escape_string() 会转义 SQL 中具有特殊含义的字符,部分举例如下所示。

原始字符转义后字符
'(单引号)\'
"(双引号)\"
\(反斜杠)\\
NULL\0

三、源码分析

本关卡Less24是基于字符型的SQL注入关卡,相关源码如下所示。

1、注册源码

Less24关卡new_user.php功能的源码功能是注册过程,详细注释后的源码如下所示。

<?php
session_start(); // 启动会话
?>
<div align="right"><!-- 右侧导航链接:返回主页 --><a style="font-size:.8em;color:#FFFF00" href='index.php'><img src="../images/Home.png" height='45' width='45'><br>HOME</a>
</div>
<?php
// 引入数据库连接配置
include("../sql-connections/sqli-connect.php");if (isset($_POST['submit'])) { // 检测表单提交// 获取用户输入并使用mysqli_real_escape_string转义$username = mysqli_real_escape_string($con1, $_POST['username']);$pass = mysqli_real_escape_string($con1, $_POST['password']);$re_pass = mysqli_real_escape_string($con1, $_POST['re_password']);echo "<font size='3' color='#FFFF00'>";// 检查用户名是否已存在(查询数据库)$sql = "SELECT COUNT(*) FROM users WHERE username='$username'";$res = mysqli_query($con1, $sql) or die('You tried to be smart, Try harder!!!! :( ');$row = mysqli_fetch_row($res);// 判断用户名是否存在if (!$row[0] == 0) { // 若存在(COUNT(*)不为0)?><script>alert("The username Already exists, Please choose a different username ")</script>;<?phpheader('refresh:1, url=new_user.php'); // 重定向回注册页} else { // 用户名不存在if ($pass == $re_pass) { // 验证两次密码是否一致// 插入新用户数据(使用双引号转义,且参数已转义)$sql = "INSERT INTO users (username, password) VALUES(\"$username\", \"$pass\")";mysqli_query($con1, $sql) or die('Error Creating your user account,  : ' . mysqli_error($con1));echo "<center><img src=../images/Less-24-user-created.jpg><font size='3' color='#FFFF00'>";echo "<br>Redirecting you to login page in 5 sec................";echo "<br>If it does not redirect, click the home button on top right</center>";header('refresh:5, url=index.php'); // 注册成功后重定向到登录页} else { // 密码不一致?><script>alert('Please make sure that password field and retype password match correctly')</script><?phpheader('refresh:1, url=new_user.php'); // 重定向回注册页}}echo "</font>";
}
?>

该代码是一个用户注册功能模块,主要逻辑如下:

  1. 接收注册数据:获取用户提交的用户名、密码和重复密码。
  2. 输入转义:使用mysqli_real_escape_string对用户名和密码进行 SQL 转义。
  3. 用户名查重:查询数据库检查用户名是否已存在。
  4. 密码验证:对比两次输入的密码是否一致。
  5. 用户创建:若通过验证,将用户数据插入数据库,并提示注册成功。

本关卡的关键源码如下所示。

    // 获取用户输入并使用mysqli_real_escape_string转义$username = mysqli_real_escape_string($con1, $_POST['username']);$pass = mysqli_real_escape_string($con1, $_POST['password']);$re_pass = mysqli_real_escape_string($con1, $_POST['re_password']);// 插入新用户数据(使用双引号转义,且参数已转义)$sql = "INSERT INTO users (username, password) VALUES(\"$username\", \"$pass\")";

2、登录源码

Less24关卡login.php功能的源码功能是登录过程,详细注释后的源码如下所示。

<?php
session_start(); // 启动会话管理
// 包含数据库连接配置文件
include("../sql-connections/sqli-connect.php");/*** 处理用户登录验证的函数* @param mysqli $con1 数据库连接对象* @return string|int 登录成功返回用户名,失败返回0*/
function sqllogin($con1) {// 获取并转义用户输入的用户名和密码$username = mysqli_real_escape_string($con1, $_POST["login_user"]);$password = mysqli_real_escape_string($con1, $_POST["login_password"]);// 构造SQL查询语句(字符串拼接方式)$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";// 执行SQL查询,失败时输出错误信息$res = mysqli_query($con1, $sql) or die('You tried to be real smart, Try harder!!!! :( ');$row = mysqli_fetch_row($res);// 判断查询结果:若存在则返回用户名,否则返回0if ($row[1]) {return $row[1];} else {return 0;}
}// 执行登录验证
$login = sqllogin($con1);// 根据验证结果处理后续逻辑
if (!$login == 0) { // 登录成功$_SESSION["username"] = $login; // 存储用户名到会话setcookie("Auth", 1, time() + 3600); // 设置认证Cookie(有效期1小时)header('Location: logged-in.php'); // 重定向到登录后页面
} else { // 登录失败// 显示失败提示图片?><tr><td colspan="2" style="text-align:center;"><br/><p style="color:#FF0000;"><center><img src="../images/slap1.jpg"></center></p></td></tr><?PHP
} 
?>

这是一个基本的用户登录验证功能:

  • 接收用户输入:从 POST 请求中获取用户名(login_user)和密码(login_password)。
  • 输入转义:使用mysqli_real_escape_string对输入进行转义,防止 SQL 注入。
  • 数据库查询:查询users表中匹配用户名和密码的记录。
  • 验证结果处理
    • 成功:将会话变量username设置为登录用户名,并创建有效期 1 小时的认证 Cookie,然后重定向到logged-in.php
    • 失败:显示错误提示图片(slap1.jpg)。

3、修改密码源码

Less23关卡pass_change.php功能的源码功能是修改密码,详细注释后的源码如下所示。

<html>
<head>
</head>
<body bgcolor="#000000">
<?PHP
// 开启Session
session_start();// 认证检查:必须同时存在Auth Cookie和username Session
if (!isset($_COOKIE["Auth"])) {if (!isset($_SESSION["username"])) {header('Location: index.php'); // 未登录重定向}header('Location: index.php'); // 冗余重定向
}
?><!-- 首页链接 -->
<div align="right"><a style="font-size:.8em;color:#FFFF00" href='index.php'><img src="../images/Home.png" height='45'; width='45'></br>HOME</a>
</div><?php
// 包含数据库连接配置
include("../sql-connections/sqli-connect.php");// 处理密码修改请求
if (isset($_POST['submit'])) {// 获取并过滤输入$username = $_SESSION["username"]; // 从Session获取用户名$curr_pass = mysqli_real_escape_string($con1, $_POST['current_password']); // 当前密码$pass = mysqli_real_escape_string($con1, $_POST['password']); // 新密码$re_pass = mysqli_real_escape_string($con1, $_POST['re_password']); // 确认密码// 验证两次新密码是否一致if($pass == $re_pass) {    // 构建并执行密码更新SQL$sql = "UPDATE users SET PASSWORD='$pass' WHERE username='$username' AND password='$curr_pass'";$res = mysqli_query($con1, $sql) or die('You tried to be smart, Try harder!!!! :( ');$row = mysqli_affected_rows($con1);echo '<font size="3" color="#FFFF00">';echo '<center>';if($row == 1) {echo "Password successfully updated";} else {header('Location: failed.php'); // 更新失败重定向}} else {// 密码不一致处理echo '<font size="5" color="#FFFF00"><center>';echo "Make sure New Password and Retype Password fields have same value";header('refresh:2, url=index.php'); // 2秒后重定向}
}// 处理登出请求
if(isset($_POST['submit1'])) {session_destroy(); // 销毁Sessionsetcookie('Auth', 1 , time()-3600); // 使Cookie过期header('Location: index.php'); // 重定向到首页
}
?>
</center>  
</body>
</html>

本代码的如下update更新的SQL注入语句有SQL注入风险,原因如下所示。

$username = $_SESSION["username"]; // 从Session获取用户名
$curr_pass = mysqli_real_escape_string($con1, $_POST['current_password']); // 当前密码
$pass = mysqli_real_escape_string($con1, $_POST['password']); // 新密码
$re_pass = mysqli_real_escape_string($con1, $_POST['re_password']); // 确认密码$sql = "UPDATE users SET PASSWORD='$pass' WHERE username='$username' AND password='$curr_pass'";
  • $pass$curr_pass虽然经过mysqli_real_escape_string过滤,但$username直接从Session获取未过滤。

  • 攻击者可篡改Session伪造管理员用户名实现垂直越权修改密码。

四、渗透实战

1、进入靶场

进入sqli-labs靶场首页,其中包含基础注入关卡、进阶挑战关卡、特殊技术关卡三部分有效关卡,如下所示。

http://127.0.0.1/sqli-labs/

点击进入Page2,如下图红框所示。 

其中第24关在进阶挑战关卡“SQLi-LABS Page-2 (Adv Injections)”中, 点击进入如下页面。

http://127.0.0.1/sqli-labs/index-1.html#fm_imagemap

点击上图红框的Less24关卡,进入到靶场的第24关卡二次注入关卡,页面有3个基本功能,分别是登录、忘记密码和注册新用户,具体如下所示。

http://127.0.0.1/sqli-labs/Less-24/

2、注入点分析

根据源码分析可知,本关卡基于GET方法传入参数id,仅对id过滤#和-- 这两个注释符号,但未处理其他可能的注入符号。本关卡的注入点为id,SQL语句的含义是根据用户提供的ID值(字符型)从users表中查询并返回匹配的用户记录的所有字段信息,且仅返回第一条匹配结果,具体代码如下所示。

    $id = $_GET['id']; // 获取id参数值// 过滤注释符号(#和--),防止使用注释绕过检测$reg = "/#/"; // 匹配#符号的正则表达式$reg1 = "/--/"; // 匹配--符号的正则表达式$replace = ""; // 替换为空字符串$id = preg_replace($reg, $replace, $id); // 替换#为空$id = preg_replace($reg1, $replace, $id); // 替换--为空// 构造SQL查询(存在SQL注入安全风险)$sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";

根据 id='$id'可知闭合方式为单引号,虽然本关卡过滤了注释符号,但是由于本关卡只查询一个参数id,注入语句中不包含注释符号也不影响注入,id被单引号包裹故而SQL注入为字符型注入。

3、渗透实战

(1)注册新用户

bp开启拦截功能,进入到靶场的第24关卡,如下所示。

点击注册进入如下页面,此时新用户名设置为admin'#mooyuan,密码为123456,如下所示。

点击Register,此时页面先是说注册成功很快又跳到回了登录首页,如下所示。

bp抓包,发现一共产生两个报文,第一个是POST报文,服务器返回注册成功,如下所示。

第二个包则是注册成功后重定向到了登陆页面,具体如下所示。

(2)查看数据库

使用navicat查看数据库security中的user表,发现多了一条数据,用户名为admin'#mooyuan,密码为123456,正是我们第一步注册的新用户,很明显存储的内容并没有对单引号进行转义。特别注意admin账户的密码还是admin,具体如下所示。

(3)用户登录

回到登录页面,输入用户名admin'#mooyuan,密码123456登录,如下所示。

登录后效果如下所示,页面显示登录成功,“YOU ARE LOGGED IN AS admin'#mooyuan”,下面还有一个修改密码的输入框(包括当前密码,新密码,确认密码),具体如下所示。 

(4)修改密码

将密码由123456修改为mooyuan123456,如下所示。

点击reset密码,如下所示提示修改成功。

原理如下所示,由于用户名此时为admin'#mooyuan,此时新密码为mooyuan123456,由于SQL的update语句如下所示。

$username = $_SESSION["username"]; // 从Session获取用户名
$curr_pass = mysqli_real_escape_string($con1, $_POST['current_password']); // 当前密码
$pass = mysqli_real_escape_string($con1, $_POST['password']); // 新密码$sql = "UPDATE users SET PASSWORD='$pass' WHERE username='$username' AND password='$curr_pass'";
  • $username直接从Session获取未过滤,故而sql语句更新后变为如下内容。

$sql = "UPDATE users SET PASSWORD='mooyuan123456' WHERE username='admin'#' AND password='123456'";

这样SQL语句就变为如下内容,从而可以修改admin的密码,进而越权修改密码成功。

UPDATE users SET PASSWORD='mooyuan123456' WHERE username='admin'

(5)查看admin用户的密码 

使用navicat查看数据库security中的user表,发现多了一条数据,用户名为admin'#mooyuan,密码为123456,并没有改变义。特别注意admin账户的密码却由admin变为了mooyuan123456,从而实现了越权修改amdin账户的密码,具体如下所示。

http://www.lryc.cn/news/603841.html

相关文章:

  • 硬件学习笔记--73 电能表新旧精度等级对应关系
  • debug redis里面的lua脚本
  • Spring Boot 防重放攻击全面指南:原理、方案与最佳实践
  • ElasticSearch 的3种数据迁移方案
  • 在Word和WPS文字中把全角数字全部改为半角
  • Vue2学习-MVVM模型
  • Spring Boot 简单接口角色授权检查实现
  • C++入门知识学习(上)
  • 嵌入式学习日志(十一)
  • css3之三维变换详说
  • SQL Server中的分页查询
  • leetcode热题——螺旋矩阵
  • 第十一天:不定方程求解
  • 镁金属接骨螺钉注册检测:骨科植入安全的科学基石
  • Rust基础-part8-模式匹配、常见集合
  • 亚马逊 Vine 计划:评论生态重构与合规运营策略
  • 学习笔记-中华心法问答系统的性能提升
  • 小孙学变频学习笔记(十二)机械特性的调整 机械特性的改善
  • 想要批量提取视频背景音乐?FFmpeg 和转换器都安排上
  • Day 25:异常处理
  • VTK开发笔记(一):VTK介绍,Qt5.9.3+VS2017x64+VTK8.2编译
  • Zynq SOC FPGA嵌入式裸机设计和开发教程自学笔记:GPIO扩展与中断控制技术,万字详解!!
  • 车载刷写架构 --- 整车刷写中为何增加了ECU 队列刷写策略?
  • 服务器分布式的作用都有什么?
  • Windows下基于 SenseVoice模型的本地语音转文字工具
  • Ubuntu25.04轻量虚拟机Multipass使用Shell脚本自动创建并启动不同版本Ubuntu并复制文件
  • 【支持Ubuntu22】Ambari3.0.0+Bigtop3.2.0——Step1—基础环境准备
  • GaussDB 约束的语法
  • 【LeetCode】前缀表相关算法
  • 服务器数据恢复—RAID上层部署的oracle数据库数据恢复案例