Saturday, March 26, 2022

A Token Post

First, a little bit of catch-up

It's been a little while since I wrote anything for this blog. I've had another entry sitting in draft phase for several years, on the topic of automating wireless traffic interception with evil twin access points, but I never quite finished developing the tool that I intended to announce with the post. Ah well, such is life.

Fast-forward to now, and I've managed to launch a successful career in information security, first on the defensive side and now on the offensive. As we've all been muddling our way through a global pandemic, I picked up an interest in hardware hacking, microcontroller programming, and PCB design. I started with a headlong dive into a nearly-abandoned project for BSides CT 2020 and, although I can't say I was successful at rescuing it, I certainly learned a great deal and it whetted my appetite for more.

Over the last two and a half years, I've been playing with Arduino kits, ATtiny chips, and a variety of development boards to hone my hardware skills. I've put together a fun challenge badge for BSides CT 2022 (whenever it actually happens) that I hope to write more about later on. In the meantime, however, I've been focusing on a smaller project - a simple DIY TOTP generator. This blog post will walk through the development process and provide all the code and components you need to build and run one yourself, if you are so inclined.

Project goals

I had one motivation for this project: to avoid picking up my phone every time I needed to run SSH.

My daily "workstation" (read: attack host) is a Kali instance hosted in the AWS cloud. I connect to it via SSH and use Google Authenticator for MFA, but since Authenticator doesn't do push notifications, I'm left wasting precious seconds throughout the day picking up my phone, signing in with my thumbprint, opening the MFA app, and tapping the right profile to get the all-powerful six-digit code.

How much simpler would life be if that code was right in front of me at all times, so all I had to do was glance at it rather than interact with it in any way? If you're reading this thinking, "So...your one motivation is that you're lazy?," I simply cock my eyebrow in tacit acknowledgement.

I decided that, as a stretch goal, I would see if there was an elegant way to show additional codes - again without any interaction, because that would defeat goal #1 above. And while we're at it, let's set an arbitrary financial goal as well, because let's face it, I could just buy a Yubikey for about $40 if I really wanted to. But I don't want to, I want to build my own for...hmm...under $20.

