Writing your own data to an Activision Skylanders NFC toy

As discussed in the case study, by knowing the algorithm used to set the read/write passwords (keys A), we can interoperably read/write our own data to a Skylanders NFC toy. Read New interoperability for Activision Skylanders NFC toys for details on the algorithm, and a demonstration video showing it in use.

By request, this page documents a workflow similar to the demo video, using standard software available on any Mac or Linux computer.

Prerequisites

You'll need to compile libnfc's utilities from source. (The last stable release of libnfc, 1.7.1, does not recognize the TNP3xxx tag inside Skylanders NFC toys as a MIFARE tag.) Linux users should be able to compile it using the normal process for their operating system. Mac users may be able to use macports or homebrew, or compile it natively.

You'll also need a libnfc-supported NFC reader. These examples used the Identiv SCL3711.

You'll need the tnp3xxx.py Python 2 program, which is an example implementation of the key generation algorithm, listed in New interoperability for Activision Skylanders NFC toys.

We'll be working in hexadecimal, which is easier for people to read, but the tools use the "MIFARE Dump" format. Download the eml2mfd.py and mfd2eml.py programs from the MIFARE Classic Tool source control so we can convert data back and forth.

You may also want the emlinsert.py Python 2 program, which makes it easier to create the file necessary to write custom data to an NFC tag, listed at the bottom of this page.

Identifying a Skylanders NFC toy

Here's the output of the libnfc standard tool, nfc-list, when run against the "Ninjini" figure used in the demo. (This is the same as for any MIFARE tag.)

nfc-list 
nfc-list uses libnfc libnfc-1.7.1-191-g216145f
NFC device: SCM Micro / SCL3711-NFC&RW opened
1 ISO14443A passive target(s) found:
ISO/IEC 14443A (106 kbps) target:
    ATQA (SENS_RES): 0f  01  
       UID (NFCID1): 04  8b  9f  38  
      SAK (SEL_RES): 01  

An ATQA of 0f 01 with an SAK of 01 means an Activision Skylander NFC toy.

The UID of this figure is 04 8b 9f 38.

Generating the keys for a Skylanders NFC toy

Pass the UID to the tnp3xxx.py program:

./tnp3xxx.py 048b9f38
4b0b20107ccb
edb9a575cc35
cbd471261bf6
58e29b8ff017
143833285e90
870ed981b571
a1630dd262b2
3255e77b8953
aae1b634d45c
39d75c9d3fbd
1fba88cee87e
8c8c6267039f
c056cac0ad18
5360206946f9
750df43a913a
e63b1e937adb

Those are the 16 keys A necessary to read or write the Skylanders NFC toy, generated algorithmically, instead of using an exploit.

Reading the data from a Skylanders NFC toy

To use these keys with the libnfc MIFARE tools, they need to be in a MIFARE Dump format. The sample algorithm implementation can output a hexadecimal, Proxmark emulator format (EML), which we can convert.

./tnp3xxx.py 048b9f38 -eml
048b9f38000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
4b0b20107ccb00000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
edb9a575cc3500000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
cbd471261bf600000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
58e29b8ff01700000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
143833285e9000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
870ed981b57100000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
a1630dd262b200000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
3255e77b895300000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
aae1b634d45c00000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
39d75c9d3fbd00000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
1fba88cee87e00000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
8c8c6267039f00000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
c056cac0ad1800000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
5360206946f900000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
750df43a913a00000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
e63b1e937adb00000000000000000000

Those are the same 16 keys A, plus the UID, written "in place", where they'd be in a hexadecimal dump of the tag.

Save that to ninjini-keys.eml and convert it to a MIFARE Dump file using eml2mfd.py.

./tnp3xxx.py 048b9f38 -eml > ninjini-keys.eml
./eml2mfd.py ninjini-keys.eml ninjini-keys.mfd

Read the contents of the tag using the libnfc standard tool nfc-mfclassic, using the generated keys, saving it into ninjini-dump.mfd. (This is the same as for any MIFARE tag.)

nfc-mfclassic r a u ninjini-dump.mfd ninjini-keys.mfd 
NFC reader: SCM Micro / SCL3711-NFC&RW opened
Warning: tag is probably not a MFC!
Found MIFARE Classic card:
ISO/IEC 14443A (106 kbps) target:
    ATQA (SENS_RES): 0f  01  
       UID (NFCID1): 04  8b  9f  38  
      SAK (SEL_RES): 01  
Guessing size: seems to be a 1024-byte card
Reading out 64 blocks |................................................................|
Done, 64 of 64 blocks read.
Writing data to file: ninjini-dump.mfd ...Done.

Convert the MIFARE Dump to hex to view it, using mfd2eml.py and the standard Unix tool cat.

./mfd2eml.py ninjini-dump.mfd ninjini-dump.eml
cat ninjini-dump.eml 
048b9f382881010fc427000000000012
6d00000070dccb6ed34d00000612f2fe
00000000000000000000000000000000
4b0b20107ccb0f0f0f69000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
edb9a575cc357f0f0869000000000000
4a68686d01d47280922a10efcc992cf9
2d1edb4723596ebc82379580ed505548
3982b1519ae73c4c2e83f19c8545c2c8
cbd471261bf67f0f0869000000000000
0f5dc22225ac4cc829f13c9bb5b945a6
25bf3115305d84f89a9cbafeab3a9fcc
8f577737b56789db90dd2d24b63725f6
58e29b8ff0177f0f0869000000000000
78330690ea993234f430bd9ffbbe4ca2
c2af04e38548b1eb2963c2a375814484
b3e2159b916e87119931a56e16e7e244
143833285e907f0f0869000000000000
2f0b587954b1d9359c3e839e24084a8d
3e9b2242b05332dccd47743fb94ff797
00000000000000000000000000000000
870ed981b5717f0f0869000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
a1630dd262b27f0f0869000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
3255e77b89537f0f0869000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
aae1b634d45c7f0f0869000000000000
5dec563fb36a990593c9018cd413497a
d9619af2021a1a17e095b50f0fefb431
d3cc12b4374fa085a9c82460d38b3484
39d75c9d3fbd7f0f0869000000000000
a643e3ba6683cee88aebb7d5f0716bd2
c65da3ce146fd87989451264031a8cc0
75e7e572654fdf5c4325b6a0b30440ec
1fba88cee87e7f0f0869000000000000
f3268969aad925141a6f476cc01802ae
d1f7e4b556e0cd359b9589bf258ecd5c
6b725252a03a538175875ccd4142a383
8c8c6267039f7f0f0869000000000000
7d910ce264ad63270e6bb427e1f51ffb
8f7cc6b94dc46470c0fecede3fb8a0b0
00000000000000000000000000000000
c056cac0ad187f0f0869000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
5360206946f97f0f0869000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
750df43a913a7f0f0869000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
e63b1e937adb7f0f0869000000000000

(This is similar to the data we saw on the toy in the case study and in Toys only store data: Skylanders Giants character figures.)

Extract the writable, 720 bytes from the hex output and evaluate it, using the standard Unix sed, xxd and file tools.

sed -n -e 5,7p -e 9,11p -e 13,15p -e 17,19p -e 21,23p -e 25,27p -e 29,31p -e 33,35p -e 37,39p -e 41,43p -e 45,47p -e 49,51p -e 53,55p -e 57,59p -e 61,63p ninjini-dump.eml | xxd -r -ps > ninjini-720.bin
file ninjini-720.bin 
ninjini-720.bin: data

Since this is the encrypted gameplay data from the toy, it's just "data", and we can't do anything further with it.

Writing our own data to a Skylanders NFC toy

Create your own data to write, up to 720 bytes, and save it. For example, this is similar text to the demo video, saved as nfctoys-sky.txt.


