No Trespassing

Oh, the secrets you’ll know if to GitHub you go. The phrases committed by coders exhibited a mistaken sense of security.

A password ensures, while its secrecy endures, a measure of proven identity.

Share that short phrase for the public to gaze at repositories open and clear. Then don’t be surprised at the attacker disguised with the secrets you thought were unknown.

*sigh*

It’s no secret that I gave a BlackHat presentation a few weeks ago. It’s no secret that the CSRF countermeasure we proposed avoids nonces, random numbers, and secrets. It’s no secret that GitHub is a repository of secrets.

And that’s how I got side-tracked for two days hunting secrets on GitHub when I should have been working on slides.

Your Secret

Security that relies on secrets (like passwords) fundamentally relies on the preservation of that secret. There’s no hidden wisdom behind that truism, no subtle paradox to grant it the standing of a koan. It’s a simple statement too often ignored, bent, and otherwise abused.

It started with research on examples of CSRF token implementations. But the hunt soon diverged from queries for connect.sid to tokens like OAUTH_CONSUMER_SECRET, to ssh:// and mongodb:// schemes. Such beasts of the wild had been noticed – they tend to roam with little hindrance.

connect.sid extension:js

Sometimes these beasts leap from cover into the territory of plaintext. Sometimes they remain camouflaged behind hashes and ciphers. Crypto functions conceal the nature of a beast, but the patient hunter will be able to discover it given time.

The mechanisms used to protect secrets, such as encryption and hash functions, are intended to maximize an attacker’s effort at trying to reverse-engineer the secret. The choice of hash function has no appreciable effect on a dictionary-based brute force attack (at least not until your dictionary or a hybrid-based approach reaches the size of the target keyspace). In the long run of an exhaustive brute force search, a “bigger” hash like SHA-512 would take longer than SHA-256 or MD5. But that’s not the smart way to increase the attacker’s work factor.

Iterated hashing techniques are more effective at increasing the attacker’s work factor. Such techniques have a tunable property that may be adjusted with regard to the expected cracking speeds of an attacker. For example, in the PBKDF2 algorithm, both the HMAC algorithm and number of rounds can be changed, so an HMAC-SHA1 could be replaced by HMAC-SHA256 and 1,000 rounds could be increased to 10,000. (The changes would not be compatible with each other, so you would still need a migration plan when moving from one setting to another.)

Of course, the choice of work factor must be balanced with a value you’re willing to encumber the site with. The number of “nonce” events for something like CSRF is far more frequent than the number of “hash” events for authentication. For example, a user may authenticate once in a one-hour period, but visit dozens of pages during that same time.

Our Secret

But none of that matters if you’re relying on a secret that’s easy to guess, like default passwords. And it doesn’t matter if you’ve chosen a nice, long passphrase that doesn’t appear in any dictionary if you’ve checked that password into a public source code repository.

In honor of the password cracking chapter of the upcoming AHTK 4th Edition, we’ll briefly cover how to guess HMAC values.

We’ll use the Connect JavaScript library for Node.js as a target for this guesswork. It contains a CSRF countermeasure that relies on nonces generated via an HMAC. This doesn’t mean Connect.js implements the HMAC algorithm incorrectly or contains a design error. It just means that the security of an HMAC relies on the secrecy of its password.

Here’s a snippet of the Connect.js code in action. Note the default secret, keyboard cat.

... var app = connect()
      .use(connect.cookieParser())
      .use(connect.session({ secret: 'keyboard cat' }))
      .use(connect.bodyParser())
      .use(connect.csrf())
...

If you come across a web app that sets a connect.sess or connect.sid cookie, then it’s likely to have been created by this library. And it’s just as likely to be using a bad password for the HMAC. Let’s put that to the test with the following cookies.

Set-Cookie: connect.sess=s%3AGY4Xp1AWB5PVzYHCANaXHznO.
PUvao3Y6%2FXxLAG%2Bp4xQEBAcbqMCJPACQUvS2WCfsmKU; Path=/;
Expires=Fri, 28 Jun 2013 23:13:52 GMT; HttpOnly

Set-Cookie: connect.sid=s%3ATdF%2FriiKHfdilCTc4W5uAAhy.
qTtH9ZL5pxgClGbZ0I0E3efJTrdC0jia6YxFh3cWKrU; path=/;
expires=Fri, 28 Jun 2013 22:51:58 GMT; httpOnly

Set-Cookie: connect.sid=CJVZnS56R6NY8kenBhhIOq0h.
0opeJzAPZ3efz0dw5YJrGqVv4Fi%2BWVIThEsGHMRqDw0; Path=/; HttpOnly

Everyone’s Secret

John the Ripper is a venerable password guessing tool with ancient roots in the security community. Its rule-based guessing techniques and speed make it a powerful tool for cracking passwords. In this case, we’re just interested in its ability to target the HMAC-SHA256 algorithm.

First, we need to reformat the cookies into a string that John recognizes. For these cookies, resolve the percent-encoded characters, replace the dot (.) with a hash (#). Some of the cookies contained a JSON-encoded version of the session value, others contained only the session value.

GY4Xp1AWB5PVzYHCANaXHznO#3d4bdaa3763afd7c4b006fa9e3140404071ba8c0893c009052f4b65827ec98a5
TdF/riiKHfdilCTc4W5uAAhy#a93b47f592f9a718029466d9d08d04dde7c94eb742d2389ae98c458777162ab5
CJVZnS56R6NY8kenBhhIOq0h#d28a5e27300f67779fcf4770e5826b1aa56fe058be595213844b061cc46a0f0d

Next, we unleash John against it. The first step might use a dictionary, such as a words.txt file you might have laying around. (The book covers more techniques and clever use of rules to target password patterns. John’s own documentation can also get you started.)

$ ./john --format=hmac-sha256 --wordlist=words.txt sids.john

Review your successes with the --show option.

$ ./john --show sids.john

Hashcat is another password guessing tool. It takes advantage of GPU processors to emphasize rate of guesses. It requires a slightly different format for the HMAC-256 input file. The order of the password and salt is reversed from John, and it requires a colon separator.

3d4bdaa3763afd7c4b006fa9e3140404071ba8c0893c009052f4b65827ec98a5:GY4Xp1AWB5PVzYHCANaXHznO
a93b47f592f9a718029466d9d08d04dde7c94eb742d2389ae98c458777162ab5:TdF/riiKHfdilCTc4W5uAAhy
d28a5e27300f67779fcf4770e5826b1aa56fe058be595213844b061cc46a0f0d:CJVZnS56R6NY8kenBhhIOq0h

Hashcat uses numeric references to the algorithms it supports. The following command runs a dictionary attack against hash algorithm 1450, which is HMAC-SHA256.

$ ./hashcat-cli64.app -a 0 -m 1450 sids.hashcat words.txt

Review your successes with the --show option.

$ ./hashcat-cli64.app --show -a 0 -m 1450 sids.hashcat words.txt

All sorts of secrets lurk in GitHub. Of course, the fundamental problem is that they shouldn’t be there in the first place. There are many more types of secrets than hashed passphrases, too.