【代码审计】SQL注入简单学习审计和分析

前言

  • 代码审计学习记录

代码审计流程

  • -反向查找流程

    • –通过可控变量(输入点)回溯危险函数
    • –查找危险函数确定可控变量
    • –传递的过程中触发漏洞
  • -案例

  • -反向查找流程特点

    • –上下文无关
    • –危险函数,调用即漏洞
  • -使用反向查找流程挖掘漏洞

    • –危险函数全局查找
    • –自动化审计工具(RISP,VCG,Fortify SCA,Cobra,grepbugs,Sky wolf, Taint)
  • -根源:危险函数导致漏洞

  • -特点

    • –暴力:全局搜索危险函数
    • –简单:无需过多理解目标网站功能与架构
    • –快速:适用于自动化代码审计工具
    • –命中率低:简单的漏洞越来越少
    • –无法挖掘逻辑漏洞:逻辑漏洞多数不存在危险函数,或危险函数的参数“看似”不可控
    • –适应性交叉:不适合存在全局过滤的站点
  • -正向查找流程(MVC架构 Model View Controller)Controller主要分发处理请求逻辑,Model专门处理数据库相关操作,View显示给用户的一些内容

    • – 从入口点函数出发
    • – 找到控制器,理解URL派发规则
    • – 跟踪控制器调用,以理解代码为目标
    • – 阅读代码的过程中,可能发现漏洞
  • -案例

    • – PHPCMS v9.6.0 前台GetShell漏洞
  • -根源:程序员疏忽或逻辑问题导致漏洞

  • -特点:

    • – 复杂:需要及其了解目标源码的功能与架构
    • – 跳跃性大: 涉及M/V/C/Service/Dao等多个层面
    • – 漏洞的组合:通常是多个漏洞的组合,很可能存在逻辑相关的漏洞
    • – 潜力无限:安全研究人员的宝库
  • -双向查找流程

    • – 阅读代码,了解架构
    • – 是否有全局过滤机制?
      • — 有:是否可以绕过?
        • —- 可以:寻找漏洞触发点
        • —- 不可以: 寻找没有过滤的变量
      • — 没有:那么它是如何处理的?
        • —- 完全没有处理:可以挖成筛子
        • —- 有处理:寻找遗漏的处理点
    • – 找到了漏洞点,漏洞利用是否有坑?
      • — 否:成功利用!
      • — 是:利用所知的语言知识(trick)解决问题。

image-20211014095411276

  • -根源:理解程序执行过程,找寻危险逻辑
  • -特点:
    • – 高效:如挖隧道,双向开工,时间减半
    • – 知识面广:需要同时掌握正向,反向挖掘技巧,并进行结合
    • – 以及所有正向、反向的优点

PHP-SQL注入漏洞挖掘技巧

  • -PHP+Mysql连接方法

    • – Mysql(废弃)
    • – Mysqli
    • – PDO
  • -SQL注入漏洞常见过滤方法

    1
    2
    3
    4
    5
    6
    7
    -- intval / addslashes / mysql_real_escape

    -- mysqli_escape_string / mysqli_real_escape_string / mysqli::escape_string

    -- PDO::quote

    -- 参数化查询
  • -addslashes / mysql_real_escape

    • – 宽字符注入
    • – 寻找字符串转换函数
      • — urldecode
      • — base64_decode
      • — iconv
      • — json_decode
      • — stripshasles
      • — simple_xml_loadstring
1
2
3
4
5
6
7
8
9
10
11
<?php 
$id = addslashes($_GET['id']);
// ' ==> \'
// \ ==> \\
// " ==> \"
// \x00 ==> \0


$sql = "SELECT * FROM dual WHERE id = '$id';";
echo $sql;
?>
  • -mysqli::escape_string / PDO::quote

    • – 与addslashes差别: 是否会主动加引号包裹
    • – 宽字节注入
  • -参数化查询

    • – 寻找非sql值的位置
    • – SELECT name from users where id = ? ORDER BY Login_time Limit 1
  • -案例:贷齐乐系统header注入

  • -略读代码,了解架构

    • – 非MVC架构
    • – 全局GPC转义
  • -目标:找到没有进行过滤的输入点

  • -结果:$SERVER[HTTP\*]均无过滤导致注入

  • -入手点

    • – 开发者不熟悉的边缘功能
    • – 常被复制粘贴代码的功能
  • -案例:ThinkSNS某版本SQL注入漏洞

  • -略读代码,了解架构

    • – 基于ThinkPHP3.1开发
    • – MVC架构
    • – 利用t函数过滤变量
  • -目标:找到t函数过滤完成以后也可以注入的点

  • -结果:表名位置SQL注入漏洞

  • -案例:Metinfo企业网站管理系统SQL注入漏洞

  • -略读代码,了解架构

    • – 非MVC架构
    • – 全局覆盖的方式注册变量
    • – 全局GPC转义
  • -目标:获取绕过全局GPC的方法

  • -结果:利用base64_decode来引入单引号