nfc.toys

Previously, only Activision hardware and games had the ability to read from and write data to Skylander NFC toys.

(Reading standard features like UID, ATQA & SAK was always possible.)

Now, with the algorithm for the keys A, Skylander NFC figures are able to interoperate with other hardware and software, and can be used to store any data at all.

Previously, only Activision hardware and games had the ability to read from and write data to Skylander NFC toys.

(Reading standard features like UID, ATQA & SAK was always possible.)

Now, with the algorithm for the keys A, Skylander NFC figures are able to interoperate with other hardware and software, and can be used to store any data at all.

nfc.toys

Convert your data to hex, in a similar structure to the Proxmark emulator format, using the standard Unix xxd tool.

xxd -c 16 -l 720 -ps nfctoys-sky.txt 
6e66632e746f79730a0a50726576696f
75736c792c206f6e6c79204163746976
6973696f6e2068617264776172652061
6e642067616d65732068616420746865
206162696c69747920746f2072656164
2066726f6d20616e6420777269746520
6461746120746f20536b796c616e6465
72204e464320746f79732e0a0a285265
6164696e67207374616e646172642066
65617475726573206c696b6520554944
2c204154514120262053414b20776173
20616c7761797320706f737369626c65
2e290a0a4e6f772c2077697468207468
6520616c676f726974686d20666f7220
746865206b65797320412c20536b796c
616e646572204e464320666967757265
73206172652061626c6520746f20696e
7465726f706572617465207769746820
6f746865722068617264776172652061
6e6420736f6674776172652c20616e64
2063616e206265207573656420746f20
73746f726520616e7920646174612061
7420616c6c2e0a0a50726576696f7573
6c792c206f6e6c792041637469766973
696f6e20686172647761726520616e64
2067616d657320686164207468652061
62696c69747920746f20726561642066
726f6d20616e64207772697465206461
746120746f20536b796c616e64657220
4e464320746f79732e0a0a2852656164
696e67207374616e6461726420666561
7475726573206c696b65205549442c20
4154514120262053414b207761732061
6c7761797320706f737369626c652e29
0a0a4e6f772c20776974682074686520
616c676f726974686d20666f72207468
65206b65797320412c20536b796c616e
646572204e4643206669677572657320
6172652061626c6520746f20696e7465
726f7065726174652077697468206f74
68657220686172647761726520616e64
20736f6674776172652c20616e642063
616e206265207573656420746f207374
6f726520616e79206461746120617420
616c6c2e0a0a6e66632e746f7973

Open up the ninjini-keys.eml file, replace the 00000000000000000000000000000000 blocks with your hex content, and save it as a new file, such as ninjini-new.eml. Remember that sector 0 is write-only, so you're starting on line 5 (block 4, sector 1), then line 6, line 7, skipping line 8 (the sector trailer with the key A), and so on. For lines that aren't 32 characters long, pad them out with zeros. (This is the same as for any MIFARE tag.)


