Hacking Veeam: Several CVEs and $30k Bounties

VoorivexVoorivex
8 min read

Hello, I’m a web guy. Usually, I’m not working on non-web applications since my mind doesn’t know binary and reverse engineering. About one year ago, I started giving myself a shot at working on some macOS applications, and I managed to uncover several RCEs, even without knowing much about applications. I just used my intuition and hacker mindset to penetrate the application. Maybe I will write another blog post about the RCEs in the future.

This post is about a program that I’ve been working on for a couple of months. I will go through the challenges I faced and the vulnerabilities I found, along with some bug bounty tips at the end. The list of vulnerabilities:

  • Authentication Bypass - $7500 (CVE-2024-29849)

  • Remote Code Execution - $7500 (CVE-2024-42024)

  • NTLM Relay to Account Takeover - $3000 (CVE-2024-29850)

  • Local Privilege Escalation - $3000 (CVE-2024-29853)

  • Broken Access Control & IDORs (CVE-2024-29852, etc)

Before starting, I should mention that I asked one of my friends to help me with some reverse engineering tasks.

Challenges

Veeam products are Windows-based, with .exe files, dll files, etc. There are also multiple components working together, like agents and local web-based platforms. Although you can test the product using a black-box approach, auditing the source code helps to have a better understanding of the platform’s structure. Most of the bugs were found through analyzing the source code.

The first step is to reverse the source code. Thanks to ILSpy .NET decompiler, with the code below in PowerShell, you can iterate through all the dll files and decompile the source code:

# Get all DLL files in the current directory
$dllFiles = Get-ChildItem -Filter *.dll

foreach ($dllFile in $dllFiles) {
    # Create a directory with the name filename_src
    $outputDirectory = Join-Path (Get-Location) ($dllFile.BaseName + "_src")
    New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null

    # Build the ilspycmd command
    $ilspycmdCommand = "ilspycmd $($dllFile.FullName) -p -o $outputDirectory"

    # Execute the ilspycmd command
    Invoke-Expression $ilspycmdCommand
}

With the source code in hand, I could start searching for spots to initiate testing. One way would be to look for routes and roles, as most of my reported broken access control bugs were found using this methodology.

Before checking the code, I started using the platforms and reading Veeam’s help documents to understand the existing roles, authentication options, and what Veeam products like Enterprise Manager (VEM) really does.

If you aren’t familiar with Veeam products, they basically offer efficient and reliable backup and recovery for virtual, physical, NAS, and cloud-native environments.

One challenge I faced during the initial testing was the need to install VMware ESXI, which required a large infrastructure with plenty of RAM and storage. VEM uses a PostgreSQL database, and one of the hack-ish methods I came up with was inserting dummy data directly into the platform’s database.

This trick helped me move forward with the available features without the hassle of setup.

Bugs

CVE-2024-29849 - Zero Interaction Administrator Account Takeover

There's a REST API channel to communicate with when installing the Veeam Enterprise Manager, located at: https://<enterprise-manager>:9398

One of the endpoints responsible for logging in and setting an Auth cookie for the end user is POST https://<enterprise-manager>:9398/api/sessionMngr/?v=latest The endpoint accepts XML data, which looks like this:

<LoginSpec xmlns="http://www.veeam.com/ent/v1.0">
<VMwareSSOToken>
Base64EncodedData
</VMwareSSOToken>
</LoginSpec>

The Base64EncodedData looks like this:

<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
    <saml2:Issuer>https://127.0.0.1:8443/websso/SAML2/Metadata</saml2:Issuer>
<saml2:Subject>
        <saml2:NameID>Administrator@lab.local</saml2:NameID>
</saml2:Subject>
<saml2:AttributeStatement>
<saml2:Attribute FriendlyName="Group">
            <saml2:AttributeValue>Admin</saml2:AttributeValue>
            <saml2:AttributeValue>User</saml2:AttributeValue>
        </saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>

It contains a saml2:Issuer, which is the SAML listener, and a saml2:NameID, the account name with the domain you wish to sign in. Looking through the code, which I will show, I realized the IF condition fails and never validates the protection check. An attacker setting up a listener and sending a request to the vulnerable endpoint can retrieve the auth cookie associated with the victim's account, which could be Administrator, and log in to the platform successfully without any interactions needed.

Vulnerable Code

Calling the endpoint, the first function being triggered, is named LogInAfterAuthentication

There's a condition (loginSpec == null || loginSpec.VwareSSOToken == null). As explained earlier, by providing VMwareSSOToken, the condition is set to false, then it moves to the next section, which is:

else{
    crestSession = this.m_loginSessionsScope.LogInBySsoToken(loginSpec, versionNames);
}

The LogInBySsoToken code:

Calling AuthorizeByVMwareSsoToken goes to the next functions named FindValidsTSEndpointUrl and ValidateAuthToken.

Eventually, due to the failed condition check:

if (CAuthorizationManager VaidateStsUr](pluginInfo, out result))]
return result;

It goes to the end of code:

