MagTek USB Card Reader Hacking

So just the other day I received my MagTek MSR100 in the mail, this unit only cost me about $20 and I have to say I’m very satisfied with it. cardreader After opening the box it was delivered in I quickly noticed no documentation was provided. No worries I figured, this will make hacking at it that much more fun.

I started out by connecting the USB device to my Gentoo Linux laptop and swiped a card, I noticed on my console prompt the card data was spewed out. That is because this device acts like a HID Keyboard:

# lsusb -vv | grep "Mag-Tek Mini Swipe" -A50 | grep bInterfaceProtocol
      bInterfaceProtocol      1 Keyboard

With this information confirmed I figured I would approach it with the same method used in one my older post Python Device Hacking (Keyboard).

Using the k() function outlined in the Keyboard hacking post mentioned above, I received the following while scanning my Pluckers card:

>>> k()
KEY_LEFTSHIFT
KEY_5
KEY_LEFTSHIFT
KEY_B
KEY_7
KEY_2
KEY_3
KEY_1
KEY_5
KEY_3
KEY_7
KEY_7
KEY_1
KEY_0
KEY_5
KEY_6
KEY_6
KEY_2
KEY_3
KEY_2
KEY_5
KEY_0
KEY_4
KEY_7
KEY_6
KEY_7
KEY_9
KEY_1
KEY_3
KEY_9
KEY_7
KEY_0
KEY_3
KEY_8
KEY_LEFTSHIFT
KEY_SLASH
KEY_SEMICOLON
KEY_2
KEY_2
KEY_1
KEY_6
KEY_5
KEY_0
KEY_0
KEY_1
KEY_1
KEY_1
KEY_6
KEY_2
KEY_3
KEY_6
KEY_7
KEY_6
KEY_EQUAL
KEY_1
KEY_LEFTSHIFT
KEY_SLASH

This data looked similar to the results I seen on screen, but not quite.. and printing on the screen at the same time was pretty ugly:

%B723153771056323250476791397088?;221350011623672=1?

At that point I figured it was time for a better approach, and where best to look than our friend Google? A quick search brought up a article describing a similar project, unfortunately our devices must differ as his code didn’t quite work with my device. However this article showed me the PyUSB module.

Using the PyUSB module I whipped up a quick script:

>>> import usb.core
>>> import usb.util
>>>
>>> vendorid = 0x0801
>>> productid = 0x0001
>>> device.detach_kernel_driver(0)
>>> endpoint = device[0][(0,0)][0]
>>>
>>> device.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, timeout=5000)
array('B', [2, 0, 34, 0, 0, 0, 0, 0])
>>> device.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, timeout=5000)
array('B', [0, 0, 0, 0, 0, 0, 0, 0])
>>> device.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, timeout=5000)
array('B', [2, 0, 5, 0, 0, 0, 0, 0])
>>> device.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, timeout=5000)
array('B', [0, 0, 0, 0, 0, 0, 0, 0])
>>> device.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize, timeout=5000)
array('B', [0, 0, 36, 0, 0, 0, 0, 0])

At this point I got stuck for a bit, you see the data above was not in ASCII format and did not match my earlier keyboard map. I did figure out the results by comparing onscreen output to my PyUSB output:

I took the known good data (from on screen):

%B723153771056323250476791397088?;221350011623672=1?

Comparing against PyUSB I noticed the 3rd column appeared to be the bits of data I needed:

[2, 0, 34, 0, 0, 0, 0, 0] = %
[2, 0, 5, 0, 0, 0, 0, 0]  = B
[0, 0, 36, 0, 0, 0, 0, 0] = 7
[0, 0, 31, 0, 0, 0, 0, 0] = 2
[0, 0, 32, 0, 0, 0, 0, 0] = 3
[0, 0, 30, 0, 0, 0, 0, 0] = 1
[0, 0, 34, 0, 0, 0, 0, 0] = 5
[0, 0, 32, 0, 0, 0, 0, 0] = 3
[0, 0, 36, 0, 0, 0, 0, 0] = 7
[0, 0, 36, 0, 0, 0, 0, 0] = 7
[0, 0, 30, 0, 0, 0, 0, 0] = 1
[0, 0, 39, 0, 0, 0, 0, 0] = 0
[0, 0, 34, 0, 0, 0, 0, 0] =
[0, 0, 35, 0, 0, 0, 0, 0]
[0, 0, 35, 0, 0, 0, 0, 0]
[0, 0, 31, 0, 0, 0, 0, 0]
[0, 0, 32, 0, 0, 0, 0, 0]
[0, 0, 31, 0, 0, 0, 0, 0]
[0, 0, 34, 0, 0, 0, 0, 0]
[0, 0, 39, 0, 0, 0, 0, 0]
[0, 0, 33, 0, 0, 0, 0, 0]
[0, 0, 36, 0, 0, 0, 0, 0]
[0, 0, 35, 0, 0, 0, 0, 0]
[0, 0, 36, 0, 0, 0, 0, 0]
[0, 0, 38, 0, 0, 0, 0, 0]
[0, 0, 30, 0, 0, 0, 0, 0]
[0, 0, 32, 0, 0, 0, 0, 0]
[0, 0, 38, 0, 0, 0, 0, 0]
[0, 0, 36, 0, 0, 0, 0, 0]
[0, 0, 39, 0, 0, 0, 0, 0]
[0, 0, 32, 0, 0, 0, 0, 0]
[0, 0, 37, 0, 0, 0, 0, 0]
[2, 0, 56, 0, 0, 0, 0, 0] = ?
[0, 0, 51, 0, 0, 0, 0, 0] = ;
[0, 0, 31, 0, 0, 0, 0, 0] = 2
[0, 0, 31, 0, 0, 0, 0, 0] = 2
[0, 0, 30, 0, 0, 0, 0, 0] = 1
[0, 0, 35, 0, 0, 0, 0, 0] = 6
[0, 0, 34, 0, 0, 0, 0, 0] = 5
[0, 0, 39, 0, 0, 0, 0, 0] = 0
[0, 0, 39, 0, 0, 0, 0, 0] = 0
[0, 0, 30, 0, 0, 0, 0, 0] = 1
[0, 0, 30, 0, 0, 0, 0, 0] = 1
[0, 0, 30, 0, 0, 0, 0, 0] = 1
[0, 0, 35, 0, 0, 0, 0, 0] = 6
[0, 0, 31, 0, 0, 0, 0, 0] = 2
[0, 0, 32, 0, 0, 0, 0, 0] = 3
[0, 0, 35, 0, 0, 0, 0, 0] = 6
[0, 0, 36, 0, 0, 0, 0, 0] = 7
[0, 0, 35, 0, 0, 0, 0, 0] = 6
[0, 0, 46, 0, 0, 0, 0, 0] = =
[0, 0, 30, 0, 0, 0, 0, 0] = 1
[2, 0, 56, 0, 0, 0, 0, 0] = ?
[0, 0, 88, 0, 0, 0, 0, 0]

After trying everything I could think I finally gave up and searched for a Developers Manual for the device. The PDF above gave me a keycode map which made complete sense on the above output (see snippet below): ../_images/keycode.jpg This last piece of information was all I needed to decipher data from my device, and this was the code I ended up with:

#!/usr/bin/env python

# MagTek MSR100 Mini Swipe Card Reader
# Written By: nessy
#
# Some Thanks need to go out to
# http://www.micahcarrick.com/credit-card-reader-pyusb.html
# for helping me get on the right track

import usb.core
import usb.util

# MagTek Device MSR100 Mini Swipe
vendorid = 0x0801
productid = 0x0001

# Define our Character Map per Reference Manual
# http://www.magtek.com/documentation/public/99875206-17.01.pdf

chrMap = {
    4:  'a',
    5:  'b',
    6:  'c',
    7:  'd',
    8:  'e',
    9:  'f',
    10: 'g',
    11: 'h',
    12: 'i',
    13: 'j',
    14: 'k',
    15: 'l',
    16: 'm',
    17: 'n',
    18: 'o',
    19: 'p',
    20: 'q',
    21: 'r',
    22: 's',
    23: 't',
    24: 'u',
    25: 'v',
    26: 'w',
    27: 'x',
    28: 'y',
    29: 'z',
    30: '1',
    31: '2',
    32: '3',
    33: '4',
    34: '5',
    35: '6',
    36: '7',
    37: '8',
    38: '9',
    39: '0',
    40: 'KEY_ENTER',
    41: 'KEY_ESCAPE',
    42: 'KEY_BACKSPACE',
    43: 'KEY_TAB',
    44: ' ',
    45: '-',
    46: '=',
    47: '[',
    48: ']',
    49: '\\',
    51: ';',
    52: '\'',
    53: '`',
    54: ',',
    55: '.',
    56: '/',
    57: 'KEY_CAPSLOCK'
}

shiftchrMap = {
    4:  'A',
    5:  'B',
    6:  'C',
    7:  'D',
    8:  'E',
    9:  'F',
    10: 'G',
    11: 'H',
    12: 'I',
    13: 'J',
    14: 'K',
    15: 'L',
    16: 'M',
    17: 'N',
    18: 'O',
    19: 'P',
    20: 'Q',
    21: 'R',
    22: 'S',
    23: 'T',
    24: 'U',
    25: 'V',
    26: 'W',
    27: 'X',
    28: 'Y',
    29: 'Z',
    30: '!',
    31: '@',
    32: '#',
    33: '

I’ve went ahead and uploaded this source to Github in case anyone likes to clone it https://github.com/jness/magtek_cardreader/blob/master/main.py Using this code is very basic and is show below:

# ./main.py
Swipe Card:
%B723153771056323250476791397088?;221350011623672=1?

I’ve went ahead and uploaded this source to Github in case anyone likes to clone it https://github.com/jness/magtek_cardreader/blob/master/main.py Using this code is very basic and is show below: