I go to Loughborough University. Recently, I got an email saying from university’s IT Services saying “hey, we’re going to force the use of multi-factor authentication”. This is a good idea for security, but the implementation of MFA that they have chosen is subpar.
TL;DR: I don’t like to be mandated to run commercial software on my own devices, so I did some reverse engineering and reimplemented it in the open.
Duo Security is a Cisco-owned company that aims to “Protect your workforce with simple, powerful access security.” Since we’re being forced onto it as a student body, they must also have a killer sales pitch.
The problem, however, is that Duo’s 2FA implementation requires that you have their app. This is inadequate, since the minimum Android API level is too new to use an dedicated, airgapped, outdated device for 2FA.
Registering a Device
The regular flow for registering a 2FA device looks like this:
- Head on over to
duo.lboro.ac.uk, where there’s an iframe pointing to some
- Choose Tablet (Phone is for SMS authentication1, apparently), then Android, then I already have Duo Mobile installed, where we are presented with a QR code.
- Scan the QR code, and, presumably, the Duo Mobile app does the rest of the work.
Throwing CyberChef at the QR code (because I’m too lazy to grab
qrdecode for my system), we see that it looks something like this:
- in here, and there’s telling marks of a Base 64 string.
If I decode with Base 64, it fails, since
- isn’t part of the standard b64 alphabet.
However, if I just take the portion after the
-, I get:
Anyway, we can find out more with some analysis of the software.
After a glance at the (ProGuard-obfuscated) Android app, we see that:
- The QR code is parsed, and decoded as ASCII text
- The resulting string is split by a
-into two components
- The first component is used as a URL parameter
- The second component is used as the URL’s authority
The constructed URL looks like:
Which I guess expands to something like
There’s also a URL-encoded request body, but I can either throw a proxy or a debugger at the actual application to get those details.
Okay, so we can see the rest of the protocol from
- Send the POST request with some parameters reassuring the server that the, uh - Android device we’re running on ;) - is Very Real and doesn’t have its integrity compromised from rooting, or something.
- JSON-decode the response, and you can find the HOTP secret at
.response.hotp_secret. I guess we’re done, HOTP is an open standard.
- Calling the
/push/v2/activationendpoint probably increments a counter server-side, so we’ll need to not get too out-of-sync with the serverside counter.
Anyway, this is shaping up to be really easy.
Doing the Thing
So, let’s take our QR code and turn it into some HOTP secret that I can use with a better OTP application. I planned to throw it into my Bitwarden vault5, but it looks like the Bitwarden client only supports TOTP. So, instead, I’m using FreeOTP, which is a Red Hat project.
duo-cli repo would be perfect, but it doesn’t let me decode the QR code straight from the CLI: I have to do it manually, and then edit the Python script to set some variables for use.
Instead of fixing this minor gripe, I made something Invented Here: github.com/char/duo-hotp-export
You can just
cargo run --release -- ./path/to/some/qr/code.png.
So now authenticating at Loughborough is as easy as:
- Building a Rust project that some random student made,
- Putting your security-sensitive credentials (as a QR code) into this program,
- Letting it spit out an
- Hoping that Duo Mobile doesn’t update to a different authentication scheme in the future!
- Giving the HOTP URI to an actual OTP management app, and letting it generate codes for you.
But, hey, at least we’re avoiding running some weird commercial software on our own devices.
Seriously, who trusts Cisco software in a post-Snowden world? ↩︎
I develop a commercial Java obfuscator, so I have this stuff ready-made for debugging my own work. ↩︎
Well, I found the base fork (called
duo-bypass), and followed the network to this, more updated, project. ↩︎