Poking through the contents of a BART card.

10 September 2024

"Check your ego at the door. No gods, no rockstars. Only n00bs." --thegibson

I try to go off the grid every couple of months to get away from everything and free up compute cycles for messing around with stuff. For whatever reason I find it helpful to go off to someplace quiet, possibly because it means that I can be nonverbal for a while. It definitely helps with my ADD. At any rate, that's my opportunity to teach myself some new things without having to multitask. When last I did this a couple of weeks back I brought with me, among other things, a Flipper Zero that I picked up some time ago but hadn't really had time to mess around with. To a degree this is because there seems to be a popular attitude of "If you don't already know how to do this thing and design your own hardware and software to do it, you're worthless" surrounding the Flipper Zero. I don't hold with that sort of bullshit and if anything it discourages lots of people from learning about things which is not okay.

I can rant for hours about this but I'll spare you.

Anyway over the summer an old friend sent me a couple of BART Clipper cards to mess around with, so I threw them into my bag of toys and spent a few hours messing around to see what I could get out of them. What I discovered I found a little bit disconcerting because it seems like a potential privacy problem. At the same time it makes sense because fares on the BART system are calculated based upon the station one gets on at and the station one gets off from. Additionally, an implicit part of their threat model involves riders trying to travel long distances while only paying for short hops (considering how gonzo the BART cops are about chasing gate jumpers it's not that surprising).

I don't think this is anything particularly groundbreaking. It's certainly not worth submitting a talk at a conference or anything. I thought it was a fun thing to do, I learned a lot about NFC doing it, and I enjoy sharing what I learn with people. So, I hope that you find this interesting and that it makes you think about privacy a little.

Clipper Cards are Near Field Communication cards that can be read from easily and written to with a little bit of work that are used to control access and pay fares to the San Francisco BART system. It's been a while since I've needed to ride it because I work from home these days, but I seem to recall that Clipper Cards are the only way to pay fares in the BART system. Each card has a 10 digit serial number printed on it (e.g., 1204425xxx). The core of each card is a Mifare DESfire chip that has a microcontroller that implements a cryptographic system in hardware for speed, some non-volatile storage for the microcontroller's firmware that also contains a unique UID code, a tiny radio (unsurprisingly) that operates at 13.56 MHz 1, and a small amount of rewritable storage that is organized into applications (which appear to be tiny blobs of code that run in the microcontroller) and encrypted data blocks (which hold account balances, trip information, and suchlike). The standard the card implements is NFC-A (technically ISO/IEC 14443-4).

If you ask the card nicely you can get an awful lot of information out of it, some of it interesting, some of it not so interesting unless you're writing software for it. I'll try to stick to the former. The card's UID is a series of seven hex values, e.g., 04:30:43:52:da:yy:yy. Only one application is burned into the card, and it seems to be the rider-side implementation of BART's ride and fare tracking system (application ID f21190). I didn't dump or try to reverse engineer it, though I might in the future. This application reserves eight files (memory blocks) for itself (numbered 1, 2, 4, 5, 6, 8, 14, and 15). You can get a little manufacturing information out of the card which is consistent with how the microprocessor industry does things; the card that I was messing around with was manufactured in the 20th week of 2013 (raw batch data: ba:44:59:df:40). The card can be reconfigured if you have the right software and cryptographic keys to do it but I didn't try to do this (mostly because I'm not particularly interested in learning to code for the DESfire chip).

The Flipper was able to pull copies of information that was already visible, like the name ("Clipper"), the card's serial number (same as the one printed on the back), the current card balance ($0.45us), the last time it was used (23 August 2015 at 19:07:02 hours UTC), the last NFC terminal it communicated with (ID code 0x4b30) (which is probably the particular turnstile at a particular BART station), the ID code of the last transaction for this card (6, which is probably the number of full rides this card was involved in), and a field called Counter which I'm guessing is the number of times the card has been used (for getting on and off BART, checking one's balance at a BART station kiosk, and putting money on said card).

The really interesting thing, I thought, was the card's ride history.

  • Ride #6
    • Date on: 20 December 2014
    • Time on: 23:59:28 hours UTC
    • Fare paid: $8.95us
    • Agency: BART (0x0004)
    • Station on: 19th Street/Oakland (0x000d)
    • Station off: SFO (0x002a)
    • Date off: 21 December 2014
    • Time off: 01:00:42 hours UTC
  • Ride #5
    • Date on: 19 December 2014
    • Time on: 01:43:15 hours UTC
    • Fare paid: $3.30us
    • Agency: BART (0x0004)
    • Station on: Powell Street (0x0008)
    • Station off: 19th Street/Oakland (0x000d)
    • Date off: 19 December 2014
    • Time off: 02:04:03d hours UTC
  • Ride #4
    • Date on: 18 December 2014
    • Time on: 23:38:11 hours UTC
    • Fare paid: $2.25us
    • Agency: Muni (0x0012) 2
    • Station on: City Street (0x0000) 3
    • Vehicle ID: 1011 4
    • Station off: n/a
    • Date off: n/a
    • Time off: n/a
  • Ride #3
    • Date on: 18 December 2014
    • Time on: 20:23:32 hours UTC
    • Fare paid: $0.00us
    • Agency: Muni (0x0012)
    • Station on: City Street (0x0000)
    • Vehicle ID: 6263
    • Station off: n/a
    • Date off: n/a
    • Time off: n/a
  • Ride #2
    • Date on: 18 December 2014
    • Time on: 19:50:09 hours UTC
    • Fare paid: $1.75us
    • Agency: Muni (0x0012)
    • Station on: Powell Street (0x0007) 5
    • Vehicle ID: 65535
    • Station off: n/a
    • Date off: n/a
    • Time off: n/a
  • Ride #1
    • Date on: 18 December 2014
    • Time on: 19:23:03 hours UTC
    • Fare paid: $3.30us
    • Agency: BART (0x0004)
    • Station on: 19th Street/Oakland (0x000d)
    • Station off: Powell Street (0x0008)
    • Date off: 18 December 2014
    • Time off: 19:45:42 hours UTC

Another card in the batch of Clipper cards I was given has the serial number 1204369xxx with a balance of $6.50us on it. It was last used on 22 June 2017 at 17:12:00 hours UTC at terminal 0xfe83 (transaction ID 500). Counter value: 893. Its UID is 04:38:1b:52:da:zz:zz, one application, eight files, 4096 bytes (of which 2016 are free). The rides it was used on:

  • Ride #16
    • Date on: 22 June 2017
    • Time on: 16:44:57 hours UTC
    • Fare paid: $1.95us
    • Agency: BART (0x0004)
    • Station on: Concord (0x0004)
    • Station off: Walnut Creek (0x0012)
    • Date off: 22 June 2017
    • Time off: 17:12:00 hours UTC
  • Ride #15
    • Date on: 22 June 2017
    • Time on: 05:17:00 hours UTC
    • Fare paid: $5.80us
    • Agency: BART (0x0004)
    • Station on: Civic Center/UN Plaza (0x0007)
    • Station off: Concord (0x0014)
    • Date off: 22 June 2017
    • Time off: 06:33:10 hours UTC
  • Ride #14
    • Date on: 21 June 2017
    • Time on: 23:17:03 hours UTC
    • Fare paid: $1.75us
    • Agency: Muni (0x0012)
    • Station on: Civic Center/UN PLaza (0x0008)
    • Vehicle ID: 65535
  • Ride #13
    • Date on: 21 June 2017
    • Time on: 22:52:38 hours UTC
    • Fare paid: $4.00us
    • Agency: BART (0x0004)
    • Station on: Downtown Berkeley (0x0018)
    • Station off: Civic Center/UN Plaza (0x0007)
    • Date off: 21 June 2017
    • Time off: 23:24:59 hours UTC
  • Ride #12
    • Date on: 21 June 2017
    • Time on: 19:04:46 hours UTC
    • Fare paid: $3.90us
    • Agency: BART (0x0004)
    • Station on: Concord (0x0014)
    • Station off: Rockridge (0x000f)
    • Date off: 21 June 2017
    • Time off: 19:39:37 hours UTC
  • Ride #11
    • Date on: 21 June 2017
    • Time on: 05:01:16 hours UTC
    • Fare paid: $5.80us
    • Agency: BART (0x0004)
    • Station on: Powell Street (0x0008)
    • Station off: Concord (0x0014)
    • Date off: 21 June 2017
    • Time off: 06:03:41 hours UTC
  • Ride #10
    • Date on: 21 June 2017
    • Time on: 03:11:33 hours UTC
    • Fare paid: $8.95us
    • Agency: BART (0x0004)
    • Station on: SFO Airport (0x002a)
    • Station off: Powell Street (0x0008)
    • Date off: 21 June 2017
    • Time off: 04:01:50 hours UTC
  • Ride #9
    • Date on: 21 June 2017
    • Time on: 01:04:01 hours UTC
    • Fare paid: $8.95us
    • Agency: BART (0x0004)
    • Station on: Civic Center/UN Plaza (0x0007)
    • Station off: SFO Airport (0x002a)
    • Date off: 21 June 2017
    • Time off: 01:35:09 hours UTC
  • Ride #8
    • Date on: 20 June 2017
    • Time on: 22:15:20 hours UTC
    • Fare paid: $5.80us
    • Agency: BART (0x0004)
    • Station on: Concord (0x0014)
    • Station off: Montgomery Street (0x0009)
    • Date off: 20 June 2017
    • Time off: 23:13:26 hours UTC
  • Ride #7
    • Date on: 18 June 2017
    • Time on: 19:08:19 hours UTC
    • Fare paid: $2.25us
    • Agency: Muni (0x0012)
    • Station on: City Stret (0x0000)
    • Vehicle ID: 1511
  • Ride #6
    • Date on: 18 June 2017
    • Time on: 14:56:07 hours UTC
    • Fare paid: $2.25us
    • Agency: Muni (0x0012)
    • Station on: Civic Center (0x0008)
    • Vehicle ID: 65535
  • Ride #5
    • Date on: 18 June 2017
    • Time on: 02:57:21 hours UTC
    • Fare paid: $11:30us
    • Agency: BART (0x0004)
    • Station on: SFO Airport (0x002a)
    • Station off: Concord (0x0014)
    • Date off: 18 June 2017
    • Time off: 04:27:06 hours UTC
  • Ride #4
    • Date on: 18 June 2017
    • Time on: 01:55:15 hours UTC
    • Fare paid: $8.80us
    • Agency: BART (0x0004)
    • Station on: 24th Street Mission (0x0005)
    • Station off: SFO Airport (0x002a)
    • Date off: 18 June 2017
    • Time off: 02:27:31 hours UTC
  • Ride #3
    • Date on: 17 June 2017
    • Time on: 20:09:20 hours UTC
    • Fare paid: $5.90us
    • Agency: BART (0x0004)
    • Station on: Concord (0x0014)
    • Station off: 16th Street Mission (0x0006)
    • Date off: 17 June 2017
    • Time off: 21:19:48 hours UTC
  • Ride #2
    • Date on: 11 June 2017
    • Time on: 03:36:06 hours UTC
    • Fare paid: $3.45us
    • Agency: BART (0x0004)
    • Station on: Civic Center/UN Plaza (0x0007)
    • Station off: 19th Street Oakland (0x000d)
    • Date off: 11 June 2017
    • Time off: 03:56:16 hours UTC
  • Ride #1
    • Date on: 10 June 2017
    • Time on: 21:27:06 hours UTC
    • Fare paid: $3.45us
    • Agency: BART (0x0004)
    • Station on: 19th Street Oakland (0x000d)
    • Station off: Embarcadero (0x000a)
    • Date off: 10 June 2017
    • Time off: 21:43:09 hours UTC

So, if you've ever wondered if there's a record of every public transportation trip in the Bay Area, there is at least one: On your Clipper Card. I don't know if that is the entire history of the card or if there is an upper limit on the size of the trip history on board. I compared the card stats and they're the same (application, files in memory, total and available sizes, and so forth) so I'm not sure. I have a few other Clipper cards that I can interrogate so maybe I'll be able to come to a conclusion after correlating the data. It seems reasonable to hypothesize that the same data is stored by BART on the back end for accounting purposes (not only to watch for fare evasion but to detect the somewhat common problem of "rider has more than one Clipper card in hand so which one was read first is unknown," along with the case of "rider accidentally tailgated the previous rider so their card wasn't scanned when they went through the turnstyle" 6), which means that it might be possible to file a California Public Records Act (CPRA) request and find out. I'll have to give that a try and see what happens.


Postscript: You're probably wondering why I didn't try writing to the card, or if I did I why I didn't talk about it. Part of the answer is basically because I wanted to get some experience with reading from NFC cards before I moved on to anything else. Additionally, because BART cards are part of a pay-for-use service I didn't think it wise to write an article that I had to run past my lawyer before I checked it into the Git repository. 7 If I was doing actual security research I'd worry about it but I'm just messing around with some stuff of personal curiosity. I'll give writing to NFC cards a shot later but it won't be with a Clipper card, it'll be with cards and devices that nobody has an active financial interest in.


  1. Technically it's a transponder - it recieves a signal from outside (that also supplies power to the chip), processes it, and transmits a contextually appropriate response. 

  2. The Bay Area has several Clipper enabled public transit systems, among them BART (the subway) and Muni (the aboveground rail). 

  3. You can just walk up and hop on Muni on the street in a couple of places. 

  4. This is the Muni train ID number ridden. 

  5. Powell Street has both BART and Muni stations. 

  6. This used to happen to me about once every two weeks. The solution is to go to the BART office at the turnstyles, get somebody's attention, have them beep you in (which will unlock the gate), go through the gate, and beep yourself out (which closes the ride). 

  7. I came of age in the 1990's. I've watched too many people get busted for publishing about something they were messing around with because the manufacturer in question decided to call the FBI on them. Lawyers have come after me for really stupid fucking reasons. Like a lot of folks in the hacker community I keep my lawyer as my In Case of Emergency contact. In short, I don't need that bullshit, I have enough going on right now.