2024 Cyber Apocalypse: Character
Challenge Information
| Attribute | Details |
|---|---|
| Event | 2024 Cyber Apocalypse |
| Category | Misc |
| Challenge | Character |
| Difficulty | Medium |
Summary
Character is a “security through induced boredom” challenge where the server reveals the flag one character at a time. Querying for each character index manually would be tedious given the flag length (106 characters). The solution requires automating socket communication to fetch characters sequentially and assembling the complete flag.
Analysis
Challenge characteristics:
- Server: Accepts index queries (0-based)
- Response Format: “Character at Index N: X” or similar
- Flag Length: 106 characters (determined experimentally)
- Limitation: Only one character per query
- Goal: Automate retrieval of all characters
The challenge tests:
- Socket programming fundamentals
- String parsing and character extraction
- Loop automation
- Patience and attention to detail
Solution
Approach 1: Basic Socket Solution
import socket
def recvuntil(sock, delim=b'\n'): """Receive data from socket until delimiter is found""" data = b'' while not data.endswith(delim): data += sock.recv(1) return data.decode()
def fetch_flag_character(ip, port, num_of_digits): flag = ''
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(2) s.connect((ip, port))
for i in range(num_of_digits): try: # Send the index query = f"{i}\n".encode() s.sendall(query)
# Receive response response = recvuntil(s, b'\n')
# Parse character from response # Expected format: "Character at Index N: X" character = response.split(': ')[-1].strip()
# Append to flag flag += character
print(f"[{i}] {character}", end='', flush=True)
except socket.timeout: print(f"\nTimeout at index {i}") break
print(f"\n\nFlag: {flag}")
# Save to file with open("flag.txt", "w") as f: f.write(flag)
if __name__ == "__main__": IP = "94.237.55.138" PORT = 53703 NUM_CHARS = 106
fetch_flag_character(IP, PORT, NUM_CHARS)Approach 2: Using Pwntools
from pwn import *import re
def exploit(): conn = remote('94.237.55.138', 53703)
flag = '' for i in range(106): conn.sendline(str(i)) response = conn.recvline().decode()
# Extract character using regex match = re.search(r'Character at Index \d+: (.)', response) if match: char = match.group(1) flag += char print(f"[{i}] {char}") else: print(f"[{i}] Parse error: {response}") break
print(f"\nComplete flag: {flag}") conn.close()
if __name__ == "__main__": exploit()Step-by-Step Execution
- Connect: Establish TCP socket to server
- Query: Send index number with newline
- Parse: Extract character from response string
- Aggregate: Build flag string character by character
- Save: Write flag to file for verification
Sample Output
[0] H[1] T[2] B[3] {[4] t[5] H...[105] }
Complete flag: HTB{tH15_1s_4_r3aLly_l0nG_fL4g_i_h0p3_f0r_y0Ur_s4k3_tH4t_y0U_sCr1pTEd_tH1s_oR_els3_iT_t0oK_qU1t3_l0ng!!}Key Implementation Details
Socket Configuration
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.settimeout(2) # Prevent indefinite blockings.connect((host, port))Response Parsing
Adjust parsing based on actual server response format:
# If response: "Character at Index 0: H\n"character = response.split(': ')[-1].strip()
# Alternative with regexmatch = re.search(r':\s*(.)', response)if match: character = match.group(1)Error Handling
try: # Send query s.sendall(query.encode()) # Receive response response = recvuntil(s, b'\n')except socket.timeout: print("Timeout - server may be slow or disconnected") breakexcept ConnectionResetError: print("Connection lost") breakPerformance Optimization
Reduce Overhead
- Batch Queries: Send multiple queries before receiving (if protocol allows)
- Connection Reuse: Keep socket open across all queries
- Timeout Tuning: Set appropriate timeout for network conditions
- Buffer Management: Optimize socket recv() buffer size
Progress Monitoring
import sys
for i in range(num_chars): # ... query logic ... sys.stdout.write(f'\r[{i}/{num_chars}] {flag}') sys.stdout.flush()Troubleshooting
| Issue | Solution |
|---|---|
| Timeout on every query | Increase timeout, check network |
| Parse error | Verify response format, use regex |
| Connection refused | Verify IP/port, check firewall |
| Incomplete flag | Verify correct number of characters |
| Garbled characters | Check encoding (UTF-8), line endings |
Key Takeaways
- Automation solves tedious manual tasks efficiently
- Socket programming is fundamental for network interaction
- Response parsing requires understanding protocol format
- Error handling prevents script crashes
- Testing with small samples before full automation saves time
- File output provides permanent record of results