return new Uri(authServiceLocationStr.Contains("websso/SAML2/Metadata", StringComparison.OrdinalIgnoreCase) ? UriFormatter .FormatHttps (uri. Host, new int?(uri.Port),
"sts/STSService", null, true) : UriFormatter.Formathttps(uri.Host, new int?(uri.Port),
"ims /STSService", null,

And receive the set-cookie: X-RestSvcSessionId= linked to the account simply by providing username@domain

Please note whether the SSO is enabled or not the component is vulnerable.

CVE-2024-42024 - RCE on Veeam One Agent

There's a system called Veeam One Agent, which is installed for interaction and transmitting data between Veeam's product components, such as Veeam Backup & Replication sending data to the Veeam One server via the agent.

Furthermore, one of the functions responsible for handling data and specifically deserializing it is called CustomSerializationBinder:

The function has a protection based off a whitelist called SupportedAssemblies and if one of the following values is not given (malformed the request) it returns error:

I could bypass the protection with passing a valid assemblyName but different typeName:

This is the exploitation file. As you can see, a valid value like mscorlib is provided, but the other argument is controlled by the user and bypassed successfully:

Running Whoami command as PoC

CVE-2024-29850 - Account Takeover via NTLM Relay

The classic old NTLM relay, thanks to this blog post, inspired me to look for such a bug in Veeam products. There’s an endpoint in the VEM product that allows the clients use Windows AD users for authentication. Searching through files after the installation, I have noticed there's no extendedProtection enabled in webapp config file.

After setting up the configuration, an attacker can relay another user's NTLM session and capture the user's authenticated cookie. This allows the attacker impersonate the victim and access the Veeam Enterprise Manager platform as an Administrator. Here's an image to help illustrate what's happening behind the scenes:

In this example attack scenario, an attacker sets up a listener on the authentication endpoint, which is Security/Windows/Winlogin.aspx, and relays the machine that has an authentication cookie.

Thanks to impacket awesome tool, with the following command:


/usr/bin/impacket-ntlmrelayx -t http://VEEAMSRV1:9080/Security/Windows/Winlogin.aspx -smb2supportpython

It was possible to grab the victim’s machine authentication cookie via Wireshark tool.

CVE-2024-29853 - Local Privilege Escalation via Veeam One Agent

Installing Veeam One, a couple of services like Veeam One server, client, and agent for communication between components are installed. Reviewing Veeam One source code, there's a section of code regarding the Veeam One agent component that is vulnerable to LPE due to an argument that is controllable by the user. By injecting the malicious DLL, I bypassed the protection with path traversal and escalated the user's role to the local system (administrator).

The TryResolveAssemblyFromFile function takes 3 args to format the dll filename. string text string.Format{"{0}{1}{2}.d11", arg, Path.DirectorySeparatorChar, arg2);

Broken Access Controls and IDORs

Besides the previously reported bugs, I found multiple access control issues along with IDORs by auditing the source code as mentioned earlier. I will provide one example in this post to show how an attacker could approach finding this bug on Veeam One product using both black-box and white-box methods.

Black-box approach

First, I needed to understand the different roles and groups to better grasp what each user does in the Veeam One system. After reading the docs and realized there are three types of group each having a set of permissions:

I created two accounts with different permissions (read-only and power user) to test features and their related API endpoints.

There was a specific section that allowed users to create backup repository locations.

Then, I added a custom location using the power user account and checked with the read-only user to see if I could delete the related record by providing the ID.

Here is the request for removing the related data.

DELETE /api/v2.2/geolocations/cities/{VALUE} HTTP/2
Host: 192.168.1.101:1239
Cookie: {VALUE}
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:125.0) Gecko/20100101 Firefox/125.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Veeam-Connection-Id: {VALUE}
Authorization: Bearer {VALUE}
Content-Type: application/json;charset=utf-8
Content-Length: 2
Origin: https://192.168.1.101:1239
Referer: https://192.168.1.101:1239/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
X-Pwnfox-Color: yellow
Te: trailers

[]

Using another numerical value like 1, 2, etc., resulted in the data added by the power user being removed by the read-only user, which was contrary to what the documents described.

White-box approach

I used two methods to find these types of bugs. The first method was identifying the API endpoints and their related functions.

For example, I could find most of the Veeam One APIs by searching for [route(“ in the source code and reading each one to see which user groups have the right to call the respective endpoint.

The second method involved finding the bug using a black-box approach and then searching for similar vulnerable endpoints in the source code.

Here is the actual vulnerable code (delete custom locations):

It takes an argument int CityId and passes it to the GetCity function directly without checking which user created the value, leading to the IDOR vulnerability.

Bug Bounty Tips

  • Don't be afraid of programs with .exe files. Not all of them are binaries; some come with web-based platform installations

  • When you find the first bug, such as broken access control, search through the source code to see if the developer made similar mistakes elsewhere

  • Search for old CVEs, it can give you handy information about the platform

Conclusion

It took me two weeks to find the first bug. Sometimes it takes weeks to fully understand the platforms, and the key is not giving up. I hope you all enjoyed my blog post.

See you in the next one!

45
Subscribe to my newsletter

Read articles from Voorivex directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Voorivex
Voorivex

I work as an instructor and hunter, leading a small team to grow, learn, and innovate.