上方部分总结

  • -开发者容易遗漏的输入点
  • -HTTP头
    • – X-Forwarded-For
    • – User-Agent
    • – Referer
  • -PHP_SELF

image-20211014201202526

  • -REQUEST_URI

image-20211014201315915

  • -文件名 $_FILES[][name]

  • -php://input

  • -引入单引号(转义符)的方法

    • – stripslashes #(' 转换为 等等)
    • – base64_decode
    • – urldecode
    • – substr
    • – iconv
    • – str_replace(‘0’,’’,$sql)
    • – xml
    • – json_encode

简单分析SQL注入的诱因

前言配置

我的common.php中配置

image-20211016085311962

1
2
3
4
5
6
7
8
9
<?php

$mysql_server="localhost";
$mysql_username="root";
$mysql_password="root";
$mysql_database="test";

$conn = mysqli_connect($mysql_server,$mysql_username,$mysql_password,$mysql_database) or die("数据库链接错误");
?>

我的mysql中表的值

image-20211016085216387

第一个SQL代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include_once('./common.php');
try{
$name = $_GET['name'];
$query = "SELECT name, age, email, country FROM user_details where name = '{$name}';";
$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

这题明显没有任何过滤,并且是最基础的sql注入漏洞直接联合查询就可以搞过

image-20211015170030656

第二个SQL代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include_once('./common.php');
try{
$name = addslashes($_GET['name']);
$query = "SET NAMES gbk;SELECT name, age, email, country FROM user_details where name = '{$name}';";
$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

这题加了一个addslashes将单引号转义了,在单引号前面加了个\所以这题是无法注入的,宽字节注入得是上下文gbk编码什么能够宽字节编码的,所以这题也无法注入,应该说,以我技术看不出来。

image-20211015171150439

第三个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


<?php
include_once('./common.php');
try{
$name = htmlspecialchars($_GET['name']);
$query = "SELECT name, age, email, country FROM user_details where name = '{$name}';";
echo $query."<br>";

$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

分析一下htmlspecialchars,查文档说将特殊符号转换为html实体编码,处理xss漏洞的一个防御函数

image-20211015172648933

开发者因为没有足够理解这个函数的关于单引号的触发机制导致漏洞,因为必须得设置ENT_QUOTES参数设置后才能转单引号,所以可以直接sql注入

image-20211015172741090

第四个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
include_once('./common.php');
try{
$age = addslashes($_GET['age']);
$query = "SELECT name, age, email, country FROM user_details where age > {$age};";
echo $query."<br>";

$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

很明显的开发者因为数字比较,忽略单引号闭合,所以也不需要用单引号逃逸,所以这题也存在盲注和联合查询注入,addslashes相当于无用了

image-20211015214239719

第五个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
include_once('./common.php');

try{
$name = str_replace("'","\\'",$_GET['name']);
$query = "SELECT name, age, email, country FROM user_details where name = '{$name}';";
echo $query."<br>";

$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

和addslashes差不多的意思,str_replace函数将子字符串的替换,将单引号替换成\加上单引号。但是和addslashes也有很大的区别,因为它会将\也变成双\,但是str_replace就只会变一次

举例

1
2
3
4
5
6
addslashes会将我们传入的数据?name=一号\‘
转换成 ==> 一号\\\' (导致我们无法逃逸单引号)

但是开发者用str_replace时,会导致我们可以sql注入
当传入?name=一号\'
会解析成 ==> 一号\\' (单引号前的\会被转义让我们的'得以逃逸,形成sql注入)

image-20211015220249634

第六个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
include_once('./common.php');
try{
$id = intval($_GET['id']);
$query = "SELECT name, age, email, country FROM user_details where id = '{$id}';";
echo $query."<br>";

$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

这个也并没有发现能够注入点,intval会将其中的字符给去掉。

第七个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
include_once('./common.php');
try{
if (intval($_GET['id'])){
$query = "SELECT name, age, email, country FROM user_details where id = {$_GET['id']};";
echo $query."<br>";

$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}
}

} catch (PDOException $e) {
echo $e->getMessage();
}

并没有将过滤后的语句传入sql语句,所以这个intval等于无用,直接注入。

image-20211016081132609

第八个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
include_once('./common.php');
try{
if (!is_numeric($_GET['id'])){
header("Status: 404 Not Found");
}
$query = "SELECT name, age, email, country FROM user_details where id = {$_GET['id']};";
echo $query."<br>";

$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

逻辑错误的分析,is_numeric会检测,如果是数字就可以执行,如果不是数字,就会弹404页面。但是解析器会继续向下运行,所以会导致SQL注入

image-20211016081923564

第九个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
include_once('./common.php');
try{
if (!is_numeric($_GET['id'])){
header("Status: 404 Not found");
exit;
}
$query = "SELECT name, age, email, country FROM user_details where id = {$_GET['id']};";
echo $query."<br>";

$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

在if中加了个exit,让解析器无法继续执行,所以这个是无法注入的

第十个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
include_once('./common.php');
try{
$order = addslashes($_GET['order']);
$query = "SELECT name, age, email, country FROM user_details ORDER BY id {$order};";
echo $query."<br>";

$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

分析:因为这里的$order传参是控制 查询出来的结果是升序(asc)或者降序(desc)

image-20211016084152095

这里我们可以控制order导致sql盲注,因为这里没有报错信息,所以无法报错注入,利用,添加排序字段实现注入语句使用

1
2
3
,if(1=1,sleep(1),0)
或者是
and(if(1=1,sleep(1),0)) # 注意这里得用括号

image-20211016084408135

第十一个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
include_once('./common.php');
try{
$order = addslashes($_GET['order']);
if (!preg_match('/DESC|ASC/i', $order)){
exit("Bad order");
}

$query = "SELECT name, age, email, country FROM user_details ORDER BY id {$order};";
echo $query."<br>";

$stmt = $conn->prepare($query); // stmt = PDOStatement 预处理状态
$stmt->execute();

$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

分析:就是第十个代码的添加了一个过滤,必须包含asc或者desc,但是这过滤并不全,并且也没有太多限制,用,添加多一个asc就可以绕过了,如果你想让这个代码变得无法SQL注入的话

1
2
3
4
payload=asc,if(1=1,sleep(1),1)

修改代码使其无法注入
!preg_match('/^(DESC|ASC)$/i', $order)

image-20211016084829538

第十二个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
include_once('./common.php');

try{
$name = $_GET['name'];

$query = "SELECT name, age, email, country FROM user_details where name = ?;";
echo $query."<br>";

$stmt = $conn->prepare($query);
$stmt->bindValue("name", $name);

$stmt->execute();
$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

标准的预编译代码,我无法注入

第十三个SQL注入代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
include_once('./common.php');

try{
$name = addslashes($_GET['name']);
$name = urldecode($name);

$query = "SELECT name, age, email, country FROM user_details where name = '{$name}';";
echo $query."<br>";

$stmt = $conn->prepare($query);

$stmt->execute();
$stmt->bind_result($name, $age,$email,$country);

while ($stmt->fetch()){
echo "$email"."<br>";
}

} catch (PDOException $e) {
echo $e->getMessage();
}

分析:这题其实不加urldecode是无法注入的,但是urldecode可以让我们绕过addslashes,从而绕过单引号

1
2
3
’ ==> %27 #单次url编码   因为浏览器会自动解码一次
‘ ==> %25%32%37 #双次url编码
payload=一号%25%32%37 union select 1,2,user(),4 %23

image-20211016090452187

我的个人博客

孤桜懶契:http://gylq.gitee.io

本文标题:【代码审计】SQL注入简单学习审计和分析

文章作者:孤桜懶契

发布时间:2021年10月14日 - 09:13:08

最后更新:2022年05月20日 - 11:47:45

原始链接:https://gylq.gitee.io/posts/172.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------------本文结束 感谢您的阅读-------------------