1.8k star java-memshell-generator简单分析

1.8k star java-memshell-generator简单分析

闲着也是闲着!来re一个工具吧!

re工具没什么技术,师傅们就当新闻资讯刷了

大部分内存马在pen4uin师傅的java-memshell-generator中已经集成了,强烈推荐!

https://github.com/pen4uin/java-memshell-generator

今天我们就来解析一下这个1.8k star工具的原理,看看一个好的工具究竟整体是怎么实现的。

首先需要下载jexpr-encoder-utils到本地maven:

1
2
3
4
5
1、下载 jexpr-encoder-utils-0.2.2.jar
https://github.com/woodpecker-appstore/jexpr-encoder-utils/releases
2、添加到本地 Maven 仓库
mvn install:install-file -Dfile=jexpr-encoder-utils-0.2.2.jar -DgroupId=me.gv7.woodpecker -DartifactId=jexpr-encoder-utils -Dversion=0.2.2 -Dpackaging=jar

尽管官方给的工具打包是mvn package assembly:single,但是主目录下的pom.xml已经指定了package参数,所以直接mvn package一样的

jmg-all模块下的pom.xml通过插件配置了入口的主类为jmg.all.jMGApp

jMGApp main函数根据传入的参数选择以gui or cli的主函数启动

所以可以利用jmg-all的jMGApp进行调试

后面我就不分析pom.xml了,都是通过配置插件指定的

从GUIApp可以看出是用的FlatLightLaf进行的可视化

其中GUIApp::createAndShowGUI 是 Java 中的方法引用语法,用于简化 Lambda 表达式的写法。等价于() -> GUIApp.createAndShowGUI(),表示无参数调用 createAndShowGUI 静态方法

OK,后面我就按照这个框架把我的my-yso改个可视化出来

跟进到jMG.start,一堆图形化的设置,省略一下。看到创建 jMGForm 实例并获取其内容面板

跟进到jMGForm构造函数,不过能调试就很EZ了

下面贴代码和对应的图形化,哥斯拉相对冰蝎会多一个SpringWebFlux中间件能用,组件类型也要多个WFHandlerMethod

创建默认下拉框的代码这里就跳过了

这里四个addActionListener添加事件监听器,第一个toolBox的监听器是,根据所选的工具类型(Godzilla、Behinder等),重新设置后面两个下拉框(中间件/框架、组件类型)。以此类推到后面两个Box

后面是监听文本框的输入的几个事件监听器

注意观察点击生成按钮后触发的事件:

初始化文本面板:调用 TextPaneUtil.initTextPane 方法设置文本面板的样式和字体。
输出启动信息:通过 TextPaneUtil.startPrintln 打印当前工具类型、服务器类型、Shell 类型和输出格式。
配置初始化:调用 initConfig 方法设置配置参数。
生成 Shell 和 Injector:分别调用 ShellGeneratorUtil.makeShell 和 InjectorGenerator.makeInjector 方法生成 Shell 和 Injector。
输出结果:调用 ResultUtil.resultOutput 方法处理并输出结果。
恢复滚动位置:调用 ComponentUtil.restoreScrollPosition 恢复滚动条位置。
配置重置:调用 resetConfig 方法重置配置参数。

AntSword MemShell

跟进到makeShell函数,根据不同的case实例化不同的Generator,然后调用其makeShell函数。这里就只看蚁剑的shell怎么制作的

makeShell先是获取shell名,shell类名参数,然后调用modifyShell制作字节码

利用javassist制作类,重点来了,因为我选的是AntSword的Listener型内存马,所以这里传入的参数是AntSwordListener。然后用javassist获取了该类

跟进到AntSwordListener,很明显按照Listener内存马的手法继承了ServletRequestListener接口,然后重写了requestInitialized方法。注意到这个代码里并没有去注册Listener,而且getResponseFromRequest也是空的

继续回到modifyShell,调用了ResponseUtil.getMethodBody方法

如果是Tomcat,则会调用到默认的getCommonMethodBody

观察到getCommonMethodBody其实是个从HttpServeltResponse中取出response的方法

getFV是个递归类及其父类去查找字段的方法

所以modifyShell内的if块是向ctClass中补全getResponseFromRequest的代码,以此从HttpServeltResponse中反射取出response。

我们可以整理一下该工具创建AntSword Tomcat Listener内存马的过程:

  1. 根据用户选择的AntSword、Listener创建了AntSwordListener,不过只有写response的代码,没有注册Listener和获取response的代码
  2. 根据用户选择的Tomcat 进行代码插桩,实现了Tomcat下从HttpServeltResponse获取response

那么注册的代码在哪?

上面跟进的代码是makeShell,继续跟进makeInjector

根据选中的选项取到TomcatListenerInjectorTpl,跟进到UtilPlus.generate

经典的javassist获取TomcatListenerInjectorTpl

跟进到TomcatListenerInjectorTpl,很明显该类下的getContext是Tomcat下获取StandardContext的办法。这里tomcat v9从Tomact WebappClassLoader的resources字段中获取是很常见的手法,不过上面的v5/v6/v7/v8为什么是从线程中的ContainerBackgroundProcessor中获取?其他文章似乎并没有提到这种获取StandardContext的办法

带着小小的疑惑我随便开了个Tomcat项目,发现可以通过如下变量图找到StandardHost,进而找到StandardContext

注意由于是通过getThreads获取了所有线程,与我们直接在一次请求中找到StanardContext不同,所有线程会包括所有主机的请求,所以StanardHost会存在很多条数据,虽然此处只有localhost,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败。所以需要遍历StandardHost

又学到辣

然后是添加Listener的部分,没什么好说的,直接addApplicationEventListener

总的连起来的代码在构造函数,先获取Context,然后getListener获取之前生成的恶意Listener,然后addListener添加

getListener是反射调用defineClass然后实例化

然后我来串一下整体逻辑:

  1. 在点击”生成”的事件监听器中,调用了makeShell生成了指定的Shell。其中makeShell会调用modifyShell去根据中间件修改获取response的方式。
  2. 依旧是在点击”生成”的事件监听器中,调用makeInjector,然后在makeInjector中调用UtilPlus.generate。在generate中生成了对应的注册器去注入内存马。

所以要修改内存马,可以直接修改jmg-core包下的jmg.core.template的注册器

但是这些都是根据中间件通用的,修改具体执行命令的Shell,需要根据Shell管理器的类型找到对应Shell。如冰蝎的在jmg-behinder模块下的jmg.behinder.memshell

工具还实现了JDK_AbstractTranslet打TemplatesImpl字节码,真是爽到发昏!

由于该工具太过全能,我就不再分析内存马了

上一篇:
HTB 第七期 Escapetwo
下一篇:
Java SpringMemShell(NO JSP)