0x00 背景

SQL注入是一种常见Web漏洞,所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。本文以代码审计的形式研究SQL注入原理、挖掘形式、防御方案及缺陷。

0x01 SQL注入产生原理

SQL注入与其他常见Web漏洞一样,均是由外部可控的参数引起的。由于程序没有经过任何过滤就将外部可控的参数拼接进入SQL语句,直接放入数据库执行,达到了欺骗服务器执行黑客恶意SQL命令的目的。在这里我们采用DVWA中low级别的源码学习SQL注入的产生原理。

<?php

if( isset( $_REQUEST[ 'Submit' ] ) ) {
    // Get input
    $id = $_REQUEST[ 'id' ];     // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );     // Get results
    while( $row = mysqli_fetch_assoc( $result ) ) {
        // Get values
        $first = $row["first_name"];
        $last  = $row["last_name"];         // Feedback for end user
        echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
    }     mysqli_close($GLOBALS["___mysqli_ston"]);
} ?> 

  在这里我们注意到变量id是由用户进行掌控的变量,用户所输入的id值并没有进行任何的过滤,直接拼接到SQL语句中执行,我们重点关注这条SQL语句:

$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

  此处,如果我们所输入的变量id为1' or 1=1 or '1,那么此条SQL语句就变为了:

$query  = "SELECT first_name, last_name FROM users WHERE user_id = '1' or 1=1 or '1';";

  这样,数据库就会根据上面这条语句执行操作,这就是SQL注入漏洞的产生原理。

0x02 SQL注入的挖掘形式

SQL注入往往出现在用户登陆、信息查询等页面,通常在HTTP头中会出现漏洞,例如Cookie值、用于获取用户IP的client-ip中,在代码审计中我们需要着重关注这几个模块。

普通注入

普通形式的SQL注入例如上文中提到的,直接通过联合查询就可以对数据库进行操作,这种注入形式往往容易被扫描器检测出来,在这里不过多进行讲解。在代码审计中,我们只需要关注一些关键字,例如select from、mysql_connect、mysql_query、update、delete、insert等即可。

编码注入

编码注入包括宽字节注入、URLdecode注入等,利用程序的编码规则缺陷,输入与转码函数不兼容的特殊字符,导致输入的字符拼接成为了恶意的SQL语句。

1.宽字节注入

宽字节注入是利用mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围),当PHP连接MySQL的时候,设置了“set_character_set_client=gkb”时,往往就会产生宽字节注入。

例如以下程序:

<?php
//连接数据库部分,注意使用了gbk编码,把数据库信息填写进去
$conn = mysql_connect('localhost', 'root', 'toor!@#$') or die('bad!');
mysql_query("SET NAMES 'gbk'");
mysql_select_db('test', $conn) OR emMsg("连接数据库失败,未找到您填写的数据库");
//执行sql语句
$id = isset($_GET['id']) ? addslashes($_GET['id']) : 1;
$sql = "SELECT * FROM news WHERE tid='{$id}'";
$result = mysql_query($sql, $conn) or die(mysql_error()); //sql出错会报错,方便观察
?> <html>
<head>
<meta charset="gbk" />
<title>新闻</title>
</head>
<body>
<?php
$row = mysql_fetch_array($result, MYSQL_ASSOC);
echo "<h2>{$row['title']}</h2><p>{$row['content']}<p>\n";
mysql_free_result($result);
?>
</body>
</html> 

以上程序中,sql语句是SELECT * FROM news WHERE tid='{$id}。参数id存在宽字节注入漏洞,程序采用addslashes函数,将$id的值转义。addslashes函数产生的效果就是,让“ ’ ”变成“ \’ ”,对此我们要想进行注入需要绕过“\”。

我们提交/test.php?id=-1' and 1=1#时,数据库执行的操作是select * from news where id = '1\' #,显然这里无法进行注入,但是我们提交/test.php?id=-1 %df ' and 1=1#时,数据库执行的操作就变成了select * from user where id = '1運' and 1=1 #。这样我们就可以实现SQL注入了。

这是因为单引号被addslashes转义成为\',我们提交的%df与\(url编码为%5c)组合成为了%df%5c,也就是gbk编码中的“運”字,组合之后被转义的“ \' ”中的“ ‘ ”还存在,成功闭合了之前的单引号。

挖掘这类的SQL注入漏洞只需要搜索关键词,确认是否采用了gbk编码即可,关键词主要有以下三种:

character_set_client=gkb

mysql_set_charset('gbk')

SET NAMES 'gbk'     //这条语句等同于如下代码:

SET
character_set_connect='gbk',
character_set_results='gbk',
character_set_client=gbk;

