CodeGate 2010 – Challenge 8: Bit-flipping attack on CBC mode

March 23, 2010 by vnsec · Leave a Comment 

Writeup for CodeGate 2010 Challenge 8 by namnx


This is a web-based cryptography challenge. In this challenge, we were provided a URL and a hint ”the first part is just an IV“.

The URL is: http://ctf1.codegate.org/99b5f49189e5a688492f13b418474e7e/web4.php.

Analysis

Go to the challenge URL. It will ask you the username for the first time. After we enter a value, for example ‘namnx‘, it will return only a single message “Hello, namnx!“. Examine the HTTP payload, we will see the cookie returned:

web4_auth=1vf2EJ15hKzkIxqB27w0AA==|5X5A0e3r48gXhUXZHEKBa5dpC+XfdVv4oamlriyi5yM=

The cookie includes 2 parts delimited by character ‘|’. After base64 decode the first part of the cookie, we have a 16-byte value. According to the hint, this is the IV of the cipher. And because it has 16-byte length, I guess that this challenge used AES cipher, and the block size is 16 bytes. Moreover, the cipher has an IV, so it can’t be in ECB mode. I guessed it in CBC mode. The last part is the base64 of a 32-byte value. This is a cipher text. We will exploit this value later.

Browse the URL again, we will receive another message: “Welcome back, namnx! Your role is: user. You need admin role.” Take a look into this message, we can guess the operation of this app: it will receive the cookie from the client, decrypt it to get the user and role information and return the message to the client based on the user and role information. So, in order to get further information, we must have the admin role. This is our goal in this challenge.

Exploit

I wrote some Python to work on this challenge easier:

    import urllib, urllib2
    import base64, re

    url = 'http://ctf1.codegate.org/99b5f49189e5a688492f13b418474e7e/web4.php'
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'

    def get_cookie(user):
        headers = { 'User-Agent' : user_agent}
        values = {'username' : user, 'submit' : "Submit"}
        data = urllib.urlencode(values)
        request = urllib2.Request(url, data, headers)
        response = urllib2.urlopen(request)
        cookie = response.info().get('Set-Cookie')
        groups = re.match("web4_auth=(.+)\|(.+);.+", cookie).groups()
        iv = base64.b64decode(groups[0])
        cipher = base64.b64decode(groups[1])
        return iv, cipher

    def get_message(iv, cipher):
        cookie = base64.b64encode(iv) + '|' + base64.b64encode(cipher)
        cookie = urllib.quote(cookie)
        cookie = 'web4_auth=' + cookie
        headers = { 'User-Agent' : user_agent, 'Cookie': cookie}
        request = urllib2.Request(url, None, headers)
        response = urllib2.urlopen(request)
        data = response.read()
        print repr(data)
        groups = re.match(".+, (.*)! .+: (.*)\. You.+", data).groups()
        return groups[0], groups[1]

The first function, get_cookie will submit a value as a username in the first visit to the page, get the returned cookie, and then parse it to get the IV and cipher. The second function, get_message, do the task like when you visit the page in later times, it parses the response message to get the returned username and role.

    >>> iv, cipher = get_cookie('123456789012')
    >>> len(cipher)
    32
    >>> iv, cipher = get_cookie('1234567890123')
    >>> len(cipher)
    48

When you input the user with a 12-byte value, the returned cipher will have 32 bytes (2 blocks). And when you enter a 13-byte value, the cipher will have 48 bytes (3 blocks). This means that beside the username value, the plain text of the cipher will be added more 20 bytes.

Try altering the cipher text to see how it is decrypted:

    >>> iv, cipher = get_cookie('1234567890')
    >>> cipher1 = cipher[:-1] + '\00'
    >>> username, role = get_message(iv, cipher1)
    'Welcome back, 1234567\xa2\xc2\xca\xfei\xdb\xee_c\xa7\xd7\x0c\xa9j\xe0\xbb! Your role is: . You need admin role.'

As you can see, the last block of the decrypted role is the first block of the plain text. So, the format of the plain text may be: ‘username=’ + username + [11 bytes].

To here, we can guess that the format of the plain text can be something like:

‘username=’ + username + [delimiter] + [param] + ‘=’ + [value]

The last 11 bytes of the plain text can be determined by the code below:

    >>> iv, cipher = get_cookie('\x00')
    >>> username, role = get_message(iv, cipher)
    'Welcome back, \x00##role=user\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00! Your role is: . You need admin role.'

You can see the last 11 bytes of the plain text in the returned message. So, at this time, we can conclude format of the plain text is:

‘username=’ + username + ‘##role=’ + role

Now, the last thing we have to do is altering the role value to ‘admin’. Because we’ve already known the format of the plain text, we can choose to input the username close to the target plain text and try to alter the cipher text in the way that the decrypted value is what we want.

Let remind the operation of CBC mode in cryptographic ciphers. In encryption process:

y[1] = C(IV xor x[1])
y[n] = C(y[n-1] xor x[n])

and in the decryption:

x[1] = D(y[1]) xor IV
x[n] = D(y[n]) xor y[n-1]

Notice that if we flip one bit in the (n-1)th block of cipher text, the respective bit in the n-th block of plain text will be also flipped. So, we will you this fact to exploit the challenge:

    >>> iv, cipher = get_cookie('012345678901234567890123#role=admin')
    >>> s = cipher[:16] + chr(ord(cipher[16]) ^ 0x10) + cipher[17:]
    >>> username, role = get_message(iv, s)
    'Welcome back, 0123456L\xaa\x17m\xe9\x91\xdc\xe2`#z)\xd8m\xd8\x18! Your role is: admin. You need admin role. Congratulations! Here is your flag: the_magic_words_are_squeamish_ossifrage_^-^!!!!!'

Successful! Such an interesting challenge, isn’t it?

References

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

CodeGate 2010 – Challenge 7: Weak SSL Cracking

March 23, 2010 by vnsec · Leave a Comment 

Writeup for CodeGate 2010 – Challenge 7 by namnx


Last weekend, I had a great hacking time with team CLGT in the CodeGate 2010 CTF Preliminary Round. It lasted 36 consecutive hours from 7:00AM March 13 to 7:00PM March 14. There were a lot of teams around the world participating in this hacking contest. And excellently, CLGT proved it as one of the best teams when got the 2nd place in this round. See final ranking.
This entry is my writeup for challenge 7. I think this is an interesting challenge from which you can learn more deeply about SSL protocol and public key cryptography. In this challenge, we were provided a tcpdump file of a SSL traffic and a hint “does the modulus look familiar?“. So our goal is to analyze and decrypt this captured traffic to get the flag.

Analysis
Firstly, I used Wireshark to load this file and start to analyze it:

There are 26 packets captured. Packet #4 is a SSL Client Hello packet, but after it, packet #8 and packet #9 have FIN flag. This mean that the session was termininated. So we just ignore them.
Packet #14 is another SSL Client Hello packet. This is where the real session began. Take a look into it:
There is nothing special. It is just a normal SSL Client Hello packet. It happens when a client want to connect to a SSL service. We continue look into the packet #16, the SSL Server Hello packet:
This is the response for SSL Client Hello packet. We can see some useful information here:
- The cipher suite will be used: RSA_WITH_AES_256_CBC_SHA
- The X509 certificate of the server
In the SSL protocol, the server send its certificate to the client in the handshaking process. This certificate will be used for supporting the key exchange afterward. The certificate contains the server’s public key and other data. By extracting the public key and recovering the private key from it, we can decrypt the SSL traffic.
Exploit
I wrote some Python code to exploit this challange:
    from scapy.all import *
    from M2Crypto import X509

    def decode_serverhello(packet):
    payload = packet.load
    cert = payload[94:1141]
    cert = X509.load_cert_string(cert, 0)
    return cert

    def get_pubkey(cert):
    pubkey = cert.get_pubkey().get_rsa()
    n = long(pubkey.n.encode('hex')[8:], 16)
    e = long(pubkey.e.encode('hex')[9:], 16)
    return n, e

    packets = rdpcap('ssl.pcap')
    cert = decode_serverhello(packets[15])
    n,e = get_pubkey(cert)

Because this traffic used RSA as public key algorithm, the public key contains 2 components: n and e. We get their values from the above code:

    n = 1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413
    e = 65537
In RSA, n is the product of 2 big prime numbers p and q. So, in order to recover the RSA private key from the public key, we must factorize n into p and q. This is the key point of the challenge. In this situation, n is a very big number (232 decimal digits). How can we do that? In the beginning, I didn’t know how to solve it. But I remembered the hint “does the modulus look familiar?“. So I tried googling it :-D (actually just its last digits). And… oh my god, I was lucky! It is RSA-768. It’s factorized just few months ago.
    RSA-768 = 33478071698956898786044169848212690817704794983713768568912431388982883793878002287614711652531743087737814467999489
    × 36746043666799590428244633799627952632279158164343087642676032283815739666511279233373417143396810270092798736308917

So now, we have all components of the RSA keys.

    n = 1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413
    e = 65537
    p = 33478071698956898786044169848212690817704794983713768568912431388982883793878002287614711652531743087737814467999489
    q = 36746043666799590428244633799627952632279158164343087642676032283815739666511279233373417143396810270092798736308917
    d = 703813872109751212728960868893055483396831478279095442779477323396386489876250832944220079595968592852532432488202250497425262918616760886811596907743384527001944888359578241816763079495533278518938372814827410628647251148091159553
The last thing we have to do is generating the RSA private key in PEM format from these components. But how can we do that? As far as I know, popular cryptographic libraries like OpenSSL do not support this. So in this case, I wrote my own tool to do this task. It is based on ASN1. It is a little long to post here. But if you want to write your own one, I recommend pyasn1.

After having the private key, just import it to Wireshark to decrypt the SSL traffic:

References
- SSL/TLS: http://en.wikipedia.org/wiki/Transport_Layer_Security
- RSA: http://en.wikipedia.org/wiki/RSA

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

CodeGate 2010 Challenge 2 – Xbox pwned

March 19, 2010 by RD · 19 Comments 

Summary

This is the most interesting challenge in CodeGate 2010 IMHO. The binary is a VM which loads the ‘codefile’ and execute it. The VM codefile is protected from being tampered with a TEA based hash algorithm. By exploiting the weakness of hash algorithm (similar to Xbox hack) together with a bug inside VM, we could change the execution flow of VM code to get back the secret key content.

Analysis

Challenge information

credentials: ssh hugh@ctf4.codegate.org -p 9474 password=takeitaway
Exploit /home/hugh/yboy to read secret.key

There are yboy, codefile and secret.key files in the home directory of hugh (you can download these files here if you want to try it by yourself)

-rw-r–r– 1 codegate codegate 1136 2010-03-12 14:45 codefile
-r——– 1 daryl daryl 140 2010-03-12 15:27 secret.key
-rwsr-xr-x 1 daryl root 22307 2010-03-12 16:07 yboy

a. Reverse Engineering yboy

yboy basically does the following things

  • load VM codes from the codefile into memory (code[])
  • load content of secret.key into memory (data[])
  • check for the integrity of codefile using TEA based hash algorithm against a hard-coded hash value. Exit if the hash not matched
  • parse/decode loaded VM codes and execute it accordingly

For the VM code inside codefile

  • ask user to input password
  • compare the input with flag inside secret.key
  • if correct, print out the flag
  • otherwise, print out access denied error and exit

b. Decompiler for codefile

Since yboy load VM code from codefile and execute it, I wrote a decompiler for it

#include <stdio.h>
#include <stdlib.h>

unsigned char *decode[32] = {
        "halt", "push", "pop", "add", "sub", "or", "xor", "nor", "shl",
        "shr", "not", "nop", "branch", "jumpreg", "callreg", "load",
        "store", "halt", "inputchar", "outputchar", "set_imm", "reload",
        "rrandom", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop",
        "nop",
};

unsigned long registers[64];
unsigned long code[32768];
unsigned int PC;

int main(int argc, char **argv)
{
        unsigned int ins;
        unsigned char reg;
        unsigned char imm1, imm2;
        unsigned char opcode;

        unsigned int codesize;
        int set_imm = 1;
        FILE *f;

        f = fopen(argv[1], "r");
        codesize = fread(code, 1, sizeof(code), f);
        fclose(f);
        //check_code();

        PC = 0;
        while (PC < (codesize) / 4) {
                set_imm = 1;
                ins = code[PC];

                opcode = (ins >> 24);   //& 0x1f;
                reg = (ins >> 16) & 0xFF;
                imm1 = (ins >> 8) & 0xFF;
                imm2 = (unsigned char) ins;

                // set_imm
                if (opcode == 20) {
                        printf("%04x: \tr%d = %s %x, %x", PC, reg,
                            decode[opcode & 0x1f], imm1, imm2);

                        if (imm2 && !imm1)
                                printf("\t; %c", imm2);
                        else if (imm1)
                                printf("\t; %04x", imm2 + (imm1 << 8));

                        printf("\n");
                        PC++;
                        continue;
                }

                reg = (ins >> 16) & 0xBF;
                imm1 = (ins >> 8) & 0xBF;
                imm2 = ins & 0xBF;

                printf("%04x: \tr%d = %s r%d, r%d", PC, reg,
                    decode[opcode & 0x1f], imm1, imm2);

                if (opcode == 12)       // comment for branch
                        printf("\t; if (r%d) goto r%d\n", imm1, imm2);
                else
                        printf("\n");

                PC++;
        }
        return 0;
}

Here is the output of the decompiler (click to open)

rd@jps(~/working/ctf/codegate2010/2/)$ ./yboy-decompile codefile
0000:   r1 = set_imm 0, 45      ; E
0001:   r0 = outputchar r1, r0
0002:   r1 = set_imm 0, 6e      ; n
0003:   r0 = outputchar r1, r0
0004:   r1 = set_imm 0, 74      ; t
0005:   r0 = outputchar r1, r0
0006:   r1 = set_imm 0, 65      ; e
0007:   r0 = outputchar r1, r0
0008:   r1 = set_imm 0, 72      ; r
0009:   r0 = outputchar r1, r0
000a:   r1 = set_imm 0, 20      ;
000b:   r0 = outputchar r1, r0
000c:   r1 = set_imm 0, 70      ; p
000d:   r0 = outputchar r1, r0
000e:   r1 = set_imm 0, 61      ; a
000f:   r0 = outputchar r1, r0
0010:   r1 = set_imm 0, 73      ; s
0011:   r0 = outputchar r1, r0
0012:   r1 = set_imm 0, 73      ; s
0013:   r0 = outputchar r1, r0
0014:   r1 = set_imm 0, 77      ; w
0015:   r0 = outputchar r1, r0
0016:   r1 = set_imm 0, 6f      ; o
0017:   r0 = outputchar r1, r0
0018:   r1 = set_imm 0, 72      ; r
0019:   r0 = outputchar r1, r0
001a:   r1 = set_imm 0, 64      ; d
001b:   r0 = outputchar r1, r0
001c:   r1 = set_imm 0, 3e      ; >
001d:   r0 = outputchar r1, r0
001e:   r1 = set_imm 0, 3e      ; >
001f:   r0 = outputchar r1, r0
0020:   r60 = set_imm 0, ff     ; �
0021:   r61 = set_imm 0, 1      ;
0022:   r4 = set_imm 5, 39      ; 0539
0023:   r3 = inputchar r0, r0
0024:   r50 = set_imm 0, a      ;
0025:   r0 = store r4, r3
0026:   r0 = nop r0, r0
0027:   r10 = sub r3, r50
0028:   r10 = not r10, r0
0029:   r11 = set_imm 0, 2f     ; /
002a:   r0 = branch r10, r11    ; if (r10) goto r11
002b:   r4 = add r61, r4
002c:   r10 = sub r60, r3
002d:   r11 = set_imm 0, 23     ; #
002e:   r0 = branch r10, r11    ; if (r10) goto r11
002f:   r19 = set_imm 5, 39     ; 0539
0030:   r20 = set_imm 0, 0
0031:   r21 = set_imm 0, 23     ; #
0032:   r21 = sub r21, r20
0033:   r21 = not r21, r0
0034:   r22 = set_imm 0, 5e     ; ^
0035:   r0 = branch r21, r22    ; if (r21) goto r22
0036:   r21 = load r20, r0
0037:   r25 = add r19, r20
0038:   r0 = nop r0, r0
0039:   r26 = load r25, r0
003a:   r26 = sub r21, r26
003b:   r22 = set_imm 0, 41     ; A
003c:   r0 = branch r26, r22    ; if (r26) goto r22
003d:   r23 = set_imm 0, 1      ;
003e:   r20 = add r20, r23
003f:   r22 = set_imm 0, 31     ; 1
0040:   r0 = branch r22, r22    ; if (r22) goto r22
0041:   r1 = set_imm 0, 41      ; A
0042:   r0 = outputchar r1, r0
0043:   r1 = set_imm 0, 63      ; c
0044:   r0 = outputchar r1, r0
0045:   r1 = set_imm 0, 63      ; c
0046:   r0 = outputchar r1, r0
0047:   r1 = set_imm 0, 65      ; e
0048:   r0 = outputchar r1, r0
0049:   r1 = set_imm 0, 73      ; s
004a:   r0 = outputchar r1, r0
004b:   r1 = set_imm 0, 73      ; s
004c:   r0 = outputchar r1, r0
004d:   r1 = set_imm 0, 20      ;
004e:   r0 = outputchar r1, r0
004f:   r1 = set_imm 0, 44      ; D
0050:   r0 = outputchar r1, r0
0051:   r1 = set_imm 0, 65      ; e
0052:   r0 = outputchar r1, r0
0053:   r1 = set_imm 0, 6e      ; n
0054:   r0 = outputchar r1, r0
0055:   r1 = set_imm 0, 69      ; i
0056:   r0 = outputchar r1, r0
0057:   r1 = set_imm 0, 65      ; e
0058:   r0 = outputchar r1, r0
0059:   r1 = set_imm 0, 64      ; d
005a:   r0 = outputchar r1, r0
005b:   r1 = set_imm 0, a       ;
005c:   r0 = outputchar r1, r0
005d:   r0 = halt r0, r0
005e:   r1 = set_imm 0, 47      ; G
005f:   r0 = outputchar r1, r0
0060:   r1 = set_imm 0, 72      ; r
0061:   r0 = outputchar r1, r0
0062:   r1 = set_imm 0, 65      ; e
0063:   r0 = outputchar r1, r0
0064:   r1 = set_imm 0, 65      ; e
0065:   r0 = outputchar r1, r0
0066:   r1 = set_imm 0, 74      ; t
0067:   r0 = outputchar r1, r0
0068:   r1 = set_imm 0, 7a      ; z
0069:   r0 = outputchar r1, r0
006a:   r1 = set_imm 0, 20      ;
006b:   r0 = outputchar r1, r0
006c:   r1 = set_imm 0, 68      ; h
006d:   r0 = outputchar r1, r0
006e:   r1 = set_imm 0, 61      ; a
006f:   r0 = outputchar r1, r0
0070:   r1 = set_imm 0, 63      ; c
0071:   r0 = outputchar r1, r0
0072:   r1 = set_imm 0, 6b      ; k
0073:   r0 = outputchar r1, r0
0074:   r1 = set_imm 0, 65      ; e
0075:   r0 = outputchar r1, r0
0076:   r1 = set_imm 0, 72      ; r
0077:   r0 = outputchar r1, r0
0078:   r1 = set_imm 0, 73      ; s
0079:   r0 = outputchar r1, r0
007a:   r1 = set_imm 0, 2e      ; .
007b:   r0 = outputchar r1, r0
007c:   r1 = set_imm 0, 20      ;
007d:   r0 = outputchar r1, r0
007e:   r1 = set_imm 0, 4b      ; K
007f:   r0 = outputchar r1, r0
0080:   r1 = set_imm 0, 65      ; e
0081:   r0 = outputchar r1, r0
0082:   r1 = set_imm 0, 65      ; e
0083:   r0 = outputchar r1, r0
0084:   r1 = set_imm 0, 70      ; p
0085:   r0 = outputchar r1, r0
0086:   r1 = set_imm 0, 20      ;
0087:   r0 = outputchar r1, r0
0088:   r1 = set_imm 0, 75      ; u
0089:   r0 = outputchar r1, r0
008a:   r1 = set_imm 0, 70      ; p
008b:   r0 = outputchar r1, r0
008c:   r1 = set_imm 0, 20      ;
008d:   r0 = outputchar r1, r0
008e:   r1 = set_imm 0, 74      ; t
008f:   r0 = outputchar r1, r0
0090:   r1 = set_imm 0, 68      ; h
0091:   r0 = outputchar r1, r0
0092:   r1 = set_imm 0, 65      ; e
0093:   r0 = outputchar r1, r0
0094:   r1 = set_imm 0, 20      ;
0095:   r0 = outputchar r1, r0
0096:   r1 = set_imm 0, 67      ; g
0097:   r0 = outputchar r1, r0
0098:   r1 = set_imm 0, 6f      ; o
0099:   r0 = outputchar r1, r0
009a:   r1 = set_imm 0, 6f      ; o
009b:   r0 = outputchar r1, r0
009c:   r1 = set_imm 0, 64      ; d
009d:   r0 = outputchar r1, r0
009e:   r1 = set_imm 0, 20      ;
009f:   r0 = outputchar r1, r0
00a0:   r1 = set_imm 0, 77      ; w
00a1:   r0 = outputchar r1, r0
00a2:   r1 = set_imm 0, 6f      ; o
00a3:   r0 = outputchar r1, r0
00a4:   r1 = set_imm 0, 72      ; r
00a5:   r0 = outputchar r1, r0
00a6:   r1 = set_imm 0, 6b      ; k
00a7:   r0 = outputchar r1, r0
00a8:   r1 = set_imm 0, 2e      ; .
00a9:   r0 = outputchar r1, r0
00aa:   r1 = set_imm 0, 20      ;
00ab:   r0 = outputchar r1, r0
00ac:   r1 = set_imm 0, 53      ; S
00ad:   r0 = outputchar r1, r0
00ae:   r1 = set_imm 0, 74      ; t
00af:   r0 = outputchar r1, r0
00b0:   r1 = set_imm 0, 61      ; a
00b1:   r0 = outputchar r1, r0
00b2:   r1 = set_imm 0, 79      ; y
00b3:   r0 = outputchar r1, r0
00b4:   r1 = set_imm 0, 20      ;
00b5:   r0 = outputchar r1, r0
00b6:   r1 = set_imm 0, 73      ; s
00b7:   r0 = outputchar r1, r0
00b8:   r1 = set_imm 0, 68      ; h
00b9:   r0 = outputchar r1, r0
00ba:   r1 = set_imm 0, 61      ; a
00bb:   r0 = outputchar r1, r0
00bc:   r1 = set_imm 0, 72      ; r
00bd:   r0 = outputchar r1, r0
00be:   r1 = set_imm 0, 70      ; p
00bf:   r0 = outputchar r1, r0
00c0:   r1 = set_imm 0, 2e      ; .
00c1:   r0 = outputchar r1, r0
00c2:   r1 = set_imm 0, 20      ;
00c3:   r0 = outputchar r1, r0
00c4:   r1 = set_imm 0, 44      ; D
00c5:   r0 = outputchar r1, r0
00c6:   r1 = set_imm 0, 69      ; i
00c7:   r0 = outputchar r1, r0
00c8:   r1 = set_imm 0, 73      ; s
00c9:   r0 = outputchar r1, r0
00ca:   r1 = set_imm 0, 6f      ; o
00cb:   r0 = outputchar r1, r0
00cc:   r1 = set_imm 0, 62      ; b
00cd:   r0 = outputchar r1, r0
00ce:   r1 = set_imm 0, 65      ; e
00cf:   r0 = outputchar r1, r0
00d0:   r1 = set_imm 0, 79      ; y
00d1:   r0 = outputchar r1, r0
00d2:   r1 = set_imm 0, 20      ;
00d3:   r0 = outputchar r1, r0
00d4:   r1 = set_imm 0, 6d      ; m
00d5:   r0 = outputchar r1, r0
00d6:   r1 = set_imm 0, 69      ; i
00d7:   r0 = outputchar r1, r0
00d8:   r1 = set_imm 0, 73      ; s
00d9:   r0 = outputchar r1, r0
00da:   r1 = set_imm 0, 69      ; i
00db:   r0 = outputchar r1, r0
00dc:   r1 = set_imm 0, 6e      ; n
00dd:   r0 = outputchar r1, r0
00de:   r1 = set_imm 0, 66      ; f
00df:   r0 = outputchar r1, r0
00e0:   r1 = set_imm 0, 6f      ; o
00e1:   r0 = outputchar r1, r0
00e2:   r1 = set_imm 0, 72      ; r
00e3:   r0 = outputchar r1, r0
00e4:   r1 = set_imm 0, 6d      ; m
00e5:   r0 = outputchar r1, r0
00e6:   r1 = set_imm 0, 61      ; a
00e7:   r0 = outputchar r1, r0
00e8:   r1 = set_imm 0, 74      ; t
00e9:   r0 = outputchar r1, r0
00ea:   r1 = set_imm 0, 69      ; i
00ec:   r1 = set_imm 0, 6f      ; o
00ed:   r0 = outputchar r1, r0
00ee:   r1 = set_imm 0, 6e      ; n
00ef:   r0 = outputchar r1, r0
00f0:   r1 = set_imm 0, 2e      ; .
00f1:   r0 = outputchar r1, r0
00f2:   r1 = set_imm 0, a       ;
00f3:   r0 = outputchar r1, r0
00f4:   r1 = set_imm 0, 59      ; Y
00f5:   r0 = outputchar r1, r0
00f6:   r1 = set_imm 0, 6f      ; o
00f7:   r0 = outputchar r1, r0
00f8:   r1 = set_imm 0, 75      ; u
00f9:   r0 = outputchar r1, r0
00fa:   r1 = set_imm 0, 72      ; r
00fb:   r0 = outputchar r1, r0
00fc:   r1 = set_imm 0, 20      ;
00fd:   r0 = outputchar r1, r0
00fe:   r1 = set_imm 0, 66      ; f
00ff:   r0 = outputchar r1, r0
0100:   r1 = set_imm 0, 6c      ; l
0101:   r0 = outputchar r1, r0
0102:   r1 = set_imm 0, 61      ; a
0103:   r0 = outputchar r1, r0
0104:   r1 = set_imm 0, 67      ; g
0105:   r0 = outputchar r1, r0
0106:   r1 = set_imm 0, 20      ;
0107:   r0 = outputchar r1, r0
0108:   r1 = set_imm 0, 69      ; i
0109:   r0 = outputchar r1, r0
010a:   r1 = set_imm 0, 73      ; s
010b:   r0 = outputchar r1, r0
010c:   r1 = set_imm 0, 3a      ; :
010d:   r0 = outputchar r1, r0
010e:   r1 = set_imm 0, 20      ;
010f:   r0 = outputchar r1, r0
0110:   r29 = set_imm 0, 1      ;
0111:   r30 = xor r30, r30
0112:   r1 = load r30, r0
0113:   r0 = outputchar r1, r0
0114:   r30 = add r29, r30
0115:   r31 = set_imm 0, 26     ; &
0116:   r31 = sub r30, r31
0117:   r32 = set_imm 1, 12     ; 0112
0118:   r0 = branch r31, r32    ; if (r31) goto r32
0119:   r1 = set_imm 0, a       ;
011a:   r0 = outputchar r1, r0
011b:   r0 = halt r0, r0

Pseudo C code of the decompiled codefile

// data is an int array - int data[0x2000/4]
// the first 140 bytes of data store the content of "secret.key" file
printf("Enter password>>");
r4 = 1337;
while (!EOF) {
        r3 = getc();
        data[r4] = r3;
        if (r3 == '\n') break;
        r4++;
}
r19 = 1337;
r20 = 0;
while (1) {
        if (r20 == 0x23) goto correctpass;
        if (data[r20] != data[r19+r20]) goto wrongpass;
        r20++;
}

wrongpass:
printf("Access Denied\n");
exit(0);

correctpass:
printf("Greetz hackers. Keep up the good work. Stay sharp. Disobey misinformation.\n");
printf("Your flag is: ");
for(i=0; i<0x26; i++)
        print("%c", data[i]);
printf("\n");

c. Xbox’s TEA hash collision

From the decompiled VM code above, if we could modify the content of codefile, it would be possible to print out the flag inside secret.key stored at data[0]. However, the codefile is protected from being tampered with a hash algorithm.

int
check_code()
{
        int result;
        unsigned int v1;
        unsigned int v2;
        unsigned int i;

        hash_block(0x99999999, 0xBBBBBBBB, 0x44444444, 0x55555555, code[0],
            code[1], &v2, &v1);

        for (i = 2; i <= 32766; i += 2)
                hash_block(v2, v1, v2, v1, code[i], code[i + 1], (int *) &v2,
                    (int *) &v1);

        if (v2 != 0x1EC0A9F0 || (result = v1, v1 != 0x9217F034)) {
                puts("Tampering detected. Prepare for imminent arrest.");
                exit(0);
        }
        return result;
}

Google the constant 0×61C88647 and searching around, I found that it’s a TEA based hash algorithm. Using TEA hash is bad and there is a weakness in the algorithm in which by flipping the 32nd and 64th bit of a 64 bits block, the hash value will remain the same. Xbox was hacked because of this one. (Actually I didn’t know about Xbox’s TEA bits flipping attack. I found this collision by writing a tool doing the brute force on bits flipping of 64 bits block to find the collision. Later, I realized that Yboy is Xbox with two bits flipped)

Now, the next problem is to find how codefile should be patched to print out the flag.

d. Branch instruction handing bug

The 32nd and 64th bit of a 64 bits block are MSB bits of the opcode field of two consequence instructions. Since the code only uses the least 05 bits in opcode for instruction decode, changing the MSB of the opcode won’t affect the instruction decode part. However, there is a problem with branch instruction handling code which will help us to modify the behavior of branch.

If we look at the VM parsing code, an instruction (4 byes) structure is as the following

[ opcode ] [ output register ] [ imm1 ] [ imm2 ]

                opcode = (ins >> 24);   //& 0x1f;
                reg = (ins >> 16) & 0xFF;
                imm1 = (ins >> 8) & 0xFF;
                imm2 = (unsigned char) ins;

The least 05 bits of opcode are being used as an index to lookup for the corresponding function from the decode function table (decode[opcode & 0x1f])

Lets look deeper at the code handling ‘branch’ instruction:

int branch(int imm1, int imm2)
{
        if (imm1)
                PC = imm2;
        else
                PC++;
        return 0;
}

Inside main()

codegatechal2

As we can see, it only uses the least five bits of opcode ((ins >> 24) & 0×1f) for decode while the full byte (ins >> 24) is used for comparing later (opcode value for branch is oxC).

If we set the MSB of opcode, the opcode would become 0×8c. In this case, the branch() function is still being called, however, the (opcode == 0xC) check in main() will be false and PC will be increased by 1 unexpectedly.

e. Subvert the code flow to print out the flag

Look back at the decompiled VM code

0020:   r60 = set_imm 0, ff     ; r60 = 255
0021:   r61 = set_imm 0, 1      ; r61 = 1
0022:   r4 = set_imm 5, 39      ; r4 = 0x539  (1337)
0023:   r3 = inputchar r0, r0   ; r3 = getc()
0024:   r50 = set_imm 0, a      ; r50 = '\n'
0025:   r0 = store r4, r3          ; data[r4] = r3
0026:   r0 = nop r0, r0
0027:   r10 = sub r3, r50        ; r10 = r3 - '\n'
0028:   r10 = not r10, r0         ; !r10
0029:   r11 = set_imm 0, 2f     ; r11 = 0x002f
002a:   r0 = branch r10, r11    ; if (r3 == '\n') goto 002f
002b:   r4 = add r61, r4           ; r4++
002c:   r10 = sub r60, r3         ; r10 = 255 - r3
002d:   r11 = set_imm 0, 23    ; 0x0023
002e:   r0 = branch r10, r11    ; if (r3 != EOF) goto 0023
002f:   r19 = set_imm 5, 39     ; r19 = 0x539 (1337)
0030:   r20 = set_imm 0, 0      ; r20 = 0
0031:   r21 = set_imm 0, 23    ; r21 = 0x23 (35)
0032:   r21 = sub r21, r20      ; r21 = r21 - r20
0033:   r21 = not r21, r0        ; !r21
0034:   r22 = set_imm 0, 5e     ; 0x005e
0035:   r0 = branch r21, r22    ; if (r20 == 35) goto 005e //goodpassword
0036:   r21 = load r20, r0        ; r21 = data[r20]
0037:   r25 = add r19, r20       ; r25 = 0x539 + r20
0038:   r0 = nop r0, r0
0039:   r26 = load r25, r0        ; r26 = data[0x539 + r20]
003a:   r26 = sub r21, r26        ; r26 = r26 - r21
003b:   r22 = set_imm 0, 41     ; 0x0041
003c:   r0 = branch r26, r22    ; if (data[r20] != data[0x539+r20) goto 0041 //badpassword
003d:   r23 = set_imm 0, 1      ; r23 = 1
003e:   r20 = add r20, r23       ; r20++
003f:   r22 = set_imm 0, 31     ; 0x0032
0040:   r0 = branch r22, r22    ; goto 0032 //loop

The code above read password from stdin, stores it inside data array starting at data[0x539] then compares the input with the content of secret.key stored at the beginning of data[0] (35 DWORDS = 140 bytes).

What if we modify the MSB bit of branch instruction at 002a?

//modify code[2a] from 0x0c000a0b to 0x8c000a0b
002a: r0 = branch r10, r11 ; if (r3 == '\n') goto 002f

When '\n' is read, the branch() instruction will set PC to the password check code at 002f (002f: r19 = set_imm 5, 39 ; r19 = 0x539). However, because the opcode now is 0x8c instead of 0x0c, PC will be also increased by 1 unexpectedly due to the bug at main loop code mentioned above. Hence, the PC will point to instruction at 0030 (0030: r20 = set_imm 0, 0 ; r20 = 0) instead of 002f.

Since the instruction at 002f is skipped, r19 register will be 0 (default value) instead of 0x539. The VM code becomes

//r19 register value is 0 while it's expected to be 0x539
r20 = 0;
while (1) {
        if (r20 == 0x23) goto correctpass;
        if (data[r20] != data[r19+r20]) goto wrongpass;
        r20++;
}

It's comparing identical data. Yboy Pwned!

Exploit

  • Copy the codefile, edit it to set the 32nd and 64th bits at offset 0x2a

code[2a] 0x0c000a0b -> 0x8c000a0b
code[2b] 0x03043d04 -> 0x83043d04

  • Run the yboy with the new codefile
  • Press enter and get the flag

hugh@codegate-desktop:/tmp/rd$ ./yboy newcodefile
...
Enter password>>
Greetz hackers. Keep up the good work. Stay sharp. Disobey misinformation.
Your flag is: TEA - Toiletpaper Esque Aspirations

References

Keywords: TEA, VM, Xbox, codegate 2010

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

CodeGate 2010 Challenge 15 – SHA1 padding attack

March 16, 2010 by RD · 13 Comments 

Summary

This is a web based crypto challenge vulnerable to padding/length extension attack in its sha1 based authentication scheme.

Analysis

Challenge URL: http://ctf1.codegate.org/03c1e338b6445c0f127319f5cb69920a/web1.php

This page will ask for submitting a username for the first time. Once a username is submited ( ‘aaaa’ for example), the script will set a cookie as the following:

web1_auth = YWFhYXwx|8f5c14cc7c1cd461f35b190af57927d1c377997e

The first part YWFhYXwx is the base64 encoded string of ‘aaaa|1′ (username|role). The second part 8f5c14cc7c1cd461f35b190af57927d1c377997e is the sha1(unknown_secretkey + username + role).

In the next visit, the web1.php script will check for the cookie and return the following message

“Welcome back, aaaa! You are not the administrator.”

We can guest that 1 is the role value for normal user and 0 for administrator.

Solution

If we try to modify to first part of the web1_auth cookie to something like base64_encode(’aaaa|0′), the script will return an error message saying that the data has been tampered due to the wrong signature.

As we know that popular hash functions including sha1 are vulnerable to length extension (or padding) attacks. This can be used to break naive authentication schemes based on hash functions.

I will not write the detail on how to do sha1 length extension attack, you can read papers in the References section below for more information. Basically, with padding attack, we can append arbitrary data to the cookie and generate a valid signature for it without knowing the secret key. In this challenge, we want to have ‘|0′ (administrator role) at the end of the first part of the cookie.

$ python sha-padding.py
usage: sha-padding.py <keylen> <original_message> <original_signature> <text_to_append>

$ python sha-padding.py 25 ‘aaaa|1′ 8f5c14cc7c1cd461f35b190af57927d1c377997e ‘|0′
new msg: ‘aaaa|1\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8|0′
base64: YWFhYXwxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4fDA=
new sig: 70f8bf57aa6d7faaa70ef17e763ef2578cb8d839

And here is what we got with the web1_auth cookie using YWFhYXwxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4fDA= and signature 70f8bf57aa6d7faaa70ef17e763ef2578cb8d839

Welcome back, aaaa! Congratulations! You did it! Here is your flag: CryptoNinjaCertified!!!!!

Source Codes

References

Keywords: sha1, padding, length extension attack, codegate 2010

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

Lỗ hổng nghiêm trọng của TLS/SSL

November 6, 2009 by thaidn · 1 Comment 

Một phát hiện hết sức thú vị:

The SSL 3.0+ and TLS 1.0+ protocols are vulnerable to a set of related attacks which allow a man-in-the-middle (MITM) operating at or below the TCP layer to inject a chosen plaintext prefix into the encrypted data stream, often without detection by either end of the connection. This is possible because an “authentication gap” exists during the renegotiation process at which the MitM may splice together disparate TLS connections in a completely standards-compliant way. This represents a serious security defect for many or all protocols which run on top of TLS, including HTTPS.

Thú vị ở chỗ bao nhiêu người, bao nhiêu chuyên gia, bao nhiêu năm qua dòm vô TLS/SSL mà không thấy được lỗ hổng có vẻ như rất hiển nhiên mà các tác giả ở trên phát hiện.

Có lẽ nguyên nhân nhiều người dòm nhưng không thấy là vì họ chỉ dòm TLS/SSL khi nó đứng một mình, mà không nhìn vào bức tranh lớn OSI, trong đó TLS/SSL chỉ là một layer. Chuyện gì sẽ xảy ra nếu TLS/SSL không hiểu rõ cơ chế hoạt động của các protocol bên trên nó, như HTTP, SMTP hay POP3? Nói cách khác, chuyện gì sẽ xảy ra nếu các protocol ở mức Application không hiểu rõ cơ chế vận hành của TLS/SSL để sử dụng cho đúng cách? Đó là lúc lỗ hổng xuất hiện.

Tổng quan thì lỗ hổng này nằm ở sự thiếu “ăn rơ” giữa TLS/SSL và các protocol trên nó như HTTP hay SMTP. Khai thác lỗ hổng này thì kẻ tấn công có thể chèn thêm một đoạn plaintext bất kỳ vào TLS/SSL encrypted stream giữa client và server mà cả client và server đều không thể phát hiện được.

Đây là một lỗ hổng cực kỳ nghiêm trọng, bởi vì nó phá vỡ hoàn toàn cam kết an toàn của bộ giao thức TLS/SSL. Nói một cách *hoành tráng* thì về mặt lý thuyết, nền tảng của thương mại điện tử đang chao đảo. Tôi dùng chữ lý thuyết vì để cho hướng tấn công này nguy hiểm hơn trong thực tế, thì còn có nhiều trở ngại phải vượt qua (và sẽ bị vượt qua).

Để minh họa cho câu chuyện, và để dễ giải thích, tôi đặt ra một ví dụ như sau:

0. Giả định:

* Ngân hàng A có cung cấp dịch vụ Internet Banking ở địa chỉ https://www.ebank.com. Máy chủ của của họ chạy phần mềm có lỗ hổng mà chúng ta đang bàn ở đây. Chúng ta gọi máy chủ này là server.

* Để tăng cường an ninh, ngân hàng A yêu cầu khi khách hàng (giờ cứ gọi là client) sử dụng các tính năng có liên quan đến giao dịch tài chính nằm trong khu vực https://www.ebank.com/account/, thì (browser của) họ phải có cài đặt client certificate cho ngân hàng A cung cấp. Lưu ý là nhiều ngân hàng ở VN thực hiện cái này lắm nha.

* Ngoài ra ngân hàng A còn hỗ trợ khách hàng truy cập bằng (Safari trên) iPhone, lúc đó khách hàng sẽ được chuyển đến https://www.ebank.com/iphone/. Do iPhone có processor yếu, nên ngân hàng A cấu hình máy chủ web của họ để sử dụng một bộ ciphersuite yếu hơn bộ ciphersuite mà họ sử dụng cho các khách hàng thông thường. Cái này trong thực tế cũng có nhiều công ty triển khai.

Rồi bây giờ tôi sẽ sử dụng cái kỹ thuật vừa mới phát hiện để tấn công các khách hàng của ngân hàng A theo 3 hướng tấn công mà các tác giả nêu ra. Àh lưu ý là đây là loại tấn công MITM, nghĩa là attacker phải có quyền theo dõi, điều chỉnh dữ liệu truyền qua lại giữa client và server nha. Attacker có thể làm việc này thông qua các tấn công vào các giao thức ARP hay DNS.

1. Hướng tấn công số 1

Đối với hướng tấn công số 1, tôi sẽ lợi dụng việc khi truy cập vào https://www.ebank.com/account/ thì server sẽ yêu cầu client phải trình certificate.

Sơ đồ bên dưới là tôi lấy từ paper của các tác giả phát hiện ra lỗ hổng này. Tôi thấy cái sơ đồ này giải thích rất rõ lỗ hổng này và cách thức tấn công theo hướng thứ 1. Thật ra thì hướng thứ 2 và hướng thứ 3 cũng khá giống hướng thứ 1, nên tôi nghĩ nắm rõ hướng thứ 1 thì sẽ thấy các hướng kia cũng đơn giản.

Có 4 bước khi triển khai tấn công này:

* Bước 1: client truy cập vào https://www.ebank.com. Lúc này client sẽ kết nối đến attacker, và gửi CLIENT_HELLO để bắt đầu giao thức TLS/SSL. Attacker sẽ tạm dừng cái kết nối này và lưu msg CLIENT_HELLO lại để dùng trong bước 3.

* Bước 2: attacker mở kết nối đến server thật. Hai bên sẽ bắt tay theo giao thức TLS/SSL để tạo thành một session. Sau khi hoàn tất bắt tay, attacker gửi một HTTP request, đại loại như:

POST /account/transfer?amount=1000&amp;receiver=attacker HTTP/1.1\r\n

* Bước 3: server thấy có một request đến khu vực /account/ nên nó tạm thời dừng xử lý request này lại và như đã nói ở trên, nó yêu cầu attacker phải đưa client certificate cho nó xem. Cái hay ở đây, mặc dầu attacker không có (private key của) certificate của client, nhưng hắn vẫn có thể *proxy* cái certificate đó từ client lên server, mà không bị bên nào phát hiện cả.

Server bắt đầu quá trình xác thực bằng việc gửi một msg HELLO_REQUEST ngược lại cho attacker. Attacker nhận được msg này thì hắn gửi CLIENT_HELLO mà hắn đã lưu ở bước 1 ngược lại cho server. Rồi cứ thế, attacker đứng giữa, chuyển msg qua lại giữa client và server cho đến khi quá trình xác thực bằng client certificate kết thúc thành công.

Lưu ý là có 2 loại msg mà attacker sẽ gửi. Loại thứ nhất (trên sơ đồ là những msg kết thúc hoặc bắt đầu từ cột m) là những msg mà hắn phải giải mã/mã hóa trước khi gửi đi. Ví dụ như hắn nhận “Certificate” từ phía client thì hắn sẽ mã hóa cái msg này lại, rồi mới gửi cho server. Loại thứ hai (trên sơ đồ là những msg màu hồng và đỏ) là những msg mà hắn không đọc được (vì không có key), hắn chỉ làm mỗi việc là nhận từ client thì gửi qua server và ngược lại.

* Bước 4: quá trình xác thực client certificate đã kết thúc thành công, server tiếp tục xử lý cái request của attacker ở trên, và trả kết quả lại cho attacker (lưu ý là attacker sẽ không đọc được kết quả này).

Điểm yếu là ở đây. Như chúng ta thấy, khi attacker gửi request ở bước 3, lúc đó hắn chưa được xác thực. Nói cách khác, lúc này request của hắn là unauthenticated request. Việc xác thực diễn ra sau đó, và sau khi xác thực rồi thì server lại quay lại xử lý tiếp cái unauthenticated request của attacker.

Lưu ý, ở bước này, để tránh bị tình nghi, attacker có thể tiếp tục trả kết quả về cho client để đóng kết nối lại một cách êm đẹp.

2. Hướng tấn công số 2

Trước khi bắt đầu giải thích hướng số 2, tôi muốn nhấn mạnh ý này: tất cả 3 hướng tấn công này đều hướng đến chôm credential của client để gửi các authenticated request đến server. Credential ở đây có thể là certificate (như ở hướng số 1) hay cookie/session (như ở hướng số 2 và số 3). Nếu chỉ áp dụng cho HTTPS, nhìn ở một góc độ nào đó, các hướng tấn công này rất giống với tấn công CSRF. Nên nếu ứng dụng của bạn đã có các phương thức phòng chống CSRF rồi hay nếu ứng dụng của bạn không chấp nhận thay đổi state bằng GET, thì tạm thời cũng không phải có gì lo lắng.

Đối với hướng số 1, tôi lợi dụng client certificate để gửi một authenticated request. Ở trường hợp các server không xác thực bằng certificate, tôi sẽ sử dụng hướng tấn cống số 2.

Hướng tấn công này cũng có 4 bước:

* Bước số 1: tương tự như hướng tấn công số 1.

* Bước 2: attacker mở kết nối đến server thật. Hai bên sẽ bắt tay theo giao thức TLS/SSL để tạo thành một session.

Sau khi hoàn tất bắt tay, attacker gửi một HTTP request, đại loại như:

GET /iphone/login HTTP/1.1\r\n
Host: ebank.com\r\n
Connection: keep-alive\r\n
\r\n
GET /account/transfer?amount=1000&amp;receiver=attacker HTTP/1.1\r\n
Host: ebank.com\r\n
Connection: close\r\n
X-ignore-this:

* Bước số 3: server thấy có request đến /iphone/ nên nó tạm thời dừng xử lý request này lại và, như đã nói ở phần giả định, server sẽ bắt đầu quá trình renegotiate lại để chọn một bộ ciphersuite yếu hơn. Vấn đề ở đây là server sẽ buffer lại toàn bộ nhóm unauthenticated request này, khi mà renegotiate xong thì lại quay lại xử lý hết tất cả.

Trong quá trình renogotiation, vai trò của attacker cũng tương tự như ở bước số 3 của hướng tấn công số 1, nghĩa là hắn cũng chỉ *proxy* msg qua lại giữa client và server, cho đến khi quá trình renegotiate kết thúc thành công.

* Bước số 4: lúc này, client thấy đã handshake xong rồi, nên bản thân nó sẽ gửi tiếp cái HTTP request của nó ở dạng:

GET /index HTTP/1.1\r\n
Cookie: AuthMe=Now\r\n
\r\n

Chuyện bất ngờ diễn ra ở đây. Server nó sẽ gom nhóm unauthenticated request ở bước 2 (do attacker gửi) và cái authenticated request này (do client gửi) rồi xử lý chung một lần. Nguyên nhân server xử lý như thế là do cái cờ keep-alive ở request đầu tiên. Thành ra lúc này nhóm request trở thành như sau (màu cam là attacker gửi, màu xanh là client gửi):

GET /iphone/login HTTP/1.1\r\n
Host: ebank.com\r\n
Connection: keep-alive\r\n
\r\n
GET /account/transfer?amount=1000&amp;receiver=attacker HTTP/1.1\r\n
Host: ebank.com\r\n
Connection: close\r\n
X-ignore-this:GET /index HTTP/1.1\r\n
Cookie: AuthMe=Now\r\n
\r\n

Ở đây cái header X-ignore-this đã vô hiệu hóa cái request GET /index HTTP/1.1 của client, đồng thời chôm luôn cookie của client để gắn vào cái unauthenticated request GET /account/transfer?amount=1000&receiver=attacker. Rất hay!

3. Hướng tấn công số 3

Đây là hướng tấn công mạnh nhất, không cần server phải có cấu hình đặc biệt gì để thực hiện. Sự khác biệt cơ bản giữa tấn công này với hai hướng tấn công vừa rồi là trong trường hợp này, client bắt đầu quy trình renegotiation.

Ý tưởng thực hiện tấn công rất giống với hướng 2, chỉ khác nhau ở bước số 2, attacker sẽ không gửi GET /iphone/login nữa mà gửi trực tiếp luôn request của hắn, kèm theo một cái “X-ignore-this” header.

Ngay sau khi gửi cái request đó, attacker sẽ forward cái CLIENT_HELLO thu được ở bước 1 sang cho phía server để bắt đầu quy trình renegotiation. Khi đã renegotiate xong, client sẽ gửi request ban đầu của mình đến server, lúc này toàn bộ request sẽ trông như sau (phần màu cam của attacker gửi, phần màu xanh của client gửi):

GET /account/transfer?amount=1000&amp;receiver=attacker HTTP/1.1\r\n
Host: ebank.com\r\n
Connection: close\r\n
X-ignore-this: GET /index HTTP/1.1\r\n
Cookie: AuthMe=Now\r\n
\r\n

Tương tự ở trên, X-ignore-this đã vô hiệu hóa request của client và chôm cookie để biến request của attacker thành authenticated. Không cần keep-alive, không cần server phải có cấu hình đặc biệt gì cả!

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

Next Page »