It’s been an interesting past few days. As you can probably tell from the counters at http://shellshock.brandonpotter.com, the vulnerability test tool logs some statistical information.

When the tool sends a HTTP request, it contains a special URL that, if successful, lets us know that the bash command was executed on the remote system, and via which type of HTTP header embed was successful in executing the command.

At the time of this post, a little over 121,000 tests have been run, representing approximately 88,000 unique hosts. The vast majority of systems have not been affected in a way that this tool detects. Last week, it was around 8% of systems, and as of today that number has dropped closer to 6% and continues to drop. Here is the breakdown of vulnerabilities that the tool has found:

  • 35% – Cookie Attack – These sites were susceptible to a bash command embedded in a Cookie HTTP header.
  • 33% – Referer Attack – Bash command embedded in a Referer HTTP header.
  • 32% – User-Agent Attack – Bash command embedded in a User-Agent header.

So, it’s roughly evenly distributed across HTTP headers, but Cookie is the most vulnerable by a few points.

The test tool also uses a few different commands, since it depends on wget or curl and these may be located in different locations. /usr/bin/wget is by far the most successful, with 52% of the vulnerabilities identified through it. (Not that this matters, since most intentional attacks would likely focus on doing something a little more evil than wget’ing a URL, but it has been interesting to see which worked.)

Note: Keep in mind that this is a HTTP test tool only, and while HTTP is the easiest and most open attack point to your servers, it is not necessarily the only way this can be exploited if you didn’t patch. For the test tool to find a vulnerability, you must have a vulnerable Bash shell, in addition to a CGI script / environment running on your web server that calls Bash to do something.

On a different, more mildly interesting note of how folks are testing their junk…

There is a disclaimer that the test tool should only be used on your own sites. Of the crazy people, the misfits, the rebels, the troublemakers:

  • 695 people have tested google.com – fear not, they have things under control over there now.
  • 259 people have tested facebook.com – uncle Mark would be proud.
  • 155 people have tested shellshock.brandonpotter.com (y’all think you’re funny)
  • 75 people have tested 127.0.0.1 (again this is why we can’t have nice things)

A few of the popular sites have been blocked now, since it’s just a waste of resources at this point testing them over and over. I’m no fun.

