透过一道CTF题目学到的小知识点

  • A+
所属分类:逆向工程

透过一道CTF题目学到的小知识点

0x00 正文


先放出这道题的源码

<?php
class trick{
public $trick1;
public $trick2;
public function __destruct(){
$this->trick1 = (string)$this->trick1;
if(strlen($this->trick1) > 5 || strlen($this->trick2) > 5){
die("你太长了");
}
if($this->trick1 !== $this->trick2 && md5($this->trick1) === md5($this->trick2) && $this->trick1 != $this->trick2){
echo file_get_contents("/flag");
}
}
}
highlight_file(__FILE__);
unserialize(_GET[trick]);

审计源码可以看到定义了一个trick类,trick1和trick2两个变量,当程序结束,__destruct()方法被调用

trick1被强转为字符串,接下来判断两个变量的长度,当两个变量的长度不大于5时进入下面的逻辑

接着往下,当trick1和trick2不相等并且md5值相等时才能输出flag

本质上是一道简单的题,但是关键点就在于如何绕过,让strick1被强转成字符串后的md5值还和strick2相等

那么重点来了

我们构造序列化的时候需要传的值为

trick1=0.01

trick2=0.1*0.1

为什么要传这两个值呢,别急,我们一点一点往下看

先看我构造序列化的代码

<?php
class trick{
public $trick1;
public $trick2;
public function __destruct(){
$this->trick1 = (string)$this->trick1;
if(strlen($this->trick1) > 5 || strlen($this->trick2) > 5){
die("你太长了");
}
if($this->trick1 !== $this->trick2 && md5($this->trick1) === md5($this->trick2) && $this->trick1 != $this->trick2){
echo file_get_contents("/flag");
}
}
}
$chr1sto = new trick();
$chr1sto->trick1=0.01;
$chr1sto->trick2=0.1*0.1;
$s = serialize($chr1sto);
print($s);

按照我们之前分析的逻辑,当我们赋值了这两个变量,程序结束,调用析构方法,先将trick1强转为字符串

然后判断两个变量的长度是否大于5

先看一下我们序列化出的结果


透过一道CTF题目学到的小知识点


O:5:"trick":2:{s:6:"trick1";d:0.01;s:6:"trick2";d:0.010000000000000002;}

有意思的事情出现了

我们赋值的trick2=0.1*0.1反序列化后的结果为0.010000000000000002

按照正常的理解不应该是0.01吗


透过一道CTF题目学到的小知识点


透过一道CTF题目学到的小知识点


那为什么会这样呢

这其实是一个浮点数精度的问题

官方文档里是这么说的:

浮点数的精度
浮点数的精度有限。尽管取决于系统,PHP 通常使用 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16。非基本数学运算可能会给出更大误差,并且要考虑到进行复合运算时的误差传递。

此外,以十进制能够精确表示的有理数如 0.1 或 0.7,无论有多少尾数都不能被内部所使用的二进制精确表示,因此不能在不丢失一点点精度的情况下转换为二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999999991118...。

所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数。

通俗来说,像0.1,0.7,0.58之类的浮点数十进制值转换为二进制是一个无限长的值,也就是说没办法用二进制精准表示,这个是所有语言的通病,因为计算机底层就是这么规定的,所以会出现精度丢失的问题

以下情况会出现精度丢失的问题

  • 十进制数的二进制表示形式可能不精确。

  • 使用的数字之间类型不匹配(例如,混合使用浮点型和双精度型)

同样的,这样的浮点数在参与基本运算的时候也会出现精度丢失的情况

经过多次尝试后我发现

显示输出时候会显示为


透过一道CTF题目学到的小知识点


透过一道CTF题目学到的小知识点


透过一道CTF题目学到的小知识点


这是因为php最大精度是14位

如果超出14位会衰减

看下面的例子


透过一道CTF题目学到的小知识点


透过一道CTF题目学到的小知识点


别看php现在把0.100000000000001当成了0.1

实际上数还是那个数

还要注意四舍五入的情况


透过一道CTF题目学到的小知识点


理解了这些之后让我们回到题目

传入这两个值之后会进行strlen()和md5()的判断

这两个函数也受精度缺陷的影响,所以可以绕过这两个函数

这道题就这么出来了


透过一道CTF题目学到的小知识点


另外,经过我多次尝试

php5到php7.0的php版本中

如0.1,0.7这样的浮点数单独或经过运算,序列化后打印出的字符串是0.100000000000001,0.69999999999999996这样精度丢失的样子


透过一道CTF题目学到的小知识点


特殊的是在php5.2.17中打印出来的序列化后的字符串有了更多的位数


透过一道CTF题目学到的小知识点


而在php7.1中就会序列化为0.1和0.7


透过一道CTF题目学到的小知识点


这到底是为什么我也没搞明白,有知道的师傅教教我Orz


0x01

文章的最后再加两个这道题的另外两种解法

第一种

两个变量的值都为INF

payload:

O:5:"trick":2:{s:6:"trick1";d:INF;s:6:"trick2";d:INF;}


透过一道CTF题目学到的小知识点


第二种

两个变量的值都为NAN

payload:

O:5:"trick":2:{s:6:"trick1";d:NAN;s:6:"trick2";d:NAN;}


透过一道CTF题目学到的小知识点


透过一道CTF题目学到的小知识点

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: