2024 Cyber Apocalypse: Character

Challenge Information

AttributeDetails
Event2024 Cyber Apocalypse
CategoryMisc
ChallengeCharacter
DifficultyMedium

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:

  1. Server: Accepts index queries (0-based)
  2. Response Format: “Character at Index N: X” or similar
  3. Flag Length: 106 characters (determined experimentally)
  4. Limitation: Only one character per query
  5. 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

  1. Connect: Establish TCP socket to server
  2. Query: Send index number with newline
  3. Parse: Extract character from response string
  4. Aggregate: Build flag string character by character
  5. 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 blocking
s.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 regex
match = 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")
break
except ConnectionResetError:
print("Connection lost")
break

Performance Optimization

Reduce Overhead

  1. Batch Queries: Send multiple queries before receiving (if protocol allows)
  2. Connection Reuse: Keep socket open across all queries
  3. Timeout Tuning: Set appropriate timeout for network conditions
  4. 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

IssueSolution
Timeout on every queryIncrease timeout, check network
Parse errorVerify response format, use regex
Connection refusedVerify IP/port, check firewall
Incomplete flagVerify correct number of characters
Garbled charactersCheck 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