Stina, Yubico’s CEO, gave me a Yubikey at RSA London last year. It’s a small keyfob simulating a USB keyboard. Each time you press the button while it’s inserted in a USB port, it generates a one-time-password.

The Yubikey is a clever little two-factor authentication device.
But I’ve some issues using the Yubikey in a really secure system. As Twitter plays a role in this, and because lately Twitter started to be used by trojans as a communication channel, I decided it’s time to publish the issues I encountered together with mitigating actions.
Technical details
The OTP generated by the Yubikey is an AES-encrypted data stream. I’ve obtained the AES-key embedded in my Yubikey from Yubico and am able to decode the OTP with a simple Python program.
Insert Yubikey and start generating OTPs:
OTP: lkeuuuceeeivjgtbjcbevigeccerfufugdijuhflckrd
public_id = lkeuuuceeeiv
secret_id = ************
counter = 26
counter_session = 1
random_number = 13042
timestamp = 0x8321a8
- public_id and secret_id identifies the Yubikey (notice that the public_id is the prefix of the OTP).
- counter: this is a persistent register. It is increased with 1 each time the Yubikey is inserted in a USB port.
- counter_session: this is a volatile register. It is set to 0 each time the Yubikey is inserted in a USB port, and increased with 1 for each OTP generation
- random_number is what it says on the tin: a random number, different per OTP
- timestamp is a volatile 32-bit register. It is set to a random value each time the Yubikey is inserted in a USB port, and is then increased with 1 by a 8Hz clock. Yubico specifies an average variation of 20% on the 8Hz clock per Yubikey. With the measurements I made, I calculate that the 8Hz clock of my key has a 32% deviation.
Validating an OTP is done by successfully decrypting the OTP. Replay attacks are mitigated by comparing the counters and timestamp with historical data.
When trying to design a website that uses the Yubikey to authenticate, I imagined the following attack and found a way to mitigate it.
Attack 1:
Assume a website that uses the Yubikey to logon (i.e. an OTP generated with your Yubikey is needed to log on to the site, possibly together with more classic credentials like a username/password combo).
Because this website has my AES key and can decrypt my OTP, my Yubikey authenticates me and I’m granted access to the site.
A web browser trojan could steal an OTP like this:
- I generate an OTP (OTP1) with my Yubikey
- The trojan intercepts and stores OTP1, doesn’t send OTP1 to the website, but makes the browser display a fake error message (404, server load too high, Yubikey error, …) prompting me to generate a second OTP
- I’m fooled by the fake error, and generate a second OTP (OTP2) with my Yubikey
- The trojan intercepts and stores OTP2, and sends OTP1 to the website
- The website grants me access, and the trojan stops interfering
- OTP2 can be used by the operators of the trojan to get access to the website, as long as I’m not first to access the website at a later time with a new OTP (OTP3). Twitter could be used as a channel to communicate the OTPs in real-time to the trojan operators.
Mitigation:
The website can detect this attack (the malicious use of OTP2) if the following algorithm is implemented:
- for every account, the last valid OTP is stored, together with a server-side timestamp (when it was received by the website)
- if a new OTP is received, the counter value of the previous OTP is compared with the counter value of the new OTP
- when both counters have the same value, the website knows that the OTPs were generated in the same session, and thus that it can compare timestamps.
- it calculates the delta of the timestamps of the OTPs, and also the delta of the server-side timestamps when it received the OTPs.
- if the 2 deltas differ too much (more than 20% margin), then it refuses the OTP and doesn’t grand access to the website
But now comes the second attack for which I have no mitigation, despite some help from the Yubico forum.
Attack 2:
Assume a website which uses the Yubikey OTP to 1) authenticate users and 2) validate transactions. A classic example of such a site is an online banking site. The user generates an OTP to logon, and then has to generate an OTP for each financial transaction.
A web browser trojan could insert its own transaction like this:
- I generate an OTP (OTP1) with my Yubikey
- The trojan intercepts and stores OTP1 together with a timestamp, doesn’t send OTP1 to the website, but makes the browser display a fake error message (404, server load too high, Yubikey error, …) prompting me to generate a second OTP
- I’m fooled by the fake error, and generate a second OTP (OTP2) with my Yubikey
- The trojan intercepts and stores OTP2 together with a timestamp, and sends OTP1 to the website
- The website grants me access
- After an amount of time equal to the delta between the 2 stored timestamps, the trojan starts a transaction (invisible to the user) and uses OTP2 to validate the transaction.
- When I start my own transactions, the trojan passes the OTPs on to the website, but delays them with the same timestamp delta to avoid post-exploitation detection.
I can’t device an algorithm to detect this fraud server-side, if the Yubikey is the only authentication and validation mechanism used. This attack would not work with a challenge-response token, because the keys generated by such a token are different for logon and transaction validation. Typically, these tokens generate one type of keys for logon, and another type of keys based on a challenge for transactions. The challenge encodes data of the transaction, so that a particular challenge can’t be used for another transaction.
One Yubico forum member suggests a type of CAPTCHA to ensure that each submitted OTP is submitted by a human (hence the CAPTCHA), but I don’t believe this is practical, as malware is able to defeat some CAPTCHAs and humans are unable to solve some CAPTCHAs.
Feel free to post a comment with your migitation suggestions, but please keep them practical ;-).