20120426

Introducing MungeTLS. Also, Debugging Encryption

Here I am, continuing my ongoing saga of cryptography related coding on Windows. In the last couple blogs I got symmetric encryption and public key encryption working in a kind of minimal sample code type of way. This is all well and good, but it came time to integrate it into the larger program.

Wait, what do you mean you don't know what the "larger program" is? Haven't I told you what this project actually is? ... I haven't!? Man, I'm a real jerk sometimes. Well, might as well get this over with.

The project is called MungeTLS. It is planned to be a relatively minimal implementation of TLS. This protocol, formerly known as SSL and marked by the "https" URL scheme, is absolutely foundational in almost everything you do on the internet today. It uses a lot of other protocols and technologies to allow things like buying stuff online, because it safely encrypts data sent over the Internet.

Why is MungeTLS going to be "minimal"? Well, because TLS has a lot of aspects to it, and a number of extensions and plugins. Also, 90% of the TLS traffic in the world probably doesn't use most of them. I'm mostly concerned with things that are commonly used. For example, I'm not sure if I'm going to bother with client authentication (as opposed to server authentication, which most assuredly is important).

But the main feature of MungeTLS is that it will have a very tightly integrated plugin system, where the caller will get callbacks through every stage of the TLS connection. I mean to allow the caller to mess with data at any part of the process from initiating the connection to the handshake to every message sent and received. The simple rationale for this is that I don't believe something like this exists out there. In my line of work, I've been tasked with testing two or even three different SSL implementations, and never had the time to write this sort of test tool to really screw with the protocol in deep and interesting ways. This project aims to fill this void.

Since I've only needed to test client side TLS implementations so far, I'll be targeting writing the server-side TLS code for now. My plan is to write a functioning web server that can interact against OpenSSL and SChannel (i.e. the TLS implementation used by most Microsoft products, notably Internet Explorer). Once I have that working, I'll be adding in the plugin model.

As far as current progress, I'm mid-way through the TLS handshake. I've just decrypted ClientKeyExchange message, which is the only message involving public key cryptography. That's what this blog post is about.

Public Key Craziness

So yeah, going back to the intro paragraph, I was ready to integrate my sample public key cryptography code into the rest of the program. I diligently copied over functions with names like EncryptBuffer, and set up some levels of abstraction that might allow me to be crypto library-agnostic (WindowsPublicKeyCipherer derives from PublicKeyCipherer).

I integrated it into the regular message handling and set it up to decrypt the payload of the exchange_keys field of the ClientKeyExchange message. Everything was looking good.

Until it wasn't.

I got varying error messages from CryptDecrypt: things like NTE_BAD_LEN, and ERROR_OUT_OF_MEMORY. None of these made any sense to me, because I was sure my buffer allocation sizes were correct, and that I was pulling out the correct part of the message from the network to pass to decryption.

I started passing CRYPT_DECRYPT_RSA_NO_PADDING_CHECK to CryptDecrypt in the hopes that I might see some slightly malformed data that I could fix up... I don't even know how. That yielded just blobs of bytes with no pattern. This phenomenon was happening with both OpenSSL and IE, so I had to be doing something pretty wrong.

Encryption in OpenSSL

I needed to see what was being encrypted, and into what data was being encrypted and what it turned into on the wire. I couldn't figure out how to get this on Windows, because they have layers and layers of internals, and I doubt I'd find any code calling functions I was familiar with, like CryptEncrypt. Instead, I dumped the DLL exports of libeay32.dll (a central DLL from OpenSSL) and looked for anything useful.

>dumpbin /exports \programs\openssl\bin\libeay32.dll | findstr /i rsa
...
        490  895 0002F770 RSA_private_decrypt
        491  896 0002F740 RSA_private_encrypt
        492  897 0002F7A0 RSA_public_decrypt
        493  898 0002F710 RSA_public_encrypt
...

So I set a breakpoint on RSA_public_encrypt and saw it get hit in my OpenSSL test app. I had to guess at the parameters, to see if I could get the cleartext and ciphertext, but it wasn't too bad. Here, might as well show it. I don't show enough debugging on this blog. Debugging without symbols can be especially interesting.

