SQL注入漏洞,产生的直接原因就是服务器过于信任用户输入,将用户输入直接拼入SQL查询语句,返回了大量用户的敏感信息。
今天在这里,要分享的是我在代码审计学习中关于sql注入漏洞的研究,因为在代码审计领域还处于入门阶段,故此文可归为扫盲类,大神可以过过过。
(2)编码绕过GPC
GPC,即magic_quotes_gpc。在开启情况下,如果输入的数据有单引号(’)、双引号(”)、反斜线()与 NUL(NULL 字符)等字符都会被加上反斜线进行转义处理,和addslashes函数功能相同。代码修改为如下:
$query="select * from book where id = '$id'";
$result=mysql_query($query);
由于GPC会对单引号’进行转义为’,导致不能注入sql语句。但是仍然有一些地方没有魔术引号的保护,如$_SERVER变量、$_FILES变量、getenv()得到的变量、$HTTP_RAW_POST_DATA与PHP输入、输出流等,同时一些变量的编码与解码使用不当也可以造成绕过GPC的保护,如base64_decode、urldecode、rawurldecode以及反序列化函数unserialize等。
(3)宽字节注入
宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而导致的注入漏洞。具体原理如下:
-
- 正常情况下当GPC开启或使用addslashes函数过滤GET或POST提交的参数时,黑客使用的单引号 ‘ 就会被转义为: ’;
- 如果存在宽字节注入,我们输入%df%27时首先经过上面提到的单引号转义变成了%df%5c%27(%5c是反斜杠),之后在数据库查询前由于使用了GBK多字节编码,即在汉字编码范围内两个字节会被编码为一个汉字。然后MySQL服务器会对查询语句进行GBK编码即%df%5c转换成了汉字“運”(注:GBK的汉字编码范围见附录),而单引号逃逸了出来,从而造成了注入漏洞。
这里将id变量从UTF-8转为gbk,因为反斜杠的GBK编码为5c,“錦”这个字的gbk编码是e55c,刚好出现5C,会对GPC转义单引号的反斜杠进行转义,从而单引号不会被转义,造成注入
除了上述这些示例,还可以关注二次攻击造成的注入、魔术引号针对数组类型只处理值而忽略key的情况。
1.WillNews(Mobile新闻系统)
关注一处输入处理:
$id=sqlReplace(Trim($_GET['id']));
$sqlStr="select * from wiinews_news where news_id=$id";
$result = mysql_query($sqlStr) or die ("查询失败,请检查 SQL 语句。编码号:1010");
$row = mysql_fetch_array($result);
跟进sqlReplace函数:
function sqlReplace($str)
{
$strResult = $str;
if(!get_magic_quotes_gpc())
//如果 gpc 没有开的话
{
$strResult = addslashes($strResult);
//编码
}
return HTMLEncode($strResult);
//gpc 开的话,返回 HTMLEncode()
}
判断了gpc是否开启,如果magic_quotes_gpc=On,PHP解析器就会自动为post、get、cookie过来的数据增加转义字符“”,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符(认为是php的字符)引起的污染。如果gpc没开,就会php内置函数addslashes进行处理,如果开启了,使用函数HTMLEncode处理,跟进一下函数:
function HTMLEncode($str){
if (!empty($str)){
$str=str_replace("&","&",$str);
$str=str_replace(">",">",$str);
$str=str_replace("<","<",$str);
$str=str_replace(CHR(32)," ",$str);
$str=str_replace(CHR(9)," ",$str);
$str=str_replace(CHR(9)," ",$str);
$str=str_replace(CHR(34),""",$str);
$str=str_replace(CHR(39),"'",$str);
$str=str_replace(CHR(13),"",$str);
$str=str_replace(CHR(10),"",$str);
}
return $str;
}
此时,可以发现代码中拦截了CHR(9)tab空格、CHR(10)换行、CHR(13)回车、CHR(32)空格、CHR(34)双引号、CHR(39)单引号,未拦截select、from、and等函数。则可利用宽字节、十六进制等绕过限制。单引号的宽字节:%bf%27 %df%27 %aa%27,如下列payload:
select column_name from information_schema.tables where table_name="users"
改换成:select column_name from information_schema.tables where table_name=0x7573657273
2.tpshop注入
首先查看index.php:
if (extension_loaded('zlib')){
ob_end_clean();
ob_start('ob_gzhandler');
} //
检测PHP环境
if(version_compare(PHP_VERSION,'5.3.0','<')) die('require PHP > 5.3.0 !');
//检测是否已安装TPshop系统
if(file_exists("./Install/") && !file_exists("./Install/install.lock")){
if($_SERVER['PHP_SELF'] != '/index.php'){
header("Content-type: text/html; charset=utf-8");
exit("请在域名根目录下安装,如:<br/>
www.xxx.com/index.php 正确 <br/>
www.xxx.com/www/index.php 错误,域名后面不能圈套目录, 但项目没有根目录存放限制,可以放在任意目录,apache虚拟主机配置一下即可");
}
header('Location:/Install/index.php');
exit();
}
error_reporting(E_ALL ^ E_NOTICE);//显示除去 E_NOTICE 之外的所有错误信息
// 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false
define('APP_DEBUG',false);
// 定义应用目录
define('APP_PATH','./Application/');
// 定义插件目录
define('PLUGIN_PATH','plugins/')
这里跟进应用目录,关注其中一个文件File:Application/Home/Controller/ApiController.class.php
class ApiController extends Controller {
/*
* 获取地区
*/
public function getRegion(){
$parent_id = I('get.parent_id');
$selected = I('get.selected',0);
$data = M('region')->where("parent_id=$parent_id")->select();
$html = '';
if($data){
foreach($data as $h){
if($h['id'] == $selected){
$html .= "<option value='{$h['id']}' selected>{$h['name']}</option>";
}
$html .= "<option value='{$h['id']}'>{$h['name']}</option>”;
}
}
echo $html;
}
通过`I('get.parent_id')` 处理输入,这是 thinkphp 的一个写法,通过 GET接收 parent_id 这个变量。I方法是ThinkPHP众多单字母函数中的新成员,其命名来自于英文Input(输入),主要用于更加方便和安全的获取系统输入变量。echo I('get.id'); // 相当于 $_GET['id']。
获取的 parent_id 之后直接带入了数据库查询。
$data = M('region')->where("parent_id=$parent_id")->select();
Python sqlmap.py -u "http://demo2.tp-shop.cn/index.php?m=Home&c=Api&a=get Region&parent_id=2" -p parent_id -v 3
3.iSiteCMS 几处注射漏洞
File:/isite/components/messages/messages.fe.php
if($form->status == TFORM_STATUS_GETED_VALID){
//这个是站内短信的写信息表单的处理
$arr = $form->getValues();//直接获取表单中信息
$tos = explode(',',trim($arr['to']));//只是分割, 不是过滤
$noExistsMenber = array();
$toMenbers = array();
foreach ($tos as $menber){
$i =$this->DBE->getOne("select `id` from #__user where `name`='$menber'");//直接拼接到sql查询语句中
if(is_null($i) or empty($i)){
$noExistsMenber[] = $menber;
//id 只要有返回就可以继续
}else{
$m['name'] = $menber;
$m['id'] = $i;
$toMenbers[] = $m;
}
}
if(!empty($noExistsMenber)){
addGlobalNotice("以下用户不存在:".implode(',',$noExistsMenber));
}else{
$msg['tos'] = $arr['to'];
$msg['subject'] = $arr['subject'];
$msg['content'] = $arr['content'];
$mMessage->sendMessage($toMenbers,$msg);
$this->flash('成功','发送成功',bu(1,'messages','inbox'));
}
4.laravel SQL注入
先放5.6.5版本中Validation/Rules/unique.php中ignore方法定义
通过阅读代码可知,用户可控制的id字符串,未经过滤赋给了$this->ignore,接着在下面代码中找到引用:
5.8.4版本:
public function __toString() {
……
$this->ignore ? '"'.$this->ignore.'"' : 'NULL',
……
}
测试代码:
则可进行注入。
如果 sql 语句中有出现+ append、 $() # 等字眼,如果没有配置 SQL 过滤文件,则判断存在 SQL 注入漏洞。
类似于PHP代码审计,不断跟入。
int intval(mixed var, int base),var 是要转换成整形的变量,base,可选,是基础数,默认是 10。
运用 floatval 或 doubleval 函数分别转换单精度和双精度浮点型参数
int floatval(mixed var),var 是要转换的变量
int doubleval(mixed var),var 是要转换的变量
运用 addslashes 函数来将单引号“’”转换成“’”,双引号“"”转换成“"”,反斜杠“”转换成“\”,NULL字符加上反斜杠“”
string addslashes (string str),str 是要检查的字符串
如果是字符型,先判断 magic_quotes_gpc 能无法 为 On,当不为 On 的时候运用 addslashes 转义特殊字符
if(get_magic_quotes_gpc()){
$var = $_GET["var"];
}else{
$var = addslashes($_GET["var"]);
}
本文来源于:代码审计--SQL注入-变化吧
特别声明:以上文章内容仅代表作者本人观点,不代表变化吧观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。
- 赞助本站
- 微信扫一扫
-
- 加入Q群
- QQ扫一扫
-
评论