Reversing Powersaves for Amiibo

Background

A while ago, we showed some nice haxx on amiibos:

While the end result is ability to craft arbitrary amiibo data, it was possible because we reversed everything about amiibo communication and data format. This involves some static keys, which makes distributing the information unattractive. Additionally, we really do not want anything to do with "piracy". Nintendo is doing poorly as it is - they don't need their current money-printing scheme to be pulled out from under them.

Enter Datel. Some weeks ago they released Powersaves for Amiibo. From initial user reports, the device functions as a barebones NFC reader/writer and communicates with a webserver at some point.

So, Datel's device sounds interesting: it doesn't allow outright piracy (from our previous reversing we know that a retail amiibo cannot have the character ID changed), and if we can reverse-engineer the PC client and/or the USB device, we might be able to re-purpose their webserver for any client and NFC frontend to use. This would allow nearly anyone to tinker with their amiibo easily, and function as a workaround for the whole issue of keys.

Amiibo crypto overview

NFC layer

Retail amiibos are using NTAG215 NFC tags. The amiibo system utilizes the password feature of this tag, blocking write access unless you are able to derive the correct password for the given tag. However, the algorithm used is trivial (really!) to discover just by sniffing a few instances of such traffic.

Application layer

The actual raw data stored on an amiibo looks like:
amiibo encrypted Top-level regions are indicated. There is a lot to digest here - refer to the NTAG215 datasheet. Some other regions are Nintendo's own crypto magic, which won't be explained here in depth. The whole point is to have Datel process this on our behalf, anyways.

The same tag, decrypted:
amiibo decrypted There's the proof for the youtube trolls :)

Target #1: PC client

It would of course be preferable to gain enough information to reuse Datel's webserver just from the PC client, which is available for free from their website. Even if that's not possible, it would still be required to investigate how to talk to their web API.

From initial observations, the PC client downloads http://settings.powersaves.net/psa/settings.xml, reads the settings/codelistUrl, and downloads that (these are later displayed in the GUI). More interestingly, it also connects to the HTTPS URL given by settings/authUrl. Looking at the binary showed they were just using a statically linked openssl, so I decided to hook the methods in order to dump the encrypted traffic. Since I already had plans to simulate the USB device, which would require non-trivial scripting, I decided to try frida. As it turns out, frida worked great (seriously WHY JAVASCRIPT?!?! though...).

SSL sniffing

After finding schannel_send, schannel_recv, and learning how to use frida, I was greeted with a pretty SSL traffic log when starting the PC client:

schannel_send{buf:541a50d, len: 4d}  
GET /api/Authorisation HTTP/1.1  
Host: psaapp.powersaves.net  
Accept: */*


schannel_recv{buf:541650c, len: 4000}  
HTTP/1.1 200 OK  
Cache-Control: no-cache  
Pragma: no-cache  
Content-Type: application/xml; charset=utf-8  
Expires: -1  
Server: Microsoft-IIS/8.5  
X-AspNet-Version: 4.0.30319  
X-Powered-By: ASP.NET  
Date: Mon, 06 Jul 2015 04:15:15 GMT  
Content-Length: 286

<VerifiyResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Datel.Powersaves.ForAmiibo.Core.Domain.Entities.Authentication"><Token>b40ab59f-e6bf  
-4722-8024-e288d4f7875d</Token><Vuid>Ixt65zWIzoscBP9tKdWL8A==</Vuid></VerifiyResponse>

It's worth noting that VerifyResponse/Vuid always base64 decodes to a 16byte value.

USB simulation

At this point, there is no other web traffic, and the application complains that it does not find the correct USB device connected. So...time to fix that with frida.

At this point, I just wanted to fake the USB device enough so that the application would continue talking with the server. The patches I made with frida were:

RVA       Name                                 New behavior  
0xe0fd0 : libusb_open_device_with_vid_pid   -> return 0xdeadbeef  
0xe14d0 : libusb_claim_interface            -> return 0  
0xe1590 : libusb_release_interface          -> nop  
0xe1310 : libusb_close                      -> nop

0xe2460 : libusb_bulk_transfer              -> simulate  
0xe2480 : libusb_interrupt_transfer         -> simulate  
Note: For simplicity, I actually intercepted do_sync_bulk_transfer,  
      which is at 0xe2310

The first try with these changes didn't work. After some debugging, I found that the application checks the USB device's descriptor.bcdDevice value. This had to be worked around with a bit of a ghetto inline patch:

var descriptor_check_addr = base.add(0x93c3);  
var skip_descriptor_check = [  
    0x59,                           // pop ecx
    0xE9, 0x19, 0x00, 0x00, 0x00    // jmp +0x1d
];
Memory.protect(descriptor_check_addr, skip_descriptor_check.length, 'rwx');  
Memory.writeByteArray(descriptor_check_addr, skip_descriptor_check);  

Now, the application recognized the faked USB device and started sending commands. I implemented a simple state machine within my do_sync_bulk_transfer intercept to feed it bogus data. Soon, I reached the point where the application could successfully read an amiibo (consisting of completely random data).
For reference, the USB traffic looks something like:

libusb_claim_interface  
-> 02 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
<- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
-> 90 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
<- 00 00 00 c6 4e 00 c4 db b0 bb e1 f9 90 ae 91 72 54 7a 9b 2a 9d  
libusb_release_interface  
libusb_claim_interface  
-> 02 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
<- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
-> 90 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
<- 00 00 71 d0 2f 87 1a 41 f4 c2 df c8 54 92 be 96 65 05 52 06 4b  
-> 80 23 1b 7a e7 35 88 ce 8b 1c 04 ff 6d 29 d5 8b f0 cd cd cd cd
<- 00 00 55 47 a8 3b 31 02 34 d7 02 c7 c0 7c 53 23 94 a6 00 00 00  
-> 11 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
<- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
-> 10 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
<- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
-> 12 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
<- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
-> 20 ff cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
-> 1c 00 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd
<- 00 00 fb 7c 15 9d 95 f7 85 40 c8 c1 ef c3 48 0d bc 56 00 00 00  
...

Failure #1 - USB device actually used in authentication

As it turns out, the application only talks to the server again when you try to apply a "cheat" to an amiibo:

schannel_send{buf:58d4865, len: 101}  
POST /api/codes HTTP/1.1  
Authorization: Basic YjQwYWI1OWYtZTZiZi00NzIyLTgwMjQtZTI4OGQ0Zjc4NzVkOlVHwqg=  
Host: psaapp.powersaves.net  
Accept: */*  
Content-Length: 1121  
Content-Type: multipart/form-data; boundary=----------------------------3068ef7d4eeb  

Note that the Authorization data is just the VerifyResponse/Token along with the UTF-8 encoded version of the USB response to command 0x80!

Next, the actual "cheat" parameters and amiibo data is sent to the server (remember, this is just random tag data):

