siunam's Website

My personal website

Home Writeups Blog Projects About E-Portfolio

Guess the Password?

Background

We found a VIP’s box, but when we try to guess his short password, we get rate limited! We managed to get the source code and it looks like the server implements its security in a way that isn’t secure! Can you reverse the python code and get the flag?

nc guessthepassword.challenges.ctf.ritsec.club 1337

Find the flag

In this challenge, we can download 3 files:

┌[siunam♥earth]-(~/ctf/RITSEC-CTF-2023/Reversing/Guess-the-Password?)-[2023.04.01|20:06:26(HKT)]
└> file *       
encoding.py:      Python script, ASCII text executable
server.py:        Python script, ASCII text executable
supersecret.json: JSON text data

By looking at function chatter() in server.py, we need to send a **8-digit password to the server. Then, i’ll check our input (encoder.check_input()):**

[...]
def chatter(self, connection_info):
    self.debug_print("Client connected")
    client_socket = connection_info[0]
    client_ip = connection_info[1][0]

    if self.user_is_rate_limited(client_ip):
        client_socket.send( "You are being rate limited".encode() )
        client_socket.close()
        return

    client_socket.send( "Enter the passcode to access the secret: \n".encode() )
    user_input = client_socket.recv(1024).decode() [:8]

    if len(user_input) == 8 and self.encoder.check_input(user_input):
        secret = self.encoder.flag_from_pwd(user_input)
        response = f"RS{ {secret} }\n"

    else:
        response = "That password isn't right!\n\tHint: The last 8 digits of your phone number\n"

    response += "\nClosing connection...\n"
    client_socket.send(response.encode())
    client_socket.close()

    self.debug_print("Client connection closed")
[...]

encoding.py:

[...]
def hash(self, user_input):
    salt = "RITSEC_Salt"
    return hashlib.sha256(salt.encode() + user_input.encode()).hexdigest()


def check_input(self, user_input):
    hashed_user_input = self.hash(user_input)
    # print("{0} vs {1}".format(hashed_user_input, self.hashed_key))
    return hashed_user_input == self.hashed_key
[...]

Next, our user input will be hashed via SHA256 with salt RITSEC_Salt.

If the our user input hash is matched to the correct one, we’re in!

supersecret.json:

{
    "key":"657fa7558ae9011e8b9d3f56d5c083273557c3139f27d7b62cac458eb1a1a19d",
    "secret":"xxxxCORRUPTED_SECRETxxxx"
}

With that said, we can write a script that brute force the 8-digit password with that salt!

Since I’m good at Python, let’s write a Python script to do that! You can write that in Rust, Go, whatever language you want.

crack_hash.py:

#!/usr/bin/env python3
import hashlib

def main():
    salt = 'RITSEC_Salt'
    key = '657fa7558ae9011e8b9d3f56d5c083273557c3139f27d7b62cac458eb1a1a19d'

    for i in range(100000000):
        user_input = f'{i:08d}'

        hashed = hashlib.sha256(salt.encode() + user_input.encode()).hexdigest()
        print(f'[*] Trying password {user_input}, after hashed: {hashed}', end='\r')

        if hashed == key:
            print('[+] Found the correct password!')
            print(f'[+] Before hashed: {user_input}')
            print(f'[+] After hashed: {hashed}')
            exit()

if __name__ == '__main__':
    main()

This script will loop 00000000 to 99999999. If the hashed number is matched to the key one, we found the correct password!

Let’s run it!

┌[siunam♥earth]-(~/ctf/RITSEC-CTF-2023/Reversing/Guess-the-Password?)-[2023.04.01|19:10:30(HKT)]
└> python3 crack_hash.py
[...]
[*] Trying password 54744973, after hashed: 657fa7558ae9011e8b9d3f56d5c083273557c3139f27d7b62cac458eb1a1a19[+] Found the correct password!
[+] Before hashed: 54744973
[+] After hashed: 657fa7558ae9011e8b9d3f56d5c083273557c3139f27d7b62cac458eb1a1a19d

Nice! We found the correct password: 54744973!

Let’s nc to the challenge’s machine!

┌[siunam♥earth]-(~/ctf/RITSEC-CTF-2023/Reversing/Cats-At-Play)-[2023.04.01|18:39:29(HKT)]
└> nc guessthepassword.challenges.ctf.ritsec.club 1337
Enter the passcode to access the secret: 
54744973
RS{'PyCr@ckd'}

Closing connection...

Conclusion

What we’ve learned:

  1. Brute Forcing SHA256 Hash With Salt