Also, a link to my Windbg Quick Reference, to decipher some of the commands.

2:003> x libeay32!*RSA_public_encrypt*
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for f:\programs\openssl\bin\LIBEAY32.dll -
00000000`0046f710 LIBEAY32!RSA_public_encrypt (<no parameter info>)

2:003> bu LIBEAY32!RSA_public_encrypt
2:003> bl
 0 e 00000000`0046f710     0001 (0001)  2:**** LIBEAY32!RSA_public_encrypt

Okay, so we've found the function and set a breakpoint on it. Then we hit g to continue execution to see if it gets hit.

2:003> g
Breakpoint 0 hit
LIBEAY32!RSA_public_encrypt:
00000000`0046f710 b838000000      mov     eax,38h

2:003> r
rax=0000000000000001 rbx=0000000001d3a1e0 rcx=0000000000000030
rdx=000000000013ef58 rsi=0000000001d26af0 rdi=0000000001d27ea4
rip=000000000046f710 rsp=000000000013eef8 rbp=0000000001d27ea6
 r8=0000000001d27ea6  r9=0000000001d3a1e0 r10=0000000000000010
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000001d27ea0
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
LIBEAY32!RSA_public_encrypt:
00000000`0046f710 b838000000      mov     eax,38h

From learning the (much simpler than x86) calling convention for x64, I know that the first four arguments to a function are passed in registers rcx, rdx, r8, and r9, respectively. Remaining arguments are pushed on the stack. Maybe one of these four arguments could have the cleartext I'm looking for.

Speaking of the cleartext, I actually have an idea of what it ought to look like. The only part of TLS that involves public key encryption is the "client key exchange", in which the client generates a random number, encrypts it with the server's public key (which it knows, since it's public), and sends it to the server, which can decrypt it with the private key. This set of random bytes is called the "premaster secret".

I actually see that rcx has a value of 0x30, that is, 48. This is exactly the right size of the premaster secret (taken from RFC 5246):

struct {
    ProtocolVersion client_version;
    opaque random[46];
} PreMasterSecret;

client_version
   The latest (newest) version supported by the client.  This is
   used to detect version rollback attacks.

random
   46 securely-generated random bytes.

struct {
    public-key-encrypted PreMasterSecret pre_master_secret;
} EncryptedPreMasterSecret;

pre_master_secret
   This random value is generated by the client and is used to
   generate the master secret, as specified in Section 8.1.

That's 2 bytes for the protocol version, followed by 46 bytes of random data. Time to examine the arguments.

2:003> db @rdx L@rcx
00000000`0013ef58  03 01 17 9f c6 23 5e 48-8a 2c 0b 82 c6 28 1f 29  .....#^H.,...(.)
00000000`0013ef68  19 54 59 70 76 96 7b 45-ab 82 25 ec 0c c1 8d dd  .TYpv.{E..%.....
00000000`0013ef78  60 27 c1 e1 8e be 4f 1b-af 7e da 58 24 ac 84 f9  `'....O..~.X$...

Oh hey, look at that! 0x0301 is the protocol version identifier for TLS 1.0. I'm pretty sure this second argument contains the cleartext. That wasn't too bad. One of the other arguments might contain, on completion, the ciphertxt. Let's look before and after.

2:003> db @r8
00000000`01d27ea6  03 00 02 00 30 82 01 fc-30 82 01 69 a0 03 02 01  ....0...0..i....
00000000`01d27eb6  02 02 10 ab 4f 74 b3 a1-7b 3b 85 41 73 e4 51 1a  ....Ot..{;.As.Q.
00000000`01d27ec6  9f 72 75 30 09 06 05 2b-0e 03 02 1d 05 00 30 14  .ru0...+......0.
00000000`01d27ed6  31 12 30 10 06 03 55 04-03 13 09 6d 74 6c 73 2d  1.0...U....mtls-
00000000`01d27ee6  74 65 73 74 30 1e 17 0d-31 30 30 34 30 31 30 37  test0...10040107
00000000`01d27ef6  30 30 30 30 5a 17 0d 33-39 31 32 33 31 32 33 35  0000Z..391231235
00000000`01d27f06  39 35 39 5a 30 14 31 12-30 10 06 03 55 04 03 13  959Z0.1.0...U...
00000000`01d27f16  09 6d 74 6c 73 2d 74 65-73 74 30 81 9f 30 0d 06  .mtls-test0..0..
2:003> db @r9
00000000`01d3a1e0  00 00 00 00 00 00 00 00-20 42 56 00 00 00 00 00  ........ BV.....
00000000`01d3a1f0  00 00 00 00 00 00 00 00-f0 a3 d3 01 00 00 00 00  ................
00000000`01d3a200  00 a5 d3 01 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`01d3a210  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`01d3a220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`01d3a230  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`01d3a240  0d f0 ad ba 0d f0 ad ba-01 00 00 00 06 00 00 00  ................
00000000`01d3a250  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

2:003> gu
SSLEAY32!SSLv3_server_method+0x24b6:
00000001`8000c8c6 0fbaa69c0100001b bt      dword ptr [rsi+19Ch],1Bh ds:00000000`01d26c8c=00040004

2:003> db 00000000`01d27ea6
00000000`01d27ea6  49 e6 b6 a6 ef f6 dc ce-b6 ac 49 bc 57 57 b3 24  I.........I.WW.$
00000000`01d27eb6  0b 76 65 8b e9 53 35 2c-f8 68 72 86 bd b4 66 44  .ve..S5,.hr...fD
00000000`01d27ec6  53 7a fe 20 a4 02 dd 1d-61 1d cf c1 0b 36 50 0e  Sz. ....a....6P.
00000000`01d27ed6  d0 7b 05 0b 12 b5 66 33-a5 45 c2 6e 50 9a dd f5  .{....f3.E.nP...
00000000`01d27ee6  2f 3c a4 dc c0 34 fa 98-3f ef 71 bd f0 33 93 81  /<...4..?.q..3..
00000000`01d27ef6  9d 82 8e bc 70 40 91 5a-ba ca ba ae 21 da d3 b4  ....p@.Z....!...
00000000`01d27f06  a8 8d 6f f4 15 39 48 19-74 51 1f d1 be ab ab 51  ..o..9H.tQ.....Q
00000000`01d27f16  7d bc 15 0b 83 e8 5f 16-4c d6 46 b3 93 5b 71 5f  }....._.L.F..[q_
2:003> db 00000000`01d3a1e0
00000000`01d3a1e0  00 00 00 00 00 00 00 00-20 42 56 00 00 00 00 00  ........ BV.....
00000000`01d3a1f0  00 00 00 00 00 00 00 00-f0 a3 d3 01 00 00 00 00  ................
00000000`01d3a200  00 a5 d3 01 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`01d3a210  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`01d3a220  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`01d3a230  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`01d3a240  0d f0 ad ba 0d f0 ad ba-01 00 00 00 06 00 00 00  ................
00000000`01d3a250  c0 a9 d3 01 00 00 00 00-00 00 00 00 00 00 00 00  ................

Looks like r8 gets totally overwritten when the function completes, whereas r9 is untouched. It's probably safe to say that r8 is the ciphertext. In fact, I did confirm this is true by looking at the traffic sent to the server.

As they say, RTFM

So after all of this, what do I end up with? I know that the cleartext beforehand is correct. I know the ciphertext is reaching the server correctly. I know it's not an OpenSSL specific problem. God, what could be going wrong? Feeling quite deflated after that rush of debugging, I went back to look at the docs.

Wait, what? Let's look at that a bit closer.

Seriously? I'm leaning closer to the screen now.

Squinting furiously at the screen at this point.

Mixture of elation, rage, and aggressive sighing ensues.

I should have known something like this would come up. In fact, when I was manually implementing my padding check, I was finding that the data was "backwards", but I didn't think much of it at the time. Now it makes perfect sense. So I inserted the appropriate array reversals at the right places, and everything magically started working. Infuriating.

So what did we learn? Little endians can be a big pain. I'm sure I'm going to have all kinds of problems getting the next stage of encryption working, too. I'll be sure to tell you all about it.

0 comments:

Post a Comment