Why is randomness important, especially in the world of cryptocurrencies? (Part 1)
By: Damian Rusinek
Tags: Blockchain Startup, Randomness, Cryptocurrency, Blockchain Development

Authentication

The authentication process is simple. After providing valid e-mail address and password, the application returns user details together with his session token. The token is used to authorize further requests.

Here is the login response, which includes the session token:

When you look at the userId and token parameters in the above JSON server response you will probably notice the problem. They look very similar.

The randomness

The random (or pseudo-random to be exact) sequences typically exhibit statistical randomness while being generated by a deterministic algorithm. It plays crucial role in applications authentication and you can confirm it in most of security cheat sheets, especially for session management. These checklist emphasize that session identifier must be unpredictable and random enough to prevent guessing where an attacker is able to obtain the identifier of a valid session.

OWASP Session Cheat Cheet specifies the following:

The session ID value must provide at least 64 bits of entropy (if a good PRNG is used, this value is estimated to be half the length of the session ID).

It is not a good idea to implement custom PRNG as there are available verified implementations. However there are two types of them: statistical and cryptographic. Statistical PRNGs has useful statistical attributes, but the output easy to guess and is unsuitable for secure parameters such as session identifier. Here is an example of PRNG in Java that returns random int, but it is easy to reproduce as the seed is taken from UNIX timestamp.

Statistical PRNG

The other group, cryptographic PRNGs, return values which are more difficult to predict. For a value to be cryptographically secure, the attacker must not distinguish it from the truly random number. Java provides the SecureRandom class as an example of cryptographic PRNG.

Cryptographic PRNG

In general, if a PRNG algorithm is not advertised as being cryptographically secure, then it is probably a statistical PRNG and should not be used in security-sensitive contexts.

Discovery

At first, I wanted to find out what is the similarity between the token values. I used Burp Suite Sequencer module to generate 100 consecutive tokens. Here is the list:

...

5a8b3cae2283a17e7619fea3

5a8b3cae2283a17e7619fea4

5a8b3cae2283a17e7619fea5

5a8b3cae2283a17e7619fea6

5a8b3caf2283a17e7619fea7

5a8b3caf2283a17e7619fea8

5a8b3caf2283a17e7619fea9

5a8b3caf2283a17e7619feaa

5a8b3caf2283a17e7619feab

5a8b3cb02283a17e7619feac

5a8b3cb02283a17e7619fead

...

The format of the token value is easily noticeable — it is a sequence of 12 bytes encoded in hex. There are only two changes: in the fourth byte and in the last byte. The last byte increases by one on each call while the second byte increases by one a little less often.

After a few tests I have found out that the fourth byte is increased every second. It means that the first fourth bytes are the time! Indeed, when I decoded 0x5a8b3caf to decimal, which is 1519074479, it turned out to be the date in UNIX timestamp — 02/19/2018 9:07PM (UTC).

Token format explained

To sum up, the token format is the following:

Encode current UNIX timestamp in hex. Increase by one the nonce value. Encode the nonce in hex (0x2283a17e7619fea7 value in the last token above). Concatenate and return encoded time and nonce.

Here is the code in Python:

Later I have found out that the same algorithm was used to generate the identifiers of users’ accounts and wallets. I̶t̶ ̶m̶e̶a̶n̶s̶ ̶t̶h̶a̶t̶ ̶t̶h̶e̶ ̶a̶l̶g̶o̶r̶i̶t̶h̶m̶ ̶i̶s̶ ̶a̶n̶ ̶a̶p̶p̶l̶i̶c̶a̶t̶i̶o̶n̶-̶w̶i̶d̶e̶ ̶P̶R̶N̶G̶.̶ Now I know that they use the ObjectId value from MongoDB as the identifiers.

Exploitation

The easy to guess format of token value allows to take over valid sessions of other users. The exploitation plan is the following:

Generate new token value. Wait some time (e.g. 30s). Generate new token value. Check if the difference between the tokens from point 3 and 1 is greater that 1. If no, go back to point 1. For all nonce values between token from point 3 and 1: For each UNIX timestamp between two checks: Generate token identifier using time from point 6 and nonce from point 5. Access authorized resource (e.g. /api/rate). If the application returns successful response, the valid token has been found.

By taking over the session, the attacker can perform any action on behalf of the victim. The most critical functionality is restoration of the X12 wallet seed which would allow to transfer the cryptocurrency to the attacker’s wallet.

The wallet seed is a secret phrase (usually a dozen or so words) used as the input for the private key generation algorithm. When you have the seed, you can generate the private key and transfer cryptocurrency from the wallet.

Fortunately, the seed restoration functionality was password-protected and it did not allow to easily empty victims’ wallets. However, attacker can perform a brute-force attack to find user’s password.

I have sent the following request 100 times with different password and the application did not block the account.



Host: app.skywallet.com:9018

Authorization: sky-wallet <5a85<redacted>5b>

Content-Length: 48

Origin:

Connection: close



{"id":"5a60<redacted>a2","password":"<password-guess>"} POST /api/wallet/mnemonic HTTP/1.1Host: app.skywallet.com:9018Authorization: sky-wallet <5a85 5b>Content-Length: 48Origin: https://app.skywallet.com Connection: close{"id":"5a60 a2","password":" "}

Consequences

The vulnerability (̶w̶e̶a̶k̶ ̶P̶R̶N̶G̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶)̶ (using ObjectId value as session identifier) allows anonymous attacker to take over victim’s account, but applies only to victims that log in when the attack is being executed. The attacker can view victim’s wallets details and perform actions on his behalf, including (in a critical case) the complete victim’s wallet and cryptocurrency takeover. To do so, he must first passively monitor victim’s wallet balances and wait for the cryptocurrency to appear (which is easy with victim’s authorization token).

The next step is the wallet seed restoration process, which is password-protected. However, as the functionality does not prevent brute-force attack, it is possible (depending on the password complexity) to recover victim’s password and recover his wallet seed. Now all he has to do is generate a private key and transfer the currency to his wallet.

Another potential consequence is the Denial of Service attack on users. Whenever the attacker finds new valid token, he can invalidate the session related to this token. This would not allow users to perform any action in the web application.

Remediation

The remediation of the vulnerability is to use cryptographically secure Pseudo Random Number Generator. The simplest solution is to use UUID generator, but in version 4 which is random! A universally unique identifier (UUID) is a 128-bit number used to identify information in computer systems. This would prevent guessing the identifiers of users, their wallets and, what is most important, their authorization token.