miscfreeeasy

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/
sequential-queryingbinary-search-flag-length

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:

  1. The service uses 0-based indexing for character positions
  2. It returns the exact character at the specified index
  3. No authentication or rate limiting is apparent
  4. 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

  1. Scripting is Essential: The challenge is designed to be tedious if done manually. Writing a script transforms a potentially hours-long task into seconds.

  2. Efficient Length Discovery: Binary search for flag length is more efficient than linear scanning, especially for long flags.

  3. Connection Management: Each query requires a new connection. The service doesn't maintain state between connections.

  4. 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