【Web安全】sql注入代码分析

sql注入代码分析

文章目录

  • sql注入代码分析
    • [1. Union注入](#1. Union注入)
    • [2. 布尔盲注](#2. 布尔盲注)
    • [3. 报错注入](#3. 报错注入)
    • [4. 时间盲注](#4. 时间盲注)
    • [5. 堆叠查询注入](#5. 堆叠查询注入)
    • [6. 二次注入](#6. 二次注入)
    • [7. 宽字节注入](#7. 宽字节注入)
    • [8. Cookie注入](#8. Cookie注入)
    • [9. base64注入](#9. base64注入)
    • [10. XFF注入](#10. XFF注入)

参考《Web安全攻防:渗透测试实战指南》

1. Union注入

php 复制代码
<?php
$con=mysqli_connect("localhost", "root", "123456", "test");
if (mysqli_connect_errno())
{
    echo "数据库连接失败: " . mysqli_connect_error();
}
$id = $_GET['id'];
$result = mysqli_query($con,"select * from users where 'id'=".$id);
$row = mysqli_fetch_array($result);
echo $row['username'] . " : " . $row['address'];
echo "<br>";
?>

当访问id=1 union select 1,2,3时,执行的SQL语句为:

sql 复制代码
select * from users where `id`=1 union select 1,2,3

此时SQL语句可以分为select*from users where `id`=1和union select 1,2,3两条,利用第二条语句(Union查询)就可以获取数据库中的数据。

2. 布尔盲注

php 复制代码
<?php
$con=mysqli_connect("localhost", "root" ,"123456", "test");
if (mysqli_connect_errno())
{
    echo "数据库连接失败: " . mysqli_connect_error();
}
$id = $_GET['id'];
if (preg_match("/union|sleep|benchmark/i", $id)) {
    exit("no");
}
$result = mysqli_query($con,"select * from users where 'id'='".$id."'");
$row = mysqli_fetch_array($result);
if ($row) {
    exit("yes");
} else {
    exit("no");
}
?>

在Boolean注入页面中程序先获取GET参数ID,通过preg_match判断其中是否存在union/sleep/benchmark等危险字符。然后将参数ID拼接到SQL语句,从数据库中查询,如果有结果,则返回yes,否则返回no。当访问该页面时,代码根据数据库查询结果返回yes或no,而不返回数据库中的任何数据,所以页面上只会显示yes或no,用的是Boolean注入。

3. 报错注入

php 复制代码
<?php
$con=mysqli_connect("localhost", "root", "123456", "test");
if (mysqli_connect_errno()) {
    echo "数据库连接错误: " . mysqli_connect_error();
}
$username = $_GET['username'];
if($result = mysqli_query($con, "select * from users where 'username'='".$username."'")) {
    echo "ok";
} else {
    echo mysqli_error($con);
}
?>

在报销注入页面中,程序获取GET参数username后,将username拼接到SQL语句中,然后到数据库查询。如果执行成功,就输出ok;如果出错,则通过echo mysqli_error($con)将错误信息输出到页面(mysqli_error返回上一个MySQL函数的错误)

4. 时间盲注

php 复制代码
<?php
$con=mysqli_connect("localhost", "root", "123456", "test");
if (mysqli_connect_errno()) {
    echo "数据库连接错误: " . mysqli_connect_error();
}
$id = $_GET['id'];
if (preg_match("/union/i", $id)) {
    exit("<html><body>no</body></html>");
}

$result = mysqli_query($con, "select * from users where 'id'='".$id."'");
$row = mysqli_fetch_array($result);
if ($row) {
    exit("<html><body>yes</body></html>");
} else {
    exit("<html><body>no</body></html>");
}
?>

在时间注意注入页面中,程序获取GET参数ID,通过preg_match判断参数ID中是否存在Union危险字符,然后将参数ID拼接到SQL语句中。从数据库中查询SQL语句,如果有结果,则返回yes,否则返回no。当访问该页面时,代码根据数据库查询结果返回yes或no,而不返回数据库中的任何数据,所以页面上只会显示yes或no,和Boolean注入不同的是,此处没有过滤sleep等字符

5. 堆叠查询注入

php 复制代码
<?php
try {
    $conn = new PDO("mysql:host=localhost;dbname=test", "root", "123456");
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $stmt = $conn->query("SELECT * FROM users where 'id' = '" . $_GET['id'] . "'");
    $result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
    foreach($stmt->fetchAll() as $k=>$v) {
        foreach ($v as $key => $value) {
            echo $value;
        }
    }
    $dsn = null;
} catch(PDOException $e) {
    echo "error";
}
$conn = null;
?>

在堆叠注入页面中,程序获取GET参数ID,使用PDO的方式进行数据查询,但仍然将参数ID拼接到查询语句,导致PDO没起到预编译的效果,程序仍然存在SQL注入漏洞。使用PDO执行SQL语句时,可以执行多语句,不过这样通常不能直接得到注入结果,因为PDO只会返回第一条SQL语句执行的结果,所以在第二条语句中可以用update更新数据或者使用时间盲注获取数据。访问dd.php?id=1';select if(ord(substring (user(),1,1))=114,sleep(3),1);%23时,执行的SQL语句为:

sql 复制代码
SELECT * FROM users where `id`='1';select if(ord(substring(user(),1,1))=114,sleep (3),1);#

此时SQL语句分为了两条,第一条SELECT*FROM users whereid='1'是代码自己的select查询,而select if(ord(substring(user(),1,1))=114,sleep(3),1);#则是我们构造的时间盲注的语句。

6. 二次注入

不懂可以点这个博文,有图好讲一点

  • 代码一:实现了简单的用户注册功能,程序获取到GET参数username和参数password,然后将username和password拼接到SQL语句,使用insert语句插入数据库中。由于参数username使用addslashes进行转义(转义了单引号,导致单引号无法闭合),参数password进行了MD5哈希,所以此处不存在SQL注入漏洞。正常插入test'之后,数据库就有了test'这个用户。

  • 当访问username=test'&password=123456时,执行的SQL语句为:

    复制代码
    insert into users(\`username\`,\`password\`)values ('test\'','e10adc3949ba59abbe56e057f20f883e')。
php 复制代码
<?php
$con=mysqli_connect("localhost", "root", "root", "sql");

if (mysqli_connect_errno()) {
    echo "数据库连接错误: " . mysqli_connect_error();
}
$username = $_GET['username'];
$password = $_GET['password'];
$result = mysqli_query($con, "insert into users(`username`, `password`) values ('".addslashes($username)."','".md5($password)."')");
echo "新 id 为: " . mysqli_insert_id($con);
?>
  • 代码二:在二次注入中,第二段中的代码如下所示,首先将GET参数ID转成int类型(防止拼接到SQL语句时,存在SQL注入漏洞),然后到users表中获取ID对应的username,接着到person表中查询username对应的数据。但是此处没有对$username进行转义,在第一步中我们注册的用户名是test',此时执行的SQL语句为:

    复制代码
    select * from person where `username`='test''

    单引号被带入SQL语句中,由于多了一个单引号,所以页面会报错。

7. 宽字节注入

php 复制代码
<?php

$conn = mysql_connect('localhost', 'root', '123456') or die('bad!');
mysql_select_db('test', $conn) OR emMsg("数据库连接失败");
mysql_query("SET NAMES 'gbk'", $conn);

$id = addslashes($_GET['id']);

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

$result = mysql_query($sql, $conn) or die(mysql_error());

$row = mysql_fetch_array($result);

if ($row) {
    echo $row['username']." : ".$row['address'];
} else {
    print_r(mysql_error());
}
?>
</font>
<?php
echo "<br>The Query String is : ".$sql."<br>";
?>

当访问id=1'时,执行的SQL语句为:

sql 复制代码
SELECT * FROM users WHERE id='1\''

可以看到单引号被转义符"\"转义,所以在一般情况下,是无法注入的,但由于在数据库查询前执行了SET NAMESꞌ GBKꞌ,将编码设置为宽字节GBK,所以此处存在宽字节注入漏洞。宽字节的格式是在地址后先加一个%df,再加单引号,因为反斜杠的编码为%5c,而在GBK编码中,%df%5c是繁体字"連",所以这时,单引号成功逃逸,报出MySQL数据库的错误.

8. Cookie注入

php 复制代码
<?php
$id = $_COOKIE['id'];
$value="1";
setcookie("id", $value);
$con=mysqli_connect("localhost", "root", "root","sql");
if (mysqli_connect_errno()) {
    echo "数据库连接错误: " . mysqli_connect_error();
}

$result = mysqli_query($con, "select * from users where 'id'=".$id);
if (!$result) {
    printf("Error: %s\n", mysqli_error($con));
    exit();
}

$row = mysqli_fetch_array($result);
echo $row['username'] . " : " . $row['password'];
echo "<br>";
?>

通过 C O O K I E 能获取浏览器 c o o k i e 中的数据,在 c o o k i e 注入页面中程序通过 _COOKIE能获取浏览器cookie中的数据,在cookie注入页面中程序通过 COOKIE能获取浏览器cookie中的数据,在cookie注入页面中程序通过_COOKIE获取参数ID,然后直接将ID拼接到select语句中进行查询,如果有结果,则将结果输出到页面。这里可以看到,由于没有过滤cookie中的参数ID且直接拼接到SQL语句中,所以存在SQL注入漏洞。当在cookie中添加id=1 union select 1,2,3%23时,执行的SQL语句为:

sql 复制代码
select * from users where `id`=1 union select 1,2,3#

此时,SQL语句可以分为select*from users where`id`=1和union select 1,2,3两条,利用第二条语句(Union查询)就可以获取数据库中的数据。

9. base64注入

php 复制代码
<?php
$id = base64_decode($_GET['id']);
$conn = mysql_connect("localhost", "root", "root");
mysql_select_db("sql", $conn);
$sql = "select * from users where id=$id";
$result = mysql_query($sql);
while ($row = mysql_fetch_array($result)) {
    echo "ID:" . $row['id'] . "<br >";
    echo "user:" . $row['username'] . "<br >";
    echo "pass:" . $row['password'] . "<br >";
    echo "<hr>";
}
mysql_close($conn);
echo "now use" . $sql . "<hr>";
?>

在base64注入页面中,程序获取GET参数ID,利用base64_decode()对参数ID进行base64解码,然后直接将解码后的 i d 拼接到 s e l e c t 语句中进行查询,通过 w h i l e 循环将查询结果输出到页面。由于代码没有过滤解码后的 id拼接到select语句中进行查询,通过while循环将查询结果输出到页面。由于代码没有过滤解码后的 id拼接到select语句中进行查询,通过while循环将查询结果输出到页面。由于代码没有过滤解码后的id,且将$id直接拼接到SQL语句中,所以存在SQL注入漏洞。这种攻击方式还有其他利用场景,例如,如果有WAF,则WAF会对传输中的参数ID进行检查,但由于传输中的ID经过base64编码,所以此时WAF很有可能检测不到危险代码,进而绕过了WAF检测。

10. XFF注入

php 复制代码
<?php
$con = mysqli_connect("localhost", "root", "root", "sql");
if (mysqli_connect_errno()) {
    echo "数据库连接错误: " . mysqli_connect_error();
}
if(getenv('HTTP_CLIENT_IP')) {
    $ip = getenv('HTTP_CLIENT_IP');
} elseif (getenv('HTTP_X_FORWARDED_FOR')) {
    $ip = getenv('HTTP_X_FORWARDED_FOR');
} elseif (getenv('REMOTE_ADDR')) {
    $ip = getenv('REMOTE_ADDR');
} else {
    $ip = $_SERVER['REMOTE_ADDR'];
}
$result = mysqli_query($con, "select * from user where 'ip'='$ip'");
if (!$result) {
    printf("Error: %s\n", mysqli_error($con));
    exit();
}
$row = mysqli_fetch_array($result);
echo $row['username'] . " : " . $row['password'];
echo "<br>";
?>

PHP中的getenv()函数用于获取一个环境变量的值,类似于 S E R V E R 或 _SERVER或 SERVER或_ENV,返回环境变量对应的值,如果环境变量不存在则返回FALSE。程序先判断是否存在HTTP头部参数HTTP_CLIENT_IP,如果存在,则赋给KaTeX parse error: Double subscript at position 31: ...在HTTP头部参数HTTP_X_̲FORWARDED_FOR,如...ip,如果不存在,则将HTTP头部参数REMOTE_ADDR赋给 i p 。接下来,将 ip。接下来,将 ip。接下来,将ip拼接到select语句,然后将查询结果输出到界面上。由于HTTP头部参数是可以伪造的,所以可以添加一个头部参数CLIENT_IP或X_FORWARDED_FOR。当设置X_FORWARDED_FOR=1'union select 1,2,3%23时,执行的SQL语句为:

复制代码
select * from user where `ip`='1' union select 1,2,3#'

此时SQL语句可以分为select*from user where`id`='1'和union select 1,2,3两条,利用第二条语句(union查询)就可以获取数据库中的数据。

相关推荐
Hello.Reader11 小时前
Flink ZooKeeper HA 实战原理、必配项、Kerberos、安全与稳定性调优
安全·zookeeper·flink
智驱力人工智能12 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算
数据与后端架构提升之路12 小时前
论系统安全架构设计及其应用(基于AI大模型项目)
人工智能·安全·系统安全
YUJIANYUE12 小时前
PHP纹路验证码
开发语言·php
麦聪聊数据13 小时前
Web 原生架构如何重塑企业级数据库协作流?
数据库·sql·低代码·架构
市场部需要一个软件开发岗位14 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
lingggggaaaa14 小时前
安全工具篇&动态绕过&DumpLsass凭据&Certutil下载&变异替换&打乱源头特征
学习·安全·web安全·免杀对抗
凯子坚持 c14 小时前
CANN-LLM:基于昇腾 CANN 的高性能、全功能 LLM 推理引擎
人工智能·安全
MZ_ZXD00114 小时前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php
Goat恶霸詹姆斯14 小时前
mysql常用语句
数据库·mysql·oracle