Firewall – Merge VPNs of two Watchguard firewalls into one firewall

firewallvpnwatchguard

I have two different Watchguard XTM 515 firewalls. Each has it's own set of VPNs created in them.

Now I need to use only one firewall to handle the VPNs of both. But the problem is that I don't know the PSK of VPNs (I inherited those firewall after joining my job). Asking the customers to change PSK is not an option for me.

Now I do know that when I export the configuration (XML file) of firewall, it includes the PSKs of all VPNs. That's the reason restoring that configuration to another firewall works. But I don't know how to get hold of those PSKs. I check the configuration XML files with plain text editor and seems like they are encrypted (no surprise here). But they must be encrypted using a static key since this configuration can be uploaded to any firewall. It's just that I don't know the decryption scheme and key.

Now my objective is certainly not to break the encryption of Watchguard XML configuration files. All I need to do is merge two firewalls into one. I thought about manually merging the sections of XML configuration files exported from both firewalls, but it seems like a daunting task.

Please can you help suggesting a method to merge VPNs of two different Watchguard firewalls into one?

Best Answer

You're right, they are encrypted with a static key, and the scheme is AES Key Wrap Algorithm (RFC 3394). And you can decrypt them.

I took this public domain C# library, stripped it right down to just the decrypt functions and ported it to PowerShell, so it would fit in a StackOverflow answer and not need compiling or binaries.

It's not pretty, has no error checking or anything, but it seems to work:

<#
.Synopsis
   Decrypts a Watchguard encrypted BOVPN pre-shared-key
.EXAMPLE
   Decrypt-WatchguardPsk -EncryptedPsk '0E611DC31F2AEBB4A6E69F2641E1E83D762F514F3636E1EFA86B9BDECFEFADFB'
#>
function Decrypt-WatchguardPsk
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param([Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]$EncryptedPsk)

    Process
    {
        function Group-ByCount ($ByteArray, $n) { #BigArray -> Arrays of n items
            $NumGroups=$ByteArray.Count/$n
            $Output= @()
            0..($NumGroups-1) | ForEach-Object { $Output += @(, [byte[]]$ByteArray[($_*$n)..(($_*$n)+$n-1)]) }
            $Output
        }

        $KeyEncryptionKey = [byte[]] @(29, 3, 245, 130, 135, 152, 43, 199, 1, 34, 115, 148, 228, 152, 222, 35)
        $EncryptedPsk = $EncryptedPsk -replace '\s|(</*psk>)|\+' # trim xml line. Here so you can do: sls '<psk>' *.xml | % Line | Decrypt-WatchguardPsk
        [byte[]] $Arrby = $EncryptedPsk -split "(?<=\G\w{2})(?=\w{2})" |% { [Convert]::ToByte($_, 16) } #HexTo[byte[]]
        $C = Group-ByCount $Arrby 8  #Byte array to groups of 8 bytes (AES blocks)

        # 1) AES Key Wrap - Initialize variables
        $A = $C[0]
        $R = @($C[1..($C.Count-1)])
        $Blockn = $R.Count

        # 2) Calculate intermediate values
        for ($j = 5; $j -ge 0; $j--) {
            for ($i = $Blockn - 1; $i -ge 0; $i--) {
                $t = $Blockn * $j + $i + 1  # add 1 because i is zero-based

                #64 bit XOR
                $A2 = $A.Clone()
                [Array]::Reverse($A2)
                $A2 = [BitConverter]::GetBytes([BitConverter]::ToInt64($A2, 0) -bxor $t)
                [Array]::Reverse($A2)
                $A = $A2

                # Decrypt block
                $Alg = New-Object -type System.Security.Cryptography.RijndaelManaged
                $Alg.Padding = [System.Security.Cryptography.PaddingMode]::None
                $Alg.Mode = [System.Security.Cryptography.CipherMode]::ECB
                $Alg.Key = $KeyEncryptionKey

                $ms = New-Object System.IO.MemoryStream
                $xf = $Alg.CreateDecryptor()
                $cs = New-Object System.Security.Cryptography.CryptoStream -ArgumentList @($ms, $xf, [System.Security.Cryptography.CryptoStreamMode]::Write)

                $AConcatRi = $A + $R[$i] + (New-Object 'byte[]' (16 - $A.Count - $R[$i].Count))

                $cs.Write($AConcatRi, 0, $Alg.BlockSize / 8)

                $B = Group-ByCount $ms.ToArray() 8
                $A         = $B[0] #MSB(B)
                $R[$i]     = $B[1] #LSB(B) 64 least significant bits of a 128
            }
        }

        -join ($R | % { [System.Text.Encoding]::ASCII.GetString($_) })
    }
}

e.g.

PS C:\> Decrypt-WatchguardPsk -EncryptedPsk '8B4B449A6D4253232C4CFC48E311B7B9DF360D5F4EAB310CAD9D7B92B4CD3CA6340841671FA9187E6AB5F4604D5E2B9319EC890A826B96EF47163B83F2294289109F8336441879416A230C26E0AEEBDC332798F54F482250'
Testing with dummy text lorem ipsum dolor sit amet, consectetur adipiscing elit

I don't think posting this is a security problem - any attacker who can get the firewall config file is already past the security, on the firewall or on a management workstation. The config contains no device management login credentials. And there isn't much else they could do to encrypt things in a config file without trading off a lot of other concerns instead. It's really a pragmatic layer to stop passwords showing up in plain text searches and indexes.

Related Topic