So, to recap, my goal was to build a small, always-on device that would produce a valid MFA token with:

  1. Zero interaction
  2. Support for multiple tokens (as long as it doesn't contradict goal #1)
  3. Total cost <$20

Some background

The first task was to do some research and learn how MFA tokens are generated. Fortunately, a lot has been written online about this topic, so it was pretty easy to grasp the basics. Twilio, for example, has a great, easy-to-read primer on the subject. Here's the important stuff:

TOTP stands for Time-based One-Time Passwords and is a common form of two factor authentication (2FA). Unique numeric passwords are generated with a standardized algorithm that uses the current time as an input. The time-based passwords are available offline and provide user friendly, increased account security when used as a second factor.

TOTP is also known as app based authentication, software tokens, or soft tokens. Authentication apps like Authy and Google Authenticator support the TOTP standard.

The TOTP algorithm follows an open standard documented in RFC 6238. The inputs include a shared secret key and the system time.

The inputs to the TOTP algorithm are device time and a stored secret key. Neither the inputs nor the calculation require internet connectivity to generate or verify a token. Therefore a user can access TOTP via an app like Authy while offline.

Great! So there's an existing standard, which meant I probably wouldn't have to reproduce the wheel, but instead could use existing libraries to do the heavy lifting. I would just need to choose the right hardware components and slap it together with (relatively) little effort.

Hardware components

Next, I put together a shopping list. I learned that there are three main components to a TOTP generator:

  • Microcontroller to store the keys, run the token generator code, and drive the other components
  • Real-time clock module to keep accurate time
  • Display module to show the token

For power, I decided USB would be appropriate since the device would always be near my computer anyhow. After comparing a variety of options for the above, I settled on the following components for small size and low cost:

Note that the links above are provided for reference only - getting to the target cost would require some bargain hunting and bulk purchasing.

Prototype assembly

Once I got my hands on the components, I wired them together on a breadboard to see what they could do. A key factor in choosing these specific components was that they all supported the I2C communications bus, which made wiring a simple exercise. I only needed to connect eight wires in total - four from the Nano to the RTC, and four from the Nano to the display. Since both of the external modules shared the same bus, I connected them in parallel. Here's how it looked on my breadboard:


Component testing

Next it was time to give the components a test! The Arduino made this part super easy - I just had to plug in the Nano to my laptop via USB, install the free Arduino app, install some libraries for the components, and try out the example scripts that came with those libraries.

First up was the display (because that's much cooler to play with than the RTC). I loaded the "Adafruit SSD1306" library, opened the "ssd1306_128x32_i2c" example script, deployed the code to the Nano, and lo and behold - I had a working display!

Next was the RTC module. For this one, I loaded the "RTC" library Manjunath CV, opened the "DS3231_Info" example script, deployed the code to the Nano, and opened the serial terminal to observe the output. Once again, it all worked pretty smoothly, although I confess I played with several libraries before settling on the one I liked best - some were decidedly easier to use than others.

Writing the program

Now I was finally able to dive into the fun part! I identified several problems I would have to solve in order to get a working product. Specifically, I would need to:

  • Give the Nano the key to my Google Authenticator profile (and store it somewhere)
  • Set the RTC to the correct time
  • Generate an accurate TOTP
  • Show the token on the display (and update it every 30 seconds)
  • Show some sort of countdown indicator on the display

I tackled these challenges haphazardly in no particular order, putting the pieces together as I was able to solve them (and making a lot of mistakes along the way), but for the sake of easy reading I'll skip the messy bits and summarize the solutions I found.

First, programming the key and syncing the clock both required external input, so I decided the easiest way to accomplish this was via the USB serial connection built into the Nano. I wrote a Python script to run on my laptop that would communicate with the Nano's program using a simple serial protocol. The Python program would open a serial connection, send the current time to the Nano, and then prompt the user for other actions like entering or changing the key. This worked pretty well! Although it took a lot of refining as I added additional functionality, serial communications proved to be a reliable way to exchange data between the two devices.

To store the key on the Nano (so it would only have to be programmed once from the laptop), I decided to use EEPROM. This is an area of memory that persists even when reprogramming the Nano, so it seemed an appropriate place to record sensitive information. To accomplish this, I installed the "EEPROM" library and used the built-in "EEPROM.put()" function to store the key.

To program the RTC, I used the "RTC" library along with "Wire" to handle the I2C communications. After receiving a Unix timestamp from the laptop via USB serial, I just had to set the RTC time like so:

static DS3231 rtc;
rtc.begin();
rtc.stopClock(); 
rtc.setEpoch(timestamp); 
rtc.startClock();

Now that I had a key and the accurate time, I could start generating TOTPs! I reviewed the available libraries for Arduino and decided to go with one named "TOTP library" by Luca Dentella. Using the library was straightforward - to generate a TOTP from the key and current time, all I had to do was this:

TOTP totp = TOTP(key, strlen(key));
char* code = totp.getCode(rtc.getEpoch());

For the display, I decided to use the "Adafruit GFX Library" library with "Adafruit SSD1306" to drive the hardware. I chose a couple of fonts that looked good on the display, played around with formatting until I had an appealing look, and learned how to draw lines so that I could make a progress bar that would tick down as the token was nearing expiration:


With all the pieces put together, I was able to compare the output from my TOTP generator to my phone app, and was pleased to find matching codes!

Enhancing functionality

At that point, I had a working proof-of-concept, so I was feeling pretty pleased with myself. I still had only satisfied one of my three goals, however, so I continued to refine the product.

Meeting the second goal (support for multiple tokens) proved to be a bigger challenge than I had originally expected. Since I needed to store multiple keys in EEPROM, and the keys could vary in length (according to the TOTP specification), I had to devise a data model and a more sophistocated way of reading, writing, and updating the memory. I decided to add a quality-of-life enhancement while I was at it - specifically, a label for each key to tell them apart. The data model ended up looking like this:

struct DataObj {
  uint16_t keyLen[2];
  uint8_t keyName[2][3];
  uint8_t key[2][64];
};

As you can see, each key has a label (up to three characters), a long integer storing the key length, and the key itself. I extended the Python program with some additional menu options:

# Show menu with other options
menu = [
    'Sync time',
    'List keys',
    'Add key',
    'Delete key',
    'Reset device',
    'Quit'
]

After considering various options for displaying multiple tokens, I decided to keep it simple:

  • If the user records one token, just display the TOTP for that token at full size
  • If the user records two tokens, display TOTPs for both tokens at the same time, with their labels

That said, the Arduino code can be modified to allow more keys to be stored, up to the EEPROM memory limit, but the display code would have to be modified to rotate through the extra tokens, since fitting more than two on the display would make them too small to read.

At this point, I started hitting the limits of the program memory on the Nano, and I had to make some choices to conserve space in my code. After several attempts to optimize the code, I realized that the biggest memory hog was the large font I had imported, so I replaced it with a scaled-up version of the smaller font, and was able to keep most of the other functionality I wanted.

Putting it all together

Now the project was almost complete - I just needed to do some bargain-hunting for the components to hit my price goal, and I needed something other than a breadboard to hold the components together in a neat little package. For this, I whipped out KiCad and sketched out a simple PCB design:


By positioning the RTC module on the back of the PCB and letting it stick out behind the Nano, I could keep the whole package compact and easy to mount underneath my monitor - perfect! I found a Chinese fabricator that could print 10 of the tiny boards for $5 (plus shipping), and I hunted down the best deals I could find for everything else:

  • 5pcs Emakefun Micro USB Nano Board - Amazon $23.31
  • 2pcs I2C OLED Display Module - Amazon $9.53
  • 2pcs DS3231 RTC - Amazon $12.77
  • 5pcs Carte Micro USB Nano Board - eBay $36.27
  • 8pcs I2C OLED Display Module - eBay $27.44
  • 8pcs Dorhea DS3231 RTC - Amazon $27.54
  • 10pcs custom PCBs - AllPCB $9.38
Grand total: $146.24

In the end, by ordering enough components to build 10 TOTP generators, I succeeded in getting the price per unit down to $15, beating my original goal! If you're interested in checking out the code for this project, or if you want to build one yourself, head over to my GitHub for complete instructions. Thanks for reading, as always!

0 comments:

Post a Comment