@yzddmr6
自己在L3HCTF中出了一道java上传绕过题目bypass。其中题目中的一些trick不仅仅是用于CTF出题,对于实战渗透也是有一定的帮助。今天跟大家分享一下出题时的一些思考跟解题细节。
题目有三道过滤
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。常规操作
2. 绕过可见字符检测
第二阶段题目中直接用getString获取FileItem的内容,然后传入了checkValidChars函数检测。checkValidChars函数主要功能是检测content中是否存在连着两个以上的字母数字,如果匹配成功则提示上传失败。
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();
}
这里其实是模拟了一个WAF的场景,因为很多WAF对于文件上传都会有很粗暴的拦截,碰到jsp标签就给干死。
乍一看似乎并不可能被绕过,因为只要连着两个字母数字就会被检测到,让人不由得想起了CTF经典题目《php无字母数字webshell》。但是java不像php一样支持变量函数,需要从其他地方下手。
这里就用到了一个trick:FileItem.getString()对于编码的解析跟Tomcat解析jsp是有差异的,默认为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这个类,其中有很多默认用ISO-8859-1无法直接解析的编码。
private EncodingDetector.BomResult parseBom(byte[] b4, int count) {
if (count < 2) {
return new EncodingDetector.BomResult("UTF