0000000: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d  ----------------  
0000010: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 3330  --------------30  
0000020: 3638 6566 3764 3465 6562 0d0a 436f 6e74  68ef7d4eeb..Cont  
0000030: 656e 742d 4469 7370 6f73 6974 696f 6e3a  ent-Disposition:  
0000040: 2066 6f72 6d2d 6461 7461 3b20 6e61 6d65   form-data; name  
0000050: 3d22 4368 6172 6163 7465 7222 3b20 6669  ="Character"; fi  
0000060: 6c65 6e61 6d65 3d22 6368 6172 6163 7465  lename="characte  
0000070: 722e 6269 6e22 0d0a 436f 6e74 656e 742d  r.bin"..Content-  
0000080: 5479 7065 3a20 6170 706c 6963 6174 696f  Type: applicatio  
0000090: 6e2f 6f63 7465 742d 7374 7265 616d 0d0a  n/octet-stream..  
00000a0: 0d0a fb7c 159d 95f7 8540 c8c1 efc3 480d  ...|.....@....H.  
00000b0: bc56 30cd e15b 4f28 0254 e04e 2af7 13de  .V0..[O(.T.N*...  
00000c0: 74ab 66d8 8faf ab71 e344 7ae8 81f9 cc9d  t.f....q.Dz.....  
00000d0: 9638 1bf3 1771 2ced 03bf d3ab 70ee d42d  .8...q,.....p..-  
00000e0: 8ba4 c222 7233 c910 f777 a345 bb56 bc06  ..."r3...w.E.V..  
00000f0: be04 c898 8a6c cc6e cb49 07de f170 4040  .....l.n.I...p@@  
0000100: ec14 074d 5bf7 e7cd 4dfe 25bf 1944 bec9  ...M[...M.%..D..  
0000110: 8fee 08fc d1d4 2308 4a03 9a5c dbaf 43b6  ......#.J..\..C.  
0000120: dea6 bc26 7e1f 9f60 2aae 9087 c78a cae6  ...&~..`*.......  
0000130: ea40 4a2d 9247 f90f 6ad6 650c 76bb 574b  .@J-.G..j.e.v.WK  
0000140: c78a 8ab5 66d6 14c0 5045 379f d04b 6e96  ....f...PE7..Kn.  
0000150: 6a58 2daa 6c82 8fd2 1ed0 4be0 c038 1a80  jX-.l.....K..8..  
0000160: 1660 793f 2351 f4a2 4bf5 52fd 6353 15a3  .`y?#Q..K.R.cS..  
0000170: 5ffd 4d57 5e0c 70d3 2060 f8d1 5d73 810e  _.MW^.p. `..]s..  
0000180: b901 672c fb9a 4f24 a034 4be1 11e9 f900  ..g,..O$.4K.....  
0000190: 9340 a9e6 1390 91a7 746a 22df cf9e d7b1  .@......tj".....  
00001a0: fec3 857d d4c0 f9e1 d464 82db f257 2ed3  ...}.....d...W..  
00001b0: 1c29 f25d 21e1 292a d806 232d 80cb 3f62  .).]!.)*..#-..?b  
00001c0: 35da 7bd9 21ef 933e 1e06 c325 233c f0a7  5.{.!..>...%#<..  
00001d0: 10e2 da0b 7b30 c35b bc21 3e29 65c8 3242  ....{0.[.!>)e.2B  
00001e0: cc39 2ba4 e4d2 f281 9cba e6ed b4b5 06c5  .9+.............  
00001f0: d88c c8d2 af35 e36c 3d07 f2ca 38fb ec39  .....5.l=...8..9  
0000200: bfe0 6c8f 5cce 8c8a d946 60f3 a5e9 932f  ..l.\....F`..../  
0000210: 2aea cfd5 5e5e 4888 ceeb 0756 9ea9 5531  *...^^H....V..U1  
0000220: 6941 a71a 20c3 621c 0779 dfd3 8dc2 93c9  iA.. .b..y......  
0000230: 987e 459c a61c ca39 2250 b9c9 4ea7 9153  .~E....9"P..N..S  
0000240: 3409 192c 246a 7c71 cabd 6496 db57 2cc6  4..,$j|q..d..W,.  
0000250: 39f2 9fe4 640e 0137 11c8 9c99 2cde 4e0a  9...d..7....,.N.  
0000260: 5cb9 c01f 0f66 e481 309f 4e2b 4090 1ddb  \....f..0.N+@...  
0000270: 02af d2b9 be46 3736 78e7 8b8e 581d 4638  .....F76x...X.F8  
0000280: 758c d265 437c e89f c9a9 f10c c95c 9ba6  u..eC|.......\..  
0000290: 2aca ca65 1623 64d2 a099 6027 04cb 16cb  *..e.#d...`'....  
00002a0: 3343 5d91 6427 8c43 b350 d8bd 9888 b530  3C].d'.C.P.....0  
00002b0: 3016 3f63 f04e 8e0f ec02 e009 048b 0d0a  0.?c.N..........  
00002c0: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d  ----------------  
00002d0: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 3330  --------------30  
00002e0: 3638 6566 3764 3465 6562 0d0a 436f 6e74  68ef7d4eeb..Cont  
00002f0: 656e 742d 4469 7370 6f73 6974 696f 6e3a  ent-Disposition:  
0000300: 2066 6f72 6d2d 6461 7461 3b20 6e61 6d65   form-data; name  
0000310: 3d22 5061 796c 6f61 6422 3b20 6669 6c65  ="Payload"; file  
0000320: 6e61 6d65 3d22 7061 796c 6f61 642e 6269  name="payload.bi  
0000330: 6e22 0d0a 436f 6e74 656e 742d 5479 7065  n"..Content-Type  
0000340: 3a20 6170 706c 6963 6174 696f 6e2f 6f63  : application/oc  
0000350: 7465 742d 7374 7265 616d 0d0a 0d0a 00c8  tet-stream......  
0000360: 0000 0000 0d0a 2d2d 2d2d 2d2d 2d2d 2d2d  ......----------  
0000370: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d  ----------------  
0000380: 2d2d 2d2d 3330 3638 6566 3764 3465 6562  ----3068ef7d4eeb  
0000390: 0d0a 436f 6e74 656e 742d 4469 7370 6f73  ..Content-Dispos  
00003a0: 6974 696f 6e3a 2066 6f72 6d2d 6461 7461  ition: form-data  
00003b0: 3b20 6e61 6d65 3d22 4164 6472 6573 7322  ; name="Address"  
00003c0: 0d0a 0d0a 3078 6563 0d0a 2d2d 2d2d 2d2d  ....0xec..------  
00003d0: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d  ----------------  
00003e0: 2d2d 2d2d 2d2d 2d2d 3330 3638 6566 3764  --------3068ef7d  
00003f0: 3465 6562 0d0a 436f 6e74 656e 742d 4469  4eeb..Content-Di  
0000400: 7370 6f73 6974 696f 6e3a 2066 6f72 6d2d  sposition: form-  
0000410: 6461 7461 3b20 6e61 6d65 3d22 5061 796c  data; name="Payl  
0000420: 6f61 644c 656e 6774 6822 0d0a 0d0a 3078  oadLength"....0x  
0000430: 360d 0a2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d  6..-------------  
0000440: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d  ----------------  
0000450: 2d33 3036 3865 6637 6434 6565 622d 2d0d  -3068ef7d4eeb--.  
0000460: 0a  

...and sadly, but not unexpectedly, we only get back:

schannel_recv{buf:58d0864, len: 4000}  
HTTP/1.1 401 Unauthorized  
Cache-Control: no-cache  
Pragma: no-cache  
Content-Length: 146  
Content-Type: text/plain; charset=utf-8  
Expires: -1  
Server: Microsoft-IIS/8.5  
X-AspNet-Version: 4.0.30319  
X-Powered-By: ASP.NET  
Date: Mon, 06 Jul 2015 04:15:36 GMT

<AuthenticationResponse><Message><AlertBox>Authentication Failed</AlertBox><VisitUrl></VisitUrl></AuthenticationResponse></AuthenticationResponse>  

So, I was out of luck - had to actually buy the USB device to poke around at it.
However, I learned how the web API works, and which USB command is actually used for authentication.

Target #2: USB Device

The first step after buying something is to take it apart:
usb top usb bottom So, it is pretty straightforward to identify the STM32 SWD pins. I was beyond excited to find out that the interface was still enabled, and I could simply dump the program out of the device. Could it have been that easy?!?!

Failure #2: The princess is in another castle

Observe the entirety of the handler for USB command 0x80:

int usb_cmd_80_calc_magic(uint8_t *output, uint8_t *input)  
{
  memcpy(p_i2c_xfer_buf, input, 0x10);
  if (i2c_send_cmd(0x82u, 0x10) < 0)
  {
    return -1;
  }
  delay(10000);
  if (i2c_is_len_available(0x10) < 0) || i2c_recv_buf(0x10) < 0)
  {
    return -1;
  }
  else
  {
    memcpy(output, p_i2c_xfer_buf, 16);
    return 0;
  }
}

As shown, the handler just forwards this on the STM32's I2C1 interface, using a simple protocol. Looking back at the topside picture, this corresponds to the small, mysteriously unmarked, device labelled U2.

Target #3: Magical mystery device

Taking another look at the device at U2, it is actually quite strange:

  • The package is QFN12 (weird!).
  • It has no clock input.
  • It does not appear to react to fiddling with the 3 lines which are routed to the test points.
  • After sending some command, it seems to increase background noise levels on it's external signals until it is reset (but still works fine). Either it was in a low power state until the first command, or it is attempting some anti-DPA magic. Seems scary.

A simple pinout of the mystery device (with idle states of the unknown pins) looks like:

Test Point Group:  
[1] [2] [3]
  [4] [5]
1: VSS  
5: VDD

U2:  
1   I2C SCL  
2   VSS  
3   ? (high)        test point 3  
4   ? (low)  
5   ? (high)        test point 2  
6   ? (low)  
7   ? (high)        test point 4  
8   VDD  
9   ? (high)  
10  ? (high)  
11  ? (high)  
12  I2C SDA  

Failure #3: Mystery device remains mysterious

I can guess that the device is probably just doing AES in response to USB command 0x80. I also know that it handles at least 9 other commands used by the STM32 firmware.

However, I currently have no ideas about how to actually (with tools I have) obtain the authentication key from the device.

If anyone knows more about the device or can help attack it, please contact me.

Conclusion

Overall it was a fun experiment, and an excuse to use frida. It was determined that the powersaves device is actually using some dedicated security core to protect secrets. However, while the the scheme they have used should protect against clone devices, it does not protect against other scenarios...

The frida-related code can be found here.