返回介绍

4.1.1 挖掘经验

发布于 2024-10-11 22:07:43 字数 5951 浏览 0 评论 0 收藏 0

SQL 注入经常出现在登录页面、获取 HTTP 头(user-agent/client-ip 等)、订单处理等地方,因为这几个地方是业务相对复杂的,登录页面的注入现在来说大多是发生在 HTTP 头里面的 client-ip 和 x-forward-for,一般用来记录登录的 IP 地址,另外在订单系统里面,由于订单涉及购物车等多个交互,所以经常会发生二次注入。我们在通读代码挖掘漏洞的时候可以着重关注这几个地方。

4.1.1.1 普通注入

这里说的普通注入是指最容易利用的 SQL 注入漏洞,比如直接通过注入 union 查询就可以查询数据库,一般的 SQL 注入工具也能够非常好地利用。普通注入有 int 型和 string 型,在 string 型注入中需要使用单或双引号闭合,下面简单演示普通注入漏洞,后面所有测试 SQL 注入漏洞的数据表中数据都如图 4-1 所示。

图 4-1

测试代码如下:

<?php

$uid=$_GET['id'] ; 

$sql="SELECT * FROM userinfo where id=$uid" ; 

$conn=mysql_connect ( 'localhost' , 'root' , '123456' );  

mysql_select_db ( "test" , $conn );     

$result=mysql_query ( $sql , $conn ); 

print_r ( ' 当前 SQL 语句: '.$sql.'<br /> 结果: ' ); 

print_r ( mysql_fetch_row ( $result ));

测试代码中 GET id 参数存在 SQL 注入漏洞,测试方法如图 4-2 所示。

图 4-2

从截图可以看到原本的 SQL 语句已被注入更改,使用了 union 查询到当前用户。

从上面的测试代码中可以发现,数据库操作存在一些关键字,比如 select from、mysql_connect、mysql_query、mysql_fetch_row 等,数据库的查询方式还有 update、insert、delete,我们在做白盒审计时,只需要查找这些关键字,即可定向挖掘 SQL 注入漏洞。

4.1.1.2 编码注入

程序在进行一些操作之前经常会进行一些编码处理,而做编码处理的函数也是存在问题的,通过输入转码函数不兼容的特殊字符,可以导致输出的字符变成有害数据,在 SQL 注入里,最常见的编码注入是 MySQL 宽字节以及 urldecode/rawurldecode 函数导致的。

1.宽字节注入

在使用 PHP 连接 MySQL 的时候,当设置“set character_set_client=gbk”时会导致一个编码转换的注入问题,也就是我们所熟悉的宽字节注入,当存在宽字节注入漏洞时,注入参数里带入%df%27,即可把程序中过滤的\(%5c)吃掉。举个例子,假设/1.php?id=1 里面的 id 参数存在宽字节注入漏洞,当提交/1.php?id=-1’and 1=1%23 时,MySQL 运行的 SQL 语句为 select*from user where id=’1\’and 1=1#’很明显这是没有注入成功的,我们提交的单引号被转义导致没有闭合前面的单引号,但是我们提交/1.php?id=-1%df’and 1=1%23 时,这时候 MySQL 运行的 SQL 语句为:

select * from user where id='1 運 ' and 1=1#'

这是由于单引号被自动转义成\',前面的%df 和转义字符\反斜杠(%5c)组合成了%df%5c,也就是“運”字,这时候单引号依然还在,于是成功闭合了前面的单引号。

出现这个漏洞的原因是在 PHP 连接 MySQL 的时候执行了如下设置:

set character_set_client=gbk

告诉 MySQL 服务器客户端来源数据编码是 GBK,然后 MySQL 服务器对查询语句进行 GBK 转码导致反斜杠\被%df 吃掉,而一般都不是直接设置 character_set_client=gbk,通常的设置方法是 SET NAMES'gbk',但其实 SET NAMES'gbk'不过是比 character_set_client=gbk 多干了两件事而已,SET NAMES'gbk'等同于如下代码:

SET

character_set_connection='gbk' , 

character_set_results='gbk' , 

character_set_client=gbk

这同样也是存在漏洞的,另外官方建议使用 mysql_set_charset 方式来设置编码,不幸的是它也只是调用了 SET NAMES,所以效果也是一样的。不过 mysql_set_charset 调用 SET NAMES 之后还记录了当前的编码,留着给后面 mysql_real_escape_string 处理字符串的时候使用,所以在后面只要合理地使用 mysql_real_escape_string 还是可以解决这个漏洞的,关于这个漏洞的解决方法推荐如下几种方法:

1)在执行查询之前先执行 SET NAMES'gbk',character_set_client=binary 设置 character_set_client 为 binary。

2)使用 mysql_set_charset('gbk')设置编码,然后使用 mysql_real_escape_string() 函数被参数过滤。

3)使用 pdo 方式,在 PHP5.3.6 及以下版本需要设置 setAttribute(PDO::ATTR_EMULATE_PREPARES,false);来禁用 prepared statements 的仿真效果。

如上几种方法更推荐第一和第三种。

下面对宽字节注入进行一个简单测试。

测算代码如下:

<?php

$conn=mysql_connect ( 'localhost' , 'root' , '123456' );  

mysql_select_db ( "test" , $conn );   

mysql_query ( "SET NAMES 'gbk'" , $conn ); 

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

$sql="SELECT * FROM userinfo where id='$uid'" ; 

$result=mysql_query ( $sql , $conn ); 

print_r ( ' 当前 SQL 语句: '.$sql.'<br /> 结果: ' ); 

print_r ( mysql_fetch_row ( $result )); 

mysql_close ();

当提交/1.php?id=%df'union select 1,2,3,4%23 时,成功注入的效果如图 4-3 所示。

图 4-3

对宽字节注入的挖掘方法也比较简单,只要搜索如下几个关键字即可:

SET NAMES

character_set_client=gbk

mysql_set_charset ( 'gbk' )

2.二次 urldecode 注入

只要字符被进行转换就有可能产生漏洞,现在的 Web 程序大多都会进行参数过滤,通常使用 addslashes()、mysql_real_escape_string()、mysql_escape_string() 函数或者开启 GPC 的方式来防止注入,也就是给单引号(')、双引号(")、反斜杠(\)和 NULL 加上反斜杠转义。如果某处使用了 urldecode 或者 rawurldecode 函数,则会导致二次解码生成单引号而引发注入。原理是我们提交参数到 WebServer 时,WebServer 会自动解码一次,假设目标程序开启了 GPC,我们提交/1.php?id=1%2527,因为我们提交的参数里面没有单引号,所以第一次解码后的结果是 id=1%27,%25 解码的结果是%,如果程序里面使用了 urldecode 或者 rawurldecode 函数来解码 id 参数,则解码后的结果是 id=1’单引号成功出现引发注入。

测试代码:

<?php

$a=addslashes ( $_GET['p'] ); 

$b=urldecode ( $a ); 

echo '$a='.$a ; 

echo '<br />' ; 

echo '$b='.$b ;

测试效果如图 4-4 所示。

图 4-4

既然知道了原理主要是由于 urldecode 使用不当导致的,那我们就可以通过搜索 urldecode 和 rawurldecode 函数来挖掘二次 urldecode 注入漏洞。

4.1.1.3 espcms 搜索注入分析

这里以一个笔者在 2013 年发现的一个小 CMS 程序 espcms 搜索注入的漏洞为例,我们目前尽量以相对好理解的漏洞来举例。

漏洞在 interface/search.php 文件和 interface/3gwap_search.php 文件 in_taglist() 函数都存在,一样的问题,以 interface/search.php 为例说明:

打开文件看到如下代码:

function in_taglist () {

    parent :: start_pagetemplate (); 

    include_once admin_ROOT . 'public/class_pagebotton.php' ; 

    $page = $this->fun->accept ( 'page' , 'G' ); 

    $page = isset ( $page )
 
? intval ( $page )
 
: 1 ; 

    $lng = ( admin_LNG == 'big5' )
 
? $this->CON['is_lancode'] : admin_LNG ; 

    $tagkey = urldecode ( $this->fun->accept ( 'tagkey' , 'R' )); 

    $takey = $this->fun->inputcodetrim ( $tagkey ); 

    $db_where = ' WHERE lng=\'' . $lng . '\' AND isclass=1' ; 

    if ( empty ( $tagkey )) {

    $linkURL = $_SERVER['HTTP_REFERER'] ; 

    $this->callmessage ( $this->lng['search_err'] , $linkURL , $this->lng ['gobackbotton'] ); 

    }

    if (! empty ( $tagkey )) {

    $db_where.=" AND FIND_IN_SET ( '$tagkey' , tags ) " ; 

    }

其中:

$tagkey = urldecode ( $this->fun->accept ( 'tagkey' , 'R' ));  

这行代码得到$_REQUEST['tagkey']的值,由于$tagkey 变量使用了 urldecode,从而可以绕过 GPC:

$db_where.=" AND FIND_IN_SET ( '$tagkey' , tags ) " ;

经过判断$tagkey 不为空则拼接到 SQL 语句中,导致产生注入漏洞。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文