宽字节注入除了上述方法外,有时为了避免乱码,程序员使用iconv()函数将GBK编码转换为utf-8编码,例如:

mysql_query(“set names UTF-8”) ;
$bar =iconv(“GBK”,”UTF-8”, addslashes($_GET[‘’bar])) ;

例如我们提交http://127.0.0.1/test.php?id=1%e5%5c%27,%e5%5c%27经过addslashes()变为%e5%5c%5c%5c%27,再经过iconv()变为%e9%8c%a6%5c%5c%27,这样我们所提交的请求多了一个%5c,反斜杠本身被转义,单引号(%27)生效。

2.URLdecode注入

现在绝大多数基于PHP的Web程序都会使用addslashes()等过滤函数对用户提交的变量等进行过滤,如果某处采用了urldecode()函数进行了url解码,那么将会大概率的导致URLdecode注入,例如以下代码:

<?php
$a=addslashes($_GET['c']);
$b=urldecode($a);
echo '$a='.$a;
echo '$b='.$b;
?>

我们在地址栏提交/test.php?c=1%2527时,得到的结果是$a=1%27  $b=1'

这是因为%25的解码结果为“ % ”,与后面的27拼接得到%27,也就是单引号,因此产生了注入。

所以在代码审计中挖掘urldecode注入时,只需要搜过两个关键函数即可:

urldecode

rawurldecode

0x03 防御方案及缺陷

黑名单过滤

黑名单过滤是一种原始的、低效甚至无效的过滤手段,将关键字select等只使用replace()函数置换为空,企图达到阻止SQL注入的发生。

这种防御方案的缺陷非常明显,我们只需要将关键字双向,例如seleselectct(过滤掉中间完整的select之后剩余内容仍是select),也可以大小写绕过的方式,例如SeLeCt(数据库大小写不敏感),以此绕过  防御实现sql注入。

魔术引号

在PHP4.2.3以上版本,可以开启魔法引号自动过滤

magic_quotes_gpc开启后,会在GET、POST、COOKIE中的单引号、双引号、反斜杠、空字符的前面加上反斜杠( \ ),但在PHP5中对$_SERVER变量停止了过滤,导致client-ip等容易被利用。

magic_qutoes_runtime开启后,与GPC的过滤方案一致,区别是它对数据库和数据文件进行过滤,而非GET、POST、COOKIE值。

在PHP5.4中,魔术引号被取消。

过滤函数

1.addslashes

addslashes是当前常见的一种过滤方式,过滤的内容和范围与GPC一致,不过该函数的参数必须是string型。例如:

<?php
$str=" 1' ";
echo addslashes($str);
?>

得到的结果是1\‘。

2.mysql_escape_string

在PHP4.0.3以上版本中引入了mysql_escape_string函数和mysql_real_escape_string函数,这两个函数也是对字符串进行过滤,受函数影响的字符包括单引号、双引号、\、\n、\r等。例如:

<?php
$con = mysql_connect("localhost","root","password");
$id = mysql_real_escape_string($_GET['id'],$con);
echo `select * from table where id = ' ".$id." '`;
?>

当用户发出请求/test.php?id=1'时,数据库做以下处理:select * from test where id = '1\"。

PDO预编译

PDO prepare预编译类似于Java中的preparestatement,采用预编译的形式处理数据库查询。

<?php
$dbms='mysql'; //数据库类型
$host='localhost'; //数据库主机名
$dbName='test'; //使用的数据库
$user='root'; //数据库连接用户名
$pass='pass'; //对应的密码
$dsn="$dbms:host=$host;dbname=$dbName";
$dsn -> exec("SET NAME 'gbk'");
$sql = "select * from table where user=? and pass=?";
$pstmt = $dbh -> prepare($sql);
$exeres = $pstmt -> excute (array($user,$pass));

  以上代码虽然采用了PDO进行预编译,但是再PHP5.3.6之前仍然会存在宽字节注入漏洞,因为这样查询是PHP本地模拟prepare,然后再把完整的sql语句发送给数据库,php与MySQL编码不一致导致宽字节注入漏洞的产生,所以我们需要禁止本地模拟prepare,在上述代码中加入一行即可:

$dsn -> setAttribute(PDO::ATTER_EMULATE_PREPARES, flase);

  

iconv

代码审计中的SQL注入的更多相关文章

  1. 在php中防止SQL注入的方法

    摘要:我们php手手工安装的,php的默认配置文件在 /usr/local/apache2/conf/php.ini,我们最主要就是要配置php.ini中的内容,让我们执行 php能够更安全.整个PH ...

  2. Python中防止sql注入的方法详解

    SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚至篡改数据库.下面这篇文章主要给大家介绍了关于Python中 ...

  3. php代码审计3审计sql注入漏洞

    SQL注入攻击(sql injection)被广泛用于非法获取网站控制权,在设计程序时,忽略或过度任性用户的输入,从而使数据库受到攻击,可能导致数据被窃取,更改,删除以及导致服务器被嵌入后门程序等 s ...

  4. Django中的sql注入

    Django中防止SQL注入的方法 方案一总是使用Django自带的数据库API.它会根据你所使用的数据库服务器(例如PostSQL或者MySQL)的转换规则,自动转义特殊的SQL参数.这被运用到了整 ...

  5. php代码审计--sql注入

    sql注入是web安全中最常见,也是平常中危害最大的漏洞. 最近在学习代码审计,拿自己审核的一段代码做个笔记. 1.sql语句拼接可能引起sql注入 很多偷懒的程序员对于没有过滤的参数,直接将其拼接到 ...

  6. Java代码审计连载之—SQL注入

    前言近日闲来无事,快两年都没怎么写代码了,打算写几行代码,做代码审计一年了,每天看代码都好几万行,突然发现自己都不会写代码了,真是很DT.想当初入门代码审计的时候真是非常难,网上几乎找不到什么java ...

  7. 2020/1/27代码审计学习之SQL注入漏洞

    PHP代码审计SQL注入漏洞 0x00 首先明确什么是SQL注入,SQL语句必须掌握. 常见的注入总的来说可以分为两大类:数字型和字符型. 这两类中包含了诸如报错注入,宽字节注入,盲注,二次注入,co ...

  8. php中防止SQL注入的方法

    [一.在服务器端配置] 安全,PHP代码编写是一方面,PHP的配置更是非常关键. 我们php手手工安装的,php的默认配置文件在 /usr/local/apache2/conf/php.ini,我们最 ...

  9. 转:PHP中防止SQL注入的方法

    [一.在服务器端配置] 安全,PHP代码编写是一方面,PHP的配置更是非常关键. 我们php手手工安装的,php的默认配置文件在 /usr/local/apache2/conf/php.ini,我们最 ...

随机推荐

  1. HTML5 可缩放矢量图形(1)—SVG基础

    参考文档1 SVG基础 SVG介绍 概念:SVG 是使用 XML 来描述二维图形和绘图程序的语言.(理解就是一个在网页上使用笔画图的过程) 什么是SVG SVG 指可伸缩矢量图形 (Scalable ...

  2. 题解 P1317 【低洼地】

    题目 这题挺简单的,没必要用数组 [分析] 需要判断的是低洼地的数量 通过对题目中图进行分析,显然可以发现低洼地的定义: 若数组中存在一个数值相同的连续区间,这个区间端点外相邻两点的数值都大于该区间的 ...

  3. 吴裕雄--天生自然MySQL学习笔记:MySQL 事务

    MySQL 事务主要用于处理操作量大,复杂度高的数据.比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成 ...

  4. mysql的常见面试问题

    1.如何登陆mysql数据库 MySQL -u username -p 2.如何开启/关闭mysql服务 service mysql start/stop 3.查看mysql的状态 service m ...

  5. vim里设置tab及自动换行

    今天在使用vim编辑器时发现默认的tab键是8个字符,于是就想到把它设为四个空格,经过百度,得到了以下方法: 首先进入~/.vimrc 然后在文档末尾加上以下代码: set tabstop=4    ...

  6. (递归)P1036 选数

    #include<stdio.h>#include<math.h>int x[20],n,k,i; //判断是否质数 int isprime(int n){    for(i= ...

  7. Fiddler 断点命令

    Request 断点:bpu /priceCalculate 清除命令:bpu Response 断点:bpafter /priceCalculate 清除命令:bpafter

  8. Eclipse上传Git远程仓库,并且增加Maven Dependencies

    前言: 遇见问题了,公司一台电脑,家里一台电脑,当有项目在进行的时候,又不想把电脑背来背去的,就像一个人玩单机,这个时候GIT就可以帮你解决这个问题.当GIT准备就绪的时候,新的问题来了git下载下载 ...

  9. AES加密中遇到的坑

    项目背景: 终端数据需要上传到后台,为了安全,使用了AES加密 问题1: 数据首先经过了protobuf进行格式,数据传输到后台无法解析 解决:二进制数据直接进行AES加密后大多无法正常解析,于是在加 ...

  10. 挑战目标跟踪算法极限,SiamRPN系列算法解读

    商汤科技智能视频团队首次开源其目标跟踪研究平台 PySOT.PySOT 包含了商汤科技 SiamRPN 系列算法,以及刚被 CVPR2019 收录为 Oral 的 SiamRPN++.此篇文章将解读目 ...