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

Supervisord是一款由Python语言开发,用于管理后台应用(服务)的工具,方便运维人员使用图形化界面进行管理。

近期,Supervisord曝出了一个需认证的远程命令执行漏洞(CVE-2017-11610),通过POST请求Supervisord管理界面恶意数据,可以获取服务器操作权限,存在严重的安全风险。


Supervisor介绍

Supervisor 是基于 Python 的进程管理工具,可以帮助我们更简单的启动、重启和停止服务器上的后台进程,是 Linux 服务器管理的效率工具。Supervisor有四个组件: 1. supervisord 运行Supervisor的后台服务,它用来启动和管理那些你需要Supervisor管理的子进程,响应客户端发来的请求,重启意外退出的子进程,将子进程的stdout和stderr写入日志,响应事件等。它是Supervisor最核心的部分。 2. supervisorctl 相当于supervisord的客户端,它是一个命令行工具,用户可以通过它向supervisord服务发指令,比如查看子进程状态,启动或关闭子进程。它可以连接不同的supervisord服务,包括远程机上的服务。 3. Web服务器 这是supervisord的Web客户端,用户可以在Web页面上完成类似于supervisorctl的功能。 4. XML-RPC接口 这是留给第三方集成的接口,你的服务可以在远程调用这些XML-RPC接口来控制supervisord管理的子进程。上面的Web服务器其实也是通过这个XML-RPC接口实现的。

漏洞简介

本次漏洞就出在XML-RPC接口对数据的处理上。 默认情况下Supervisor并不会开启这个接口,但这并不代表这个漏洞不重要,相反的是,在Supervisor的使用中,很多人喜欢利用web页面来管理,而不是使用上文中提到的supervisorctl命令行工具。使用web页面有一个方便之处,即通过简单配置,使用者可以在其他机器的浏览器上通过网址访问并控制Supervisor。省去非一定在本地配置的麻烦(例如在docker中使用Supervisor,就不用每次进入容器控制Supervisor)。 开启web访问的配置如下 Supervisord远程命令执行漏洞分析(CVE-2017-11610)    

漏洞分析

本次分析从Supervisor入口出发,根据漏洞的相关披露,层层进行解析。 先从入口看起:Supervisord启动文件Supervisord.py。 因为事先知道攻击数据是通过http方式传入到sever端的,所以重点关注下Supervisord启动方法(run)中的启动http服务方法,这里是self.options.openhttpservers() Supervisord远程命令执行漏洞分析(CVE-2017-11610)Supervisord远程命令执行漏洞分析(CVE-2017-11610) 跟入options.py文件中去: Supervisord远程命令执行漏洞分析(CVE-2017-11610)     在这里可以看到self.httpservers = self.make_http_servers(supervisord) 在这里调用了make_http_servers()方法 在Options类中找到make_http_servers()方法: Supervisord远程命令执行漏洞分析(CVE-2017-11610)       可见这个方法是从supervisor.http中导入的,我们继续跟进http.py文件查看make_http_servers()方法的实现   Supervisord远程命令执行漏洞分析(CVE-2017-11610)                           Supervisord远程命令执行漏洞分析(CVE-2017-11610)       Supervisord远程命令执行漏洞分析(CVE-2017-11610)                                         根据漏洞的披露,我们知道这个漏洞是在XML-RPC接口的调用上出现了问题,在上图代码的最后一行supervisor_xmlrpc_handler()方法,就是用来处理RPC调用的。 我们从supervisor的入口开始,一路顺藤摸瓜找到了罪魁祸首,接下来跟进supervisor_xmlrpc_handler()方法看一下出现漏洞的原因 supervisor_xmlrpc_handler方法在xmlrpc.py文件中实现, Supervisord远程命令执行漏洞分析(CVE-2017-11610)         找到漏洞纰漏的traverse方法,可以看到supervisor_xmlrpc_handler()方法中的call函数将解析出来的method以及params传入tracerse方法中。 我们先来看下call函数在哪里被调用,以及method,params到底是什么: 在supervisor_xmlrpc_handler这个类中,有一个continue_request()函数,如下所示 Supervisord远程命令执行漏洞分析(CVE-2017-11610)                   Supervisord远程命令执行漏洞分析(CVE-2017-11610)                   在params, method = self.loads(data)一行中,可以看到params和method的产生,并且在这个函数最下面一行,可见调用了call()将返回值给了value。我们先看下这个类中的loads函数,如下所示: Supervisord远程命令执行漏洞分析(CVE-2017-11610)                       可见params和method是由xml tag中的methodName和params中的值得来的。 上面说的有些抽象,为了方便下面漏洞的理解,下面举个例子:在这里利用python使用RPC协议给supervisord发一个请求,来看下RPC协议的结构和params、method分别是什么。 Supervisord远程命令执行漏洞分析(CVE-2017-11610)         抓取的流量如下图: Supervisord远程命令执行漏洞分析(CVE-2017-11610)                                 这里的supervisor.supervisord.options.warnings.linecache.os.system就是method参数值,而param的值就是touch /tmp/success1 正常的交互中,这两个值往往是method=supervisor.startProcess;param=要启动的应用名 现在call()函数已经明晰了,再看看call函数中的tracerse方法。下面继续跟入tracerse方法 Supervisord远程命令执行漏洞分析(CVE-2017-11610)                     Supervisord远程命令执行漏洞分析(CVE-2017-11610)       在tracerse方法中,将传入的method通过“.”来拆分,赋值给path 判断开头是否为“_”,如果是,则报错 然后看ob = getattr(ob, name, None)这行,getattr()是一个自省函数,下面举一个简单的例子说明下getattr() Supervisord远程命令执行漏洞分析(CVE-2017-11610)             我们仔细分析下如下代码: Supervisord远程命令执行漏洞分析(CVE-2017-11610)             在这里类似一个递归,将原本链状的method中的方法遍历出来,例如method原先的结构是a.b.c.d这里path列表就应该是[a,b,c,d],然后遍历name。通过ob = getattr(ob, name, None),首先将ob中的a方法赋给新的ob,再将新的ob(现在是a方法)中的b方法取出来赋给新的ob。以此类推,最终的ob会是链状结构最后一个方法(也就是d),然后传入params值,被执行 所以如果想攻击利用成功,必须找到一个调用链,例如如下调用链 Supervisord远程命令执行漏洞分析(CVE-2017-11610)     下图是调用关系: Supervisord远程命令执行漏洞分析(CVE-2017-11610)           Options中的warnings方法 Supervisord远程命令执行漏洞分析(CVE-2017-11610)                     Warnings中的linecache方法 Supervisord远程命令执行漏洞分析(CVE-2017-11610)                   Linecache中的os方法

漏洞利用

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

解决方案

升级supervisor到最新版本或在配置中修改[inet_http_server]配置

查看原文:http://blog.nsfocus.net/supervisord-cve-2017-11610/