【漏洞练习-Day1】Piwigo SQL注入

开始练习【红日团队】的PHP-Audit-Labs 代码审计 Day1
链接:https://github.com/hongriSec/PHP-Audit-Labs
感兴趣的同学可以去练习练习
预备知识:
内容题目均来自 PHP SECURITY CALENDAR 2017
Day 1 - Wish List 代码如下:

class Challenge {
  const UPLOAD_DIRECTORY = './solutions/';
  private $file;
  private $whitelist;
  public function __construct($file) {
    $this->file = $file;
    $this->whitelist = range(1, 24);
  }
  public function __destruct() {
    if (in_array($this->file['name'], $this->whitelist)) {
      move_uploaded_file(
        $this->file['tmp_name'],
        self::UPLOAD_DIRECTORY . $this->file['name']
      );
    }
  }
}
$challenge = new Challenge($_FILES['solution']);

漏洞解析 :
这一关卡考察的是一个任意文件上传漏洞,而导致这一漏洞的发生则是不安全的使用 in_array() 函数来检测上传的文件名,即

    if (in_array($this->file['name'], $this->whitelist)) {

由于该函数并未将第三个参数设置为 true ,这导致攻击者可以通过构造的文件名来绕过服务端的检测,例如文件名为 7shell.php 。因为PHP在使用 in_array() 函数判断时,会将 7shell.php 强制转换成数字7,而数字7range(1,24) 数组中,最终绕过 in_array() 函数判断,导致任意文件上传漏洞。(ps:这里之所以会发生强制类型转换,是因为目标数组中的元素为数字类型

in_array() 函数的定义:

(PHP 4, PHP 5, PHP 7)

功能:in_array — 检查数组中是否存在某个值
说明:

in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) : bool

大海捞针,在大海(haystack)中搜索针( needle),如果没有设置 strict 则使用宽松的比较。
另一种解释:
在 $haystack 中搜索 $needle ,如果第三个参数 $strict 的值为 TRUE ,则 in_array() 函数会进行强检查,检查 $needle 的类型是否和 $haystack 中的相同。如果找到 $haystack ,则返回 TRUE,否则返回 FALSE。

参数 描述
needle 必需。规定要在数组搜索的值。
haystack 必需。规定要搜索的数组。
strict 可选。如果该参数设置为 TRUE,则 in_array() 函数检查搜索的数据与数组的值的类型是否相同。
技术细节:
返回值 如果在数组中找到值则返回 TRUE,否则返回 FALSE。
PHP 版本 4+
更新日志 自 PHP 4.2 起,search 参数可以是一个数组。

实例分析:

本次实例分析,我们选取的是 piwigo2.7.1 版本。该版本由于SQL语句直接拼接 $rate 变量,而 $rate 变量也仅是用
in_array() 函数简单处理,并未使用第三个参数进行严格匹配,最终导致sql注入漏洞发生。

漏洞详情:

Piwigo是世界上最著名的免费开源相册系统之一,由PHP+MySQL架构。由于该框架搭建方便,受到国内外的开发者青睐,近日,Piwigo
<= v2.6.0爆出重要0day漏洞。

漏洞成因,Piwigo相册系统的/piwigo/picture.php页面,没有完整的验证jQuery参数。攻击者成功利用该漏洞,可以获取数据库的全部信息,漏洞及其简单暴力。

漏洞影响范围,Piwigo <= v2.6.0。

漏洞POC 本站提供安全工具、程序(方法)可能带有攻击性,仅供安全研究与教学之用,风险自负!

漏洞分析:

漏洞的分析看起来比较简单,是由于functions_rate.inc.php文件中的rate_picture函数没有对传入的$rate变量进行过滤,直接拼接到SQL中执行:
代码如下:

  pwg_query($query);
  $query = '
INSERT
  INTO '.RATE_TABLE.'
  (user_id,anonymous_id,element_id,rate,date)
  VALUES
  ('
    .$user['id'].','
    .'\''.$anonymous_id.'\','
    .$image_id.','
    .$rate
    .',NOW())
;';
  pwg_query($query);

关键在与rate_picture函数开头其实是有个对$rate变量的过滤的。如下

function rate_picture($image_id, $rate)
{
  global $conf, $user;

  if (!isset($rate) or !$conf['rate'] or !in_array($rate, $conf['rate_items']))
  {
    return false;
  }

判断$rate是否是$conf['rate_items']的项。而后面这个数组的值是配置文件里写死的。
$conf['rate_items'] 的内容可以在 include\config_default.inc.php 中找到,为

 $conf['rate_items'] = array(0,1,2,3,4,5);

看起来这句的功能是设置了一个rate变量的白名单。只能是0,1,2,3,4,5其中之一。
这样子应该很安全才对。当然事实证明这样子写是不安全的。当$rate = "5'aaaaaaaaaaaaaaaaa "时,
in_array($rate, $conf['rate_items']) 这个判断是返回True的。这是php里不同类型变量比较时候的一个特性。
所以,利用这个特性,相当于完全的bypass in_array的过滤。可以输入任意的数据拼接到SQL语句中,
只要以数组中的数字开头就可以了。此外php中的switch也存在类似的特性。
由于 if (!isset($rate) or !$conf['rate'] or !in_array($rate, $conf['rate_items']))并没有将 in_array() 函数的第三个参数设置为 true
所以会进行弱比较,可以绕过。比如我们将 $rate 的值设置成
1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));# 那么SQL语句就变成:

INSERT INTO piwigo_rate (user_id,anonymous_id,element_id,rate,date) VALUES (2,'192.168.2',1,1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));#,NOW()) ;

这样就可以进行盲注了,如果上面的代码你看的比较乱的话,可以看下面简化后的代码:
在这里插入图片描述

漏洞利用:

接下来我们直接用sqlmap进行验证, payload 如下:

这里我用物理机的sqlmap一直扫不动。。换了Kali的sqlmap就可以了
ps:这里需要自己上传一个图片

sqlmap -u "http://192.168.1.139/PHPcode/piwigo/picture.php?/1/category/1&action=rate" --data "rate=1" --dbs --batch

在这里插入图片描述

修复建议:

可以看到这个漏洞的原因是弱类型比较问题,那么我们就可以使用强匹配进行修复。例如将 in_array() 函数的第三个参数设置为 true ,或者使用 intval() 函数将变量强转成数字,又或者使用正则匹配来处理变量。这里我将 in_array() 函数的第三个参数设置为 true ,代码及防护效果如下:
在这里插入图片描述
在这里插入图片描述

结语

看完了上述分析,不知道大家是否对 in_array() 函数有了更加深入的理解
这里感谢一下【红日团队】

发布了35 篇原创文章 · 获赞 19 · 访问量 5208

猜你喜欢

转载自blog.csdn.net/zhangpen130/article/details/103864854
今日推荐