Tags: metrocard, programming, reverse-engineering, security
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.
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.
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 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.
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:
libmsr.h), to make installation and compilation simpler.
is my addition to
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
msr-quick-raw-dumper, but these generally require source
modification to change parameters and dump everything to
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
card.txt (most of these options are the default):
1 $ 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
A sample invocation:
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):
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.
Unknown number below expiration date: 21960023
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.
Unknown number below expiration date: 23752464
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:
1 2 3 4 5 6 Track 1: 10000100111000010111101000010001101100000000110100110000000000000000000010000000000010100110010110100100001111110111110101111000001000110000101111010000010011011000000001101011000100000000000000000000000000000101000010011110 Track 2: 10000100111000010111101000010001101100000000110100110000000000000000000010000000000010100110010110100100001111110111110101111000001000110000101111010000010011011000000001101011000100000000000000000000000000000101000010011110 Track 3: 11101000101001000001001010000001100111011010000000000000000000000000000000000000000000000001001000010010011100100110110100000000000000000001101011110110010001001010011001001101001011001010100110010100101001100110101010011010010101001101001010110101
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.
(This was the best picture I got of the defect.)
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
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.
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,
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