HOME BLOG ARCHIVE TAGS

The Curious Case of the Security Token

November 05, 2013

A long time ago, I was called to help integrating a security/crypto token in a late project.

Every time the device was loaded by a major network vendor VPN software, authentication failed with nonsensical errors. If the token was used elsewhere, digital signatures were correctly generated.

After reverse engineering the hardware CSP, I found the culprit: RSA padding done before signing was corrupted (an unfortunate coincidence, triggered by a buggy SEH handler). The VPN gateway, then, wasn’t able to verify the client credentials, and negotiate a valid connection/session.

I still have an incorrect pkcs1 v1.5 block dump from the CSP (it didn’t use the better OAEP). In CAPI little endian format:

00dc4cc0 6f 23 e5 ab 7d 95 4d 53 o\#..}.MS
00dc4cc8 16 8e ce 83 bc 34 51 90 .....4Q.
00dc4cd0 8c 6e 91 54 14 04 00 05 .n.T....  ;; D == sha1 message digest + ASN.1 BER SHA1 prefix
00dc4cd8 1a 02 03 0e 2b 05 06 09....+...
00dc4ce0 30 21 30 3c 90 bf de 12 0!0.....  ;; supposed to be 00 ff ff ff ff  ff ff ff ff
00dc4ce8 80 90 fe cc cc 00 34 c4 ........  ;; ff ff ff ff  ff ff ff ff  ff ff ff ff
00dc4cf0 66 41 a7 03 aa 59 77 62 ........
00dc4cf8 ff ff ff ff ff ff ff ff ........
00dc4d00 ff ff ff ff ff ff ff ff ........
00dc4d08 ff ff ff ff ff ff ff ff ........
00dc4d10 ff ff ff ff ff ff ff ff ........
00dc4d18 ff ff ff ff ff ff ff ff ........
00dc4d20 ff ff ff ff ff ff ff ff ........
00dc4d28 ff ff ff ff ff ff ff ff ........
00dc4d30 ff ff ff ff ff ff ff ff ........  ;; EB = 00 || BT || PS || 00 || D
00dc4d38 ff ff ff ff ff ff 01 00 ........  ;; block type "01", with "private-key padding"

The issue was reported, but the manufacturer refused to apply a fix remotely. Sending the devices back wasn’t a matter of choice: shipping more than a thousand tokens could take as much as 3 months (a very bad scenario, for a very late project).

As I always joke, when everything else fails, apply a patch. I was lucky that the corruption did not touch the message digest region. For the pkcs1 block type in question, the affected area was known in advance, and could be restored. I patched a core token dll, to recover the block before signing it:

.1000A57F: 50              push eax
.1000A580: 8B44240C        mov eax,[esp][0C]
.1000A584: C6401400        mov b,[eax][14],0
.1000A588: C74015FFFFFFFF  mov d,[eax][15],-1
.1000A58F: C74019FFFFFFFF  mov d,[eax][19],-1
.1000A596: C7401DFFFFFFFF  mov d,[eax][1D],-1
.1000A59D: C74021FFFFFFFF  mov d,[eax][21],-1
.1000A5A4: C74025FFFFFFFF  mov d,[eax][25],-1
.1000A5AB: 90 nop
.1000A5AC: 90 nop
.1000A5AD: 90 nop
.1000A5AE: 90 nop
.1000A5AF: 90 nop
.1000A5B0: 90 nop
.1000A5B1: 90 nop
.1000A5B2: 58 pop eax
.1000A5B3: 90 nop
.1000A5B4: 83EC0C sub esp,00000000C ;'?'

There was just one more catch: the device had a functionality to aid in system deployment. Every time it was USB plugged (if allowed by user/policy), a setup installed the vendor runtime (fetched from a read-only section of the token itself). Including the dll I’ve changed!

To overcome this cat-and-mouse game, an old trick from the trenches helped: as the token components were installed in a best-effort basis, I developed a protection agent, that started with Windows. It opened and locked the patched dll (CreateFile(), FILE_SHARE_READ, OPEN_EXISTING; LockFile(), all file size range). This avoided the setup overwrite.

Sometimes, the runtime was restored with some nice system techniques. As a countermeasure, every once in a while, the agent detected this (using RegNotifyChangeKeyValue()), and replaced the original [re]installed file with my patched one. It worked amazingly well.

Later in that year, the project failed for other reasons, and was foreclosed. Lessons learned? I still don’t know… but it was a very cool workaround, indeed.