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

sql注入代码分析

文章目录

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

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

1. Union注入


$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 "
"
; ?>

当访问id=1 union select 1,2,3时,执行的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. 布尔盲注


$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. 报错注入


$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. 时间盲注


$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("no");
}

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

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

5. 堆叠查询注入


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语句为:

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')。
    

$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. 宽字节注入



$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 "
The Query String is : "
.$sql."
"
; ?>

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

SELECT * FROM users WHERE id='1\''

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

8. Cookie注入


$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 "
"
; ?>

通过 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语句为:

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注入


$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'] . "
"
; echo "user:" . $row['username'] . "
"
; echo "pass:" . $row['password'] . "
"
; echo "
"
; } mysql_close($conn); echo "now use" . $sql . "
"
; ?>

在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注入


$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 "
"
; ?>

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查询)就可以获取数据库中的数据。

你可能感兴趣的:(web安全,web安全,sql,oracle,安全,php)