048b9f38000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
4b0b20107ccb00000000000000000000
6e66632e746f79730a0a50726576696f
75736c792c206f6e6c79204163746976
6973696f6e2068617264776172652061
edb9a575cc3500000000000000000000
6e642067616d65732068616420746865
206162696c69747920746f2072656164
2066726f6d20616e6420777269746520
cbd471261bf600000000000000000000
6461746120746f20536b796c616e6465
72204e464320746f79732e0a0a285265
6164696e67207374616e646172642066
58e29b8ff01700000000000000000000
65617475726573206c696b6520554944
2c204154514120262053414b20776173
20616c7761797320706f737369626c65
143833285e9000000000000000000000
2e290a0a4e6f772c2077697468207468
6520616c676f726974686d20666f7220
746865206b65797320412c20536b796c
870ed981b57100000000000000000000
616e646572204e464320666967757265
73206172652061626c6520746f20696e
7465726f706572617465207769746820
a1630dd262b200000000000000000000
6f746865722068617264776172652061
6e6420736f6674776172652c20616e64
2063616e206265207573656420746f20
3255e77b895300000000000000000000
73746f726520616e7920646174612061
7420616c6c2e0a0a50726576696f7573
6c792c206f6e6c792041637469766973
aae1b634d45c00000000000000000000
696f6e20686172647761726520616e64
2067616d657320686164207468652061
62696c69747920746f20726561642066
39d75c9d3fbd00000000000000000000
726f6d20616e64207772697465206461
746120746f20536b796c616e64657220
4e464320746f79732e0a0a2852656164
1fba88cee87e00000000000000000000
696e67207374616e6461726420666561
7475726573206c696b65205549442c20
4154514120262053414b207761732061
8c8c6267039f00000000000000000000
6c7761797320706f737369626c652e29
0a0a4e6f772c20776974682074686520
616c676f726974686d20666f72207468
c056cac0ad1800000000000000000000
65206b65797320412c20536b796c616e
646572204e4643206669677572657320
6172652061626c6520746f20696e7465
5360206946f900000000000000000000
726f7065726174652077697468206f74
68657220686172647761726520616e64
20736f6674776172652c20616e642063
750df43a913a00000000000000000000
616e206265207573656420746f207374
6f726520616e79206461746120617420
616c6c2e0a0a6e66632e746f79730000
e63b1e937adb00000000000000000000

You can also use the emlinsert.py Python 2 program to do the same thing automatically.

xxd -c 16 -l 720 -ps nfctoys-sky.txt > nfctoys-sky.eml
./emlinsert.py ninjini-keys.eml nfctoys-sky.eml > ninjini-new.eml

Convert the EML file to MIFARE Dump using eml2mfd.py, then write the contents of the file using the libnfc standard tool nfc-mfclassic, using the generated keys. (This is the same as for any MIFARE tag.)

./eml2mfd.py ninjini-new.eml ninjini-new.mfd
nfc-mfclassic w A u ninjini-new.mfd ninjini-keys.mfd 
NFC reader: SCM Micro / SCL3711-NFC&RW opened
Warning: tag is probably not a MFC!
Found MIFARE Classic card:
ISO/IEC 14443A (106 kbps) target:
    ATQA (SENS_RES): 0f  01  
       UID (NFCID1): 04  8b  9f  38  
      SAK (SEL_RES): 01  
Guessing size: seems to be a 1024-byte card
Writing 64 blocks |xxfailed to write trailer block 3 
x...failed to write trailer block 7 
x...failed to write trailer block 11 
x...failed to write trailer block 15 
x...failed to write trailer block 19 
x...failed to write trailer block 23 
x...failed to write trailer block 27 
x...failed to write trailer block 31 
x...failed to write trailer block 35 
x...failed to write trailer block 39 
x...failed to write trailer block 43 
x...failed to write trailer block 47 
x...failed to write trailer block 51 
x...failed to write trailer block 55 
x...failed to write trailer block 59 
x...failed to write trailer block 63 
x|
Done, 45 of 64 blocks written.

Read the data back out to verify it, using nfc-mfclassic, mfd2eml.py, sed, xxd, file, and cat. (This is the same as for any MIFARE tag.)

nfc-mfclassic r a u ninjini-verify.mfd ninjini-keys.mfd
NFC reader: SCM Micro / SCL3711-NFC&RW opened
Warning: tag is probably not a MFC!
Found MIFARE Classic card:
ISO/IEC 14443A (106 kbps) target:
    ATQA (SENS_RES): 0f  01  
       UID (NFCID1): 04  8b  9f  38  
      SAK (SEL_RES): 01  