Looking through the web logs, honorable mention goes to the good people testing these “sites”…

  • -1 or 1=1 and (select 1 and row(1,1)>(select count(*),concat(CONCAT(CHAR(95),CHAR(33),CHAR(64),CHAR(52),CHAR(100),CHAR(105),CHAR(108),CHAR(101),CHAR(109),CHAR(109),CHAR(97)),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
  • 1' || (select dbms_pipe.receive_message((chr(95)||chr(33)||chr(64)||chr(51)||chr(100)||chr(105)||chr(108)||chr(101)||chr(109)||chr(109)||chr(97)),25) from dual) || '
  • (select convert(int,CHAR(95)+CHAR(33)+CHAR(64)+CHAR(50)+CHAR(100)+CHAR(105)+CHAR(108)+CHAR(101)+CHAR(109)+CHAR(109)+CHAR(97)) FROM syscolumns)

72

Username/password combinations don’t cut it anymore, and Two-Factor authentication is a great way to help secure user accounts. If you have an account with a system that supports it, you should be using it. Likewise, if you develop systems that require users to log in with a username and password, you should be offering it. By using two-factor authentication, you dramatically reduce the attack footprint – no longer would a nefarious individual have to just guess your password, but they would have to guess your password AND a PIN number that changes every couple of minutes.

We are in the middle of adding two-factor auth to a few of our own systems. There are a few sites (like Authy) that will operate this as a cloud service for you at a monthly or usage cost, but there’s no need. Google offers a completely free solution via the Google Authenticator app for iOS and Android, with an equivalent app just called 'Authenticator' for Windows Phone. I was surprised there were no really good libraries to use this method of two-factor authentication in an implementation. So, I made one.

What is two-factor authentication?

Two-factor authentication, as the name implies, requires users to supply normal credentials (a username and password, for example), but adds a second, real-time token to the login to verify the user’s identity.

Old-School Tokens

image

You may have seen these RSA keys floating around in Enterprise IT departments. These have been around forever, but have normally been a pain to implement and support.

Text Tokens

Some forms of two-factor authentication will text you a one-time unique token when needed. And that is fairly easy to implement. However, text tokens have a few drawbacks:

  • Not everyone is within cell carrier service all the time
  • Text costs can still be prohibitive (when traveling internationally, for example)
  • Sending true text/SMS messages costs money, through services like Twilio

Two-Factor Apps

Fortunately, this problem is easily solved by apps, and Google Authenticator and its similar alternatives are my pick.

There are others, but that should cover most users pretty well.

The workflow on this is straightforward, and can be used offline – tokens are algorithm-generated, and do not require a live internet connection on the user’s device.

  1. Your system/web site/app generates a two-factor token for the user. Perhaps a GUID, or any unique identifier string specific to that user.
  2. You give the user a code to add to the Google Authenticator app, or show them a QR code to scan for the easy way.
  3. Google Authenticator then generates a 6-digit PIN code every 30 seconds. Prompt the user for this code during their login, and validate it!

It looks like this on iPhone:

GoogleAuthScreenshot

Try it out!

Go get the app and give it a try to see how it works – I set up a sample workflow here: http://GAuthTwoFactorSample.azurewebsites.net

Scan the QR code into Google Authenticator, and then try validating your PIN code.

Now, on to implementation…

This is supposed to be easy, so start by grabbing the NuGet package GoogleAuthenticator (here’s a link).

image

Present User Setup QR Code / Manual Entry Code with GenerateSetupCode

Users have two options when setting up a new Google Authenticator account. If using a mobile device, they can scan a QR code (easiest), or they can enter or copy/paste a manual code into the app.

Generating this information takes a couple lines of code:

TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();
var setupInfo = tfa.GenerateSetupCode("MyApp", "user@example.com", "SuperSecretKeyGoesHere", 300, 300);

string qrCodeImageUrl = setupInfo.QrCodeSetupImageUrl;
string manualEntrySetupCode = setupInfo.ManualEntryKey;

GenerateSetupCode requires a couple arguments:

  1. Issuer ID - this is the issuer ID that will appear on the user's Google Authenticator app, right above the code. It should be the name of your app/system so the user can easily identify it.
  2. Account Title – this will be displayed to the user in the Google Authenticator app. It cannot have any spaces (if it does, the library will filter them). The user’s e-mail address is appropriate to use here, if that works for your system.
  3. Account Secret Key – this is the unique user key that only your system knows about. A good length for this is 10-12 characters. Don’t show this to the user! Your users should never see it. I exposed it on the demo site just to show what’s going on.
  4. QR Code Width – width (in pixels) of generated QR code image
  5. QR Code Height – height (in pixels) of generated QR code image

It returns an object with a few notable properties:

  1. QrCodeSetupImageUrl – the URL to the QR code image that the user can scan (powered by Google Charts)
  2. ManualEntryKey – if the user can’t scan the QR code, this is the string they will need to enter into Google Authenticator in order to set up the two-factor account.

Validate a user’s PIN with ValidateTwoFactorPIN

Prompt the user for their current PIN displayed in Google Authenticator, and validate it:

TwoFactorAuthenticator tfa = new TwoFactorAuthenticator();
bool isCorrectPIN = tfa.ValidateTwoFactorPIN("SuperSecretKeyGoesHere", "123456");

That’s it!

About Clock Drift

Given that this two-factor authentication method is time-based, it is highly likely that there is some time difference between your servers and the user’s device. With these PIN codes changing every 30 seconds, you must decide what an acceptable ‘clock drift’ might be. Using the above code samples, the library will default to a clock drift tolerance of +/- 5 minutes from the current time. This means that if your user’s device is perfectly in sync with the server time, their PIN code will be ‘correct’ for a 10-minute window of time. However, if their device time is more than +/- 5 minutes off from your server’s time, the PIN code displayed on their device will never match up.

If you want to change this default clock drift tolerance, you can use the overloaded version of ValidateTwoFactorPIN, and provide a TimeSpan.

That’s all – hope this library is useful and makes two-factor authentication a no-brainer.