Character
hackthebox
Task: Extract a flag from a network service that reveals only one character at a time per connection. Solution: Script automated connections to query each character index sequentially, determine the flag length via binary search or linear scan, and reconstruct the full flag string.
$ ls tags/ techniques/
Character — HackTheBox
Challenge Overview
Category: Misc / Network
Difficulty: Easy
Description: "Security through Induced Boredom is a personal favourite approach of mine. Not as exciting as something like The Fray, but I love making it as tedious as possible to see my secrets, so you can only get one character at a time!"
The challenge presents a network service that allows querying individual characters of a flag, one position at a time. This is a classic "tedious access" challenge where manual extraction would be impractical, making scripting essential.
Connection: nc 94.237.122.95 37667
Reconnaissance & Discovery
Initial Connection
When connecting to the service, we receive a prompt asking for a character index:
Which character (index) of the flag do you want? Enter an index:
The service responds with the character at the requested position. This is a straightforward interface:
- Input: An integer representing the character position (0-based indexing)
- Output: The character at that position in the flag
Understanding the Service Behavior
Key observations from testing the service:
- The service uses 0-based indexing for character positions
- It returns the exact character at the specified index
- No authentication or rate limiting is apparent
- The service closes the connection after each query
Exploitation Strategy
Step 1: Determine Flag Length
Since we don't know the flag length, we need to discover it. We can use binary search to efficiently find the maximum valid index:
def find_flag_length(): low, high = 0, 1000 # Reasonable upper bound while low < high: mid = (low + high + 1) // 2 try: char = get_char(mid) # If we get a character, the flag is at least mid+1 long low = mid except: # Connection error or empty response means index is too high high = mid - 1 return low + 1 # Length is high + 1
However, for this challenge, we can also simply iterate until we find the boundary.
Step 2: Extract All Characters
Once we know the length, we iterate through all valid indices and collect each character:
def extract_flag(length): flag = "" for i in range(length): char = get_char(i) flag += char print(f"Progress: {i+1}/{length}", end="\r") return flag
Step 3: Reconstruct the Flag
Combine all characters into the final flag string.
Solution Script
#!/usr/bin/env python3 """ Character Challenge Solution HackTheBox - Misc/Network Extracts the flag character by character from a network service that reveals one character position at a time. """ import socket HOST = "94.237.122.95" PORT = 37667 def get_char(index: int) -> str: """ Query a single character from the service at the given index. Args: index: The character position to query (0-based) Returns: The character at the specified position """ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((HOST, PORT)) # Receive the prompt sock.recv(4096) # Send the index sock.send(f"{index}\n".encode()) # Receive the response (character) response = sock.recv(4096).decode().strip() sock.close() # Extract just the character from the response # Response format typically: "Character at index {index}: {char}" return response def find_flag_length() -> int: """ Determine the flag length using binary search. Tests indices until we find an invalid position. """ low, high = 0, 500 # Start with reasonable bounds while low <= high: mid = (low + high) // 2 try: char = get_char(mid) # Character exists, try higher indices low = mid + 1 except Exception as e: # Index too high high = mid - 1 return low # low is the first invalid index, so length is low def main(): print("[*] Finding flag length...") length = find_flag_length() print(f"[*] Flag length: {length}") print("[*] Extracting flag characters...") flag = "" for i in range(length): char = get_char(i) flag += char print(f"[*] Progress: {i + 1}/{length} characters", end="\r") print("\n") print("=" * 60) print(f"FLAG: {flag}") print("=" * 60) if __name__ == "__main__": main()
Alternative: Using Pwntools
For a more robust solution with better error handling:
#!/usr/bin/env python3 from pwn import * HOST = "94.237.122.95" PORT = 37667 def get_char(index: int) -> str: """Get a single character from the service.""" r = remote(HOST, PORT) r.recvuntil(b"index: ") r.sendline(str(index)) response = r.recvline().decode().strip() r.close() return response def main(): # Find length length = 0 while True: try: get_char(length) length += 1 except: break print(f"Flag length: {length}") # Extract flag flag = "".join(get_char(i) for i in range(length)) print(f"Flag: {flag}") if __name__ == "__main__": main()
Key Insights
-
Scripting is Essential: The challenge is designed to be tedious if done manually. Writing a script transforms a potentially hours-long task into seconds.
-
Efficient Length Discovery: Binary search for flag length is more efficient than linear scanning, especially for long flags.
-
Connection Management: Each query requires a new connection. The service doesn't maintain state between connections.
-
0-based Indexing: The service uses 0-based indexing, which is standard for programming challenges but important to note.
When to Use This Technique
This approach applies when:
- A service allows querying individual positions of a secret
- Each query reveals one piece of information
- No bulk extraction endpoint exists
- The data has a discoverable length
Common variations include:
- Byte-by-byte decryption oracles
- Character-by-character password checking
- Index-based information disclosure vulnerabilities
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar
Similar writeups
- [misc][free]PyDome— HackTheBox
- [crypto][free]Noisy— HackTheBox
- [misc][free]exponential— umdctf
- [crypto][free]AliEnS Challenge Scenario— HackTheBox
- [pwn][Pro]Birdy— spbctf