Guessing size: seems to be a 1024-byte card
Reading out 64 blocks |................................................................|
Done, 64 of 64 blocks read.
Writing data to file: ninjini-verify.mfd ...Done.
./mfd2eml.py ninjini-verify.mfd ninjini-verify.eml
sed -n -e 5,7p -e 9,11p -e 13,15p -e 17,19p -e 21,23p -e 25,27p -e 29,31p -e 33,35p -e 37,39p -e 41,43p -e 45,47p -e 49,51p -e 53,55p -e 57,59p -e 61,63p ninjini-verify.eml | xxd -r -ps > ninjini-verify.bin
file ninjini-verify.bin
ninjini-verify.bin: ASCII English text
cat ninjini-verify.bin
nfc.toys

Previously, only Activision hardware and games had the ability to read from and write data to Skylander NFC toys.

(Reading standard features like UID, ATQA & SAK was always possible.)

Now, with the algorithm for the keys A, Skylander NFC figures are able to interoperate with other hardware and software, and can be used to store any data at all.

Previously, only Activision hardware and games had the ability to read from and write data to Skylander NFC toys.

(Reading standard features like UID, ATQA & SAK was always possible.)

Now, with the algorithm for the keys A, Skylander NFC figures are able to interoperate with other hardware and software, and can be used to store any data at all.

nfc.toys

Restore the original contents of the toy using nfc-mfclassic. (This is the same as for any MIFARE tag.)

nfc-mfclassic w A u ninjini-dump.mfd ninjini-keys.mfd 
NFC reader: SCM Micro / SCL3711-NFC&RW opened
Warning: tag is probably not a MFC!
Found MIFARE Classic card:
ISO/IEC 14443A (106 kbps) target:
    ATQA (SENS_RES): 0f  01  
       UID (NFCID1): 04  8b  9f  38  
      SAK (SEL_RES): 01  
Guessing size: seems to be a 1024-byte card
Writing 64 blocks |xxfailed to write trailer block 3 
x...failed to write trailer block 7 
x...failed to write trailer block 11 
x...failed to write trailer block 15 
x...failed to write trailer block 19 
x...failed to write trailer block 23 
x...failed to write trailer block 27 
x...failed to write trailer block 31 
x...failed to write trailer block 35 
x...failed to write trailer block 39 
x...failed to write trailer block 43 
x...failed to write trailer block 47 
x...failed to write trailer block 51 
x...failed to write trailer block 55 
x...failed to write trailer block 59 
x...failed to write trailer block 63 
x|
Done, 45 of 64 blocks written.

emlinsert.py


#!/usr/bin/python

## emlinsert.py - Insert hex data into other hex data
##
## Written in 2018 by Vitorio Miliano
##
## To the extent possible under law, the author has dedicated all
## copyright and related and neighboring rights to this software to
## the public domain worldwide.  This software is distributed without
## any warranty.
##
## You should have received a copy of the CC0 Public Domain
## Dedication along with this software.  If not, see
## <http://creativecommons.org/publicdomain/zero/1.0/>.

import os, sys

if __name__ == '__main__':
    if not len(sys.argv) > 2:
        raise ValueError('usage: {} MFDFILE DATAFILE'.format(sys.argv[0]))

    if not os.path.exists(sys.argv[1]) or not os.path.exists(sys.argv[2]):
        raise ValueError('usage: {} MFDFILE DATAFILE'.format(sys.argv[0]))

    MFDFILE = []
    with open(sys.argv[1]) as f:
        for line in f:
            MFDFILE.append(line)

    DATAFILE = []
    with open(sys.argv[2]) as f:
        for line in f:
            DATAFILE.append(line)

    blocks = [x for x in range(4, len(MFDFILE)) if (x+1) % 4]

    NEWHEX = []
    b = 0
    for idx, x in enumerate(MFDFILE):
        if idx in blocks:
            if b < len(DATAFILE):
                NEWHEX.append(DATAFILE[b].strip() + (32-len(DATAFILE[b].strip()))*'0' + '\n')
            else:
                NEWHEX.append('0'*32+'\n')
            b = b + 1
        else:
            NEWHEX.append(x)

    print ''.join(NEWHEX).strip()