E_NO_SUCH_BLOG

Programming, philosophy, pedaling.


Decoding the MetroCard, Part 2.5: Early Results

Oct 7, 2016

Tags: programming, reverse-engineering, security, metrocard

Preword: This post is a continuation of a series that I have been (very) slowly working on. Part 1 is non-technical, while Part 2 gets into a few of the technical details gleaned by previous researchers and hints at the direction I planned to take with my own work. A HN discussion for Part 2 can be found here.

This is Part 2.5 in a series of posts on the MetroCard. There were originally only going to be three posts, but I’ve decided that those similarly interested in analyzing the MetroCard would appreciate a post that covers both the toolkit I’m using and sample(s) of the data I’ve retrieved.

The Hardware

In order to experiment fully with the MetroCard (and other magstripe systems), I thought it would be wise to invest in a combined magnetic stripe reader/writer, rather than just a reader. I had also heard that normal magstripe readers (the kind that sell on Amazon and Ebay for $15 to $20) tend to have difficultly reading MetroCards due to their thinness.

I ended up going with the MSR-505C, which I bought used from Ebay for about $80. At the time of writing this, that price seems to have gone up a bit to the $95 to $100 range.

The MSR-505C

The MSR-505C

As far as I can tell, the MSR-505C is intended to be fully firmware compatible with an earlier (?) magnetic reader/writer, the MSR-206. Neither appears to be trademarked or manufactured by one single company, but rather by a variety of nameless manufacturers with copies of the firmware and datasheet. As such, sketchy sites that sell the two at absurd markups abound. I strongly recommend avoiding these - I and others have had fine experiences with Ebay sellers for a fraction of the “retail” price claimed by these sites.

The Software

The MSR-505C came with a mini-CD containing drivers and software for Windows and, naturally, no references to Linux support. Luckily, the 505C (by virtue of compatibility with the 206) supports interaction through a serial protocol that’s fully documented in the Programmer’s Manual provided by most of the retailers I came across. Even more luckily, (some) the hard work had already been done for me in the form of libmsr.

Updating libmsr

By the time I discovered it, libmsr hadn’t received any changes in a little over 5 years. Its codebase was the apparent product of several smaller projects combined together (including dab.c, written by Joseph Battaglia for his own attempt at decoding the MetroCard with a cassette head). It also wasn’t capable of acting as a stand-alone library, due to a tangled web of headers and partially exposed interfaces. The utilities, though operational, weren’t written with modification in mind and didn’t give me the flexibility I wanted with respect to configuring the 505C’s parameters without recompilation.

With these limitations in mind, I forked libmsr to woodruffw/libmsr and made a few important changes:

msr-dump

msr-dump is my addition to msr-utils.

It provides an easy (option-based) way to quickly dump raw card reads. It’s also possible to use the (patched) utilities that were originally bundled with libmsr, like msr-quick-raw-dumper, but these generally require source modification to change parameters and dump everything to stdout.

A quick example that reads a card in high-coercivity mode from the reader attached to /dev/ttyUSB0 and saves it as pretty-printed hex bytes to card.txt (most of these options are the default):

$ msr-dump --coercivity high --output card.txt --format hex --device /dev/ttyUSB0

(You might need sudo to run msr-dump, depending on what permissions /dev/ttyUSB0 has been initialized with. On most systems, this should be as simple as adding your user to the dialout group.)

A sample invocation:

msr-dump example

The MetroCards

Given the potentially sensitive nature of MetroCard data, I’m only going to be including pictures and dumps of card that I know are inactive or expired in this particular post.

The test cards (apologies for the bad images):

Card Fronts

Card Backs

Student MetroCard

The Student MetroCard is a “Special Program Pass,” which is slightly different from the standard Student MetroCard. In particular, when issued, it was only given two rides. This is in contrast to standard Student MetroCards, which are (or were, when I last used one) given three rides daily and expired on the last day of school. You can read more about it here.

Serial: 2425590632

Expires: 06/23/13

Unknown number below expiration date: 21960023

Reduced Fare MetroCard

The Reduced Fare MetroCard provides a 50% (currently $1.35) discount on all MTA subways and buses, as well as on the LIRR and Metro-North (except for weekday rush hours). It’s generally available to senior citizens (65+) and those with “qualifying disabilities.” You can read more about it here.

Serial: 2616819745

Expires: 02/29/16

Unknown number below expiration date: 23752464

Initial Results

Both cards were dumped 4 times. The Student MetroCard dumps ranged from 729 to 737 bytes (human representation), while the Reduced Fare MetroCard was constant at 713 bytes for all 4 scans.

The average dump looks something like this:

Track 1:
10000100111000010111101000010001101100000000110100110000000000000000000010000000000010100110010110100100001111110111110101111000001000110000101111010000010011011000000001101011000100000000000000000000000000000101000010011110
Track 2:
10000100111000010111101000010001101100000000110100110000000000000000000010000000000010100110010110100100001111110111110101111000001000110000101111010000010011011000000001101011000100000000000000000000000000000101000010011110
Track 3:
11101000101001000001001010000001100111011010000000000000000000000000000000000000000000000001001000010010011100100110110100000000000000000001101011110110010001001010011001001101001011001010100110010100101001100110101010011010010101001101001010110101

Using git diff --color-words=. $dump1 $dump2, the difference in the Student MetroCard dumps appears to be a stray 8 bits (11000000) appended to track 2. Since tracks 1 and 2 are supposed to be identical and this only showed up in one dump (#3), my assumption is that it’s just random noise from the reader.

Though all Reduced Fare MetroCard dumps were the same size, each contained many smaller changes. All of these changes in track 2, and all vary between individual scans (i.e., did not appear deterministic). Closer inspection of the card itself revealed a thin scratch in the magnetic stripe, deepest between tracks 1 and 2. Although track 1 appears unaffected in the scans, the presence of a physical defect doesn’t bode well for verifying the data.

MetroCard magstripe defect

(This was the best picture I got of the defect.)

Summary

To my dismay, I was unable to find the track sentinels that Battaglia mentions in his paper and HOPE/CCC presentations. This didn’t come as a particular surprise, however, as his research is over a decade old and the MTA is more than capable of making rolling updates to the MetroCard system (as evidenced by the blue-yellow transition in the late 1990s to early 2000s). Another unsavory possibility is that I’m on the entirely wrong track (no card or train pun(s) intended) with respect to my scanning approach - Battaglia’s success was with a purpose-built scanner.

On the plus side, besides the trailing noise and physical defect on the Reduced Fare card, the general structure of the MetroCard appears unchanged from the mid-2000s. In particular, tracks 1 and 2 still appear to be the shorter, variable-data tracks, while track 3 is the longer, static track.

If you’d like to play with the dumps yourself, you can find them in (now updated) archive here under the card_dumps/ directory.

If you’d like to recreate my experimental setup with an MSR-505C (or firmware equivalent), you can download my version of libmsr and the msr-dump utility. Building both (on Linux) should be as simple as make && sudo make install.

Future Directions

I haven’t included dumps of either active or unexpired cards in this post, as I have both legal and practical concerns about doing so. While my research will continue in that direction, any future post(s) and disclosure(s) in this series will likely be limited to analysis and description of the dumps, without copies of the dumps themselves.

A large part of the next post in this series depends on me actually being in NYC to purchase new MetroCards and take short rides to observe differences in track state. Until I’m there (with my reader/writer), that work is on hold. In the mean time I’ll be analyzing the (active) MetroCards that I have in my possession, which can only get me so far due to my inability to study a card as its value and timestamps change.

Thanks for reading!

- William Woodruff

Postnotes: During my continued research, I came across two projects/organizations that claim to retrieve the leftover value from MetroCards for charity: The NeXT Stop Project and MetroChange. Both appear to be defunct, ostensibly having experienced pushback from the MTA or run into technical barriers. The latter appears to have success circa 2011 reading cards using a program inspired by Battaglia’s original dab/dmsb.