【PHP代码审计】 那些年我们一起挖掘SQL注入 - 4.全局防护Bypass之二次注入

0x01 背景

现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号。二次注入也是一种比较常见的注入,它涉及到入库和出库。因为有全局转义所以入库的时候:

Insert into table (username) values (‘hack\’’);

这样入库后转义符就会消失变成了hack’,这样如果hack’出库被带入查询的话就会成功的引入了单引号导致注入。
漏洞来源于乌云:http://www.wooyun.org/bugs/wooyun-2014-068362

0x02 环境搭建

看背景我们使用了低版本的74cms程序,版本为3.4(20140310)
①源码网上可以搜到,我打包了一份:http://pan.baidu.com/s/1c1mLCru
②解压到www的74cms(20140310)目录下,浏览器访问http://localhost/74cms(20140310)),然后按照提示一步步安装即可,安装遇到问题请自行百度或谷歌,成功后访问如下图:

0x03 漏洞分析

Part1:源码结构

源码的结构比较清晰,应该是审计过最清晰的结构了,主要有下面三块内容:

index.php引入了common.inc.php文件,我们跟进common.inc.php,发现了处理gpc的函数:

<?phpif (!empty($_GET)){    $_GET  = addslashes_deep($_GET);}if (!empty($_POST)){    $_POST = addslashes_deep($_POST);}$_COOKIE   = addslashes_deep($_COOKIE);$_REQUEST  = addslashes_deep($_REQUEST);

可以看到,服务端处理GET和POST请求的变量时都会做addslashes处理。

Part2:审计过程

1.首先在个人发布简历处:

elseif ($act == ‘make4_save‘) {    $resume_education = get_resume_education($_SESSION[‘uid‘], $_REQUEST[‘pid‘]);    if (count($resume_education) >= 6) showmsg(‘教育经历不能超过6条!‘, 1, $link);    $setsqlarr[‘uid‘] = intval($_SESSION[‘uid‘]);    $setsqlarr[‘pid‘] = intval($_REQUEST[‘pid‘]);    if ($setsqlarr[‘uid‘] == 0 || $setsqlarr[‘pid‘] == 0) showmsg(‘参数错误!‘, 1);    $setsqlarr[‘start‘] = trim($_POST[‘start‘]) ? $_POST[‘start‘] : showmsg(‘请填写开始时间!‘, 1, $link);    $setsqlarr[‘endtime‘] = trim($_POST[‘endtime‘]) ? $_POST[‘endtime‘] : showmsg(‘请填写结束时间!‘, 1, $link);    $setsqlarr[‘school‘] = trim($_POST[‘school‘]) ? $_POST[‘school‘] : showmsg(‘请填写学校名称!‘, 1, $link);    $setsqlarr[‘speciality‘] = trim($_POST[‘speciality‘]) ? $_POST[‘speciality‘] : showmsg(‘请填写专业名称!‘, 1, $link);    $setsqlarr[‘education‘] = trim($_POST[‘education‘]) ? $_POST[‘education‘] : showmsg(‘请选择获得学历!‘, 1, $link);    $setsqlarr[‘education_cn‘] = trim($_POST[‘education_cn‘]) ? $_POST[‘education_cn‘] : showmsg(‘请选择获得学历!‘, 1, $link);    //看到这里有个插入表“qs_resume_education”的操作,将教育背景相关的字段入库    if (inserttable(table(‘resume_education‘), $setsqlarr)) {        check_resume($_SESSION[‘uid‘], intval($_REQUEST[‘pid‘]));

2.这里看到insert入库了,可以尝试加个单引号,入库后就会消除转义字符。我们先继续跟进inserttables后的check_resume函数

//检查简历的完成程度function check_resume($uid, $pid){    global $db, $timestamp, $_CFG;    $uid = intval($uid);    $pid = intval($pid);    $percent = 0;    $resume_basic = get_resume_basic($uid, $pid);    $resume_intention = $resume_basic[‘intention_jobs‘];    $resume_specialty = $resume_basic[‘specialty‘];    //获取教育经历,出数据库了    $resume_education = get_resume_education($uid, $pid);    if (!empty($resume_basic)) $percent = $percent + 15;    if (!empty($resume_intention)) $percent = $percent + 15;    if (!empty($resume_specialty)) $percent = $percent + 15;    if (!empty($resume_education)) $percent = $percent + 15;    if ($resume_basic[‘photo_img‘] && $resume_basic[‘photo_audit‘] == "1" && $resume_basic[‘photo_display‘] == "1") {        $setsqlarr[‘photo‘] = 1;    } else {        $setsqlarr[‘photo‘] = 0;    }    if ($percent < 60) {        $setsqlarr[‘complete_percent‘] = $percent;        $setsqlarr[‘complete‘] = 2;    } else {        $resume_work = get_resume_work($uid, $pid);        $resume_training = get_resume_training($uid, $pid);        $resume_photo = $resume_basic[‘photo_img‘];        if (!empty($resume_work)) $percent = $percent + 13;        if (!empty($resume_training)) $percent = $percent + 13;        if (!empty($resume_photo)) $percent = $percent + 14;        $setsqlarr[‘complete‘] = 1;        $setsqlarr[‘complete_percent‘] = $percent;        require_once(QISHI_ROOT_PATH . ‘include/splitword.class.php‘);        $sp = new SPWord();        $setsqlarr[‘key‘] = $resume_basic[‘intention_jobs‘] . $resume_basic[‘recentjobs‘] . $resume_basic[‘specialty‘];        $setsqlarr[‘key‘] = "{$resume_basic[‘fullname‘]} " . $sp->extracttag($setsqlarr[‘key‘]);        $setsqlarr[‘key‘] = str_replace(",", " ", $resume_basic[‘intention_jobs‘]) . " {$setsqlarr[‘key‘]} {$resume_basic[‘education_cn‘]}";        $setsqlarr[‘key‘] = $sp->pad($setsqlarr[‘key‘]);        if (!empty($resume_education)) {            //遍历教育经历所有字段,加入到数组里            foreach ($resume_education as $li) {                $setsqlarr[‘key‘] = "{$li[‘school‘]} {$setsqlarr[‘key‘]} {$li[‘speciality‘]}";            }        }        $setsqlarr[‘refreshtime‘] = $timestamp;    }    //这里对教育经历做了次更新操作,二次注入由此产生!    updatetable(table(‘resume‘), $setsqlarr, "uid=‘{$uid}‘ AND id=‘{$pid}‘");    updatetable(table(‘resume_tmp‘), $setsqlarr, "uid=‘{$uid}‘ AND id=‘{$pid}‘");

3.我们填写一份简历简单试验下,在教育经历处学校名称字段填写aa’

保存后发现报错语句:

0x04 漏洞证明

构造获取数据库用户相关信息的POC:

查看简历发现简历姓名变成了[email protected]:

查看sql语句发现更新语句是成功执行的:

最后,有兴趣的同学可以继续获取其它的管理员账户等相关字段的信息。

原文链接:http://www.cnbraid.com/2016/02/19/sql3/,如需转载请联系作者。

时间: 2024-10-26 01:23:08

【PHP代码审计】 那些年我们一起挖掘SQL注入 - 4.全局防护Bypass之二次注入的相关文章

【PHP代码审计】 那些年我们一起挖掘SQL注入 - 5.全局防护Bypass之宽字节注入

0x01 背景 首先我们了解下宽字节注入,宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而导致的注入漏洞.具体原理如下:1.正常情况下当GPC开启或使用addslashes函数过滤GET或POST提交的参数时,黑客使用的单引号 ‘ 就会被转义为: \’:2.但如果存在宽字节注入,我们输入%df%27时首先经过上面提到的单引号转义变成了%df%5c%27(%5c是反斜杠\),之后在数据库查询前由于使用了GBK多

那些年我们一起挖掘SQL注入 - 5.全局防护Bypass之宽字节注入

0x01 背景 首先我们了解下宽字节注入,宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而导致的注入漏洞.具体原理如下:1.正常情况下当GPC开启或使用addslashes函数过滤GET或POST提交的参数时,黑客使用的单引号 ' 就会被转义为: \':2.但如果存在宽字节注入,我们输入%df%27时首先经过上面提到的单引号转义变成了%df%5c%27(%5c是反斜杠\),之后在数据库查询前由于使用了GBK多

【PHP代码审计】 那些年我们一起挖掘SQL注入 - 3.全局防护Bypass之Base64Decode

0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.同上一篇,我们需要找一些编码解码的函数来绕过全局防护,本篇介绍base64decode()的情况.漏洞来源于乌云:http://www.wooyun.org/bugs/wooyun-2014-050338 0x02 环境搭建 看背景我们使用了低版本的easytalk程序,版本为X2.4①源码我打包了一份:htt

【PHP代码审计】 那些年我们一起挖掘SQL注入 - 2.全局防护Bypass之UrlDecode

0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.遇到这种情况我们就需要找一些编码解码的函数来绕过全局防护,这篇文章讲urldecode()的情况,同样大牛请自觉绕道~漏洞来源于乌云:http://www.wooyun.org/bugs/wooyun-2014-050338 0x02 环境搭建 看背景我们使用了低版本的easytalk程序,版本为X2.4①源码

那些年我们一起挖掘SQL注入 - 2.全局防护Bypass之UrlDecode

0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.遇到这种情况我们就需要找一些编码解码的函数来绕过全局防护,这篇文章讲urldecode()的情况,同样大牛请自觉绕道~漏洞来源于乌云:http://www.wooyun.org/bugs/wooyun-2014-050338 0x02 环境搭建 看背景我们使用了低版本的easytalk程序,版本为X2.4①源码

【PHP代码审计】 那些年我们一起挖掘SQL注入 - 6.全局防护Bypass之一些函数的错误使用

0x01 背景 PHP程序员在开发过程中难免会使用一些字符替换函数(str_replace).反转义函数(stripslashes),但这些函数使用位置不当就会绕过全局的防护造成SQL注入漏洞. 0x03 漏洞分析 str_replace函数的错误使用 第一种情况是写程序时会使用str_replace函数将参数中的单引号.括号等字符替换为空,这样在一些双条件查询的情况就会引发注入问题.缺陷代码如下: <?phprequire_once('common.php');$conn = mysql_co

那些年我们一起挖掘SQL注入 - 6.全局防护Bypass之一些函数的错误使用

0x01 背景 PHP程序员在开发过程中难免会使用一些字符替换函数(str_replace).反转义函数(stripslashes),但这些函数使用位置不当就会绕过全局的防护造成SQL注入漏洞. 0x02 漏洞分析 str_replace函数的错误使用 第一种情况是写程序时会使用str_replace函数将参数中的单引号.括号等字符替换为空,这样在一些双条件查询的情况就会引发注入问题.缺陷代码如下: <?phprequire_once('common.php');$conn = mysql_co

【PHP代码审计】 那些年我们一起挖掘SQL注入 - 8.全局防护盲点的总结下篇

0x01 背景 现在的WEB应用对SQL注入的防护基本都是判断GPC是否开启,然后使用addlashes函数对单引号等特殊字符进行转义.但仅仅使用这样的防护是存在很多盲点的,接上篇http://www.cnbraid.com/2016/05/10/sql6/,这里介绍另外两种情况.盲点如下:①FILES注入,全局只转义掉GET.POST等传来的参数,遗漏了FILES:②变量覆盖,危险函数:extract().parse_str().$$. 0x02 漏洞分析 FILES注入 FILES注入一般情

【PHP代码审计】 那些年我们一起挖掘SQL注入 - 7.全局防护盲点的总结上篇

0x01 背景 现在的WEB应用对SQL注入的防护基本都是判断GPC是否开启,然后使用addlashes函数对单引号等特殊字符进行转义.但仅仅使用这样的防护是存在很多盲点的,比如最经典的整型参数传递,也即被带入数据库查询的参数是整型.数组中的key没过滤被带入了查询以及全局过滤了GET.POST但没过滤SERVER或COOKIE引发的注入.所以看似有全局防护,实则隐藏了很多“后门”-盲点如下:①注入点类似id=1这种整型的参数就会完全无视GPC的过滤:②注入点包含键值对的,那么这里只检测了val