//Character
Challenge: Character (Category: MISC)
Remote: nc 94.237.51.6 42537
Flag format: HTB{...}
>Short summary
This challenge exposes a TCP service that answers a single-character of the flag when queried with an index. The protection approach was intentionally tedious: you can only request the flag one character at a time. The solution is enumeration: repeatedly query indices until the service responds "Index out of range" and assemble the returned characters into the final flag.
I wrote two small helper scripts to automate interaction and a robust per-index fetcher that opens a fresh connection per index (avoiding prompt-state issues). Both scripts are included below, and the reliable one was used to reconstruct the full flag.
Final flag:
HTB{tH15_1s_4_r3aLly_l0nG_fL4g_i_h0p3_f0r_y0Ur_s4k3_tH4t_y0U_sCr1pTEd_tH1s_oR_els3_iT_t0oK_qU1t3_l0ng!!}
>Reconnaissance (manual)
I first connected to the service with netcat to see how it behaves:
nc 94.237.51.6 42537
The service prints a prompt like:
Which character (index) of the flag do you want? Enter an index:
When a valid index is submitted it returns a single-character line such as:
Character at Index 9: 1
Invalid or out-of-range indices produce Index out of range!.
Because the server is stateful (single connection keeps prompting), a simple approach is to open a fresh connection for each index request, read the response and close the connection. This avoids needing to track or synchronize the server prompt state.
>Approach / Plan
-
Automate repeated queries by index, starting from 1 and incrementing until the server replies
Index out of range!. -
For reliability, open a fresh TCP connection for each index query, send the index, capture the response and close the connection.
-
Parse the returned text for the character and assemble the final flag string.
Edge cases considered:
-
Network timeouts and partial reads — handled by reasonable socket timeouts and reading until timeout.
-
Unexpected formatting — fallback parsing by splitting on the last colon.
>Scripts
Both scripts used in this solve are included in the repository. Below are the contents and short explanation.
/home/noigel/HTB/MISC/Character/001_fetch_flag.py
This was my first attempt: it uses a single connection and iterates through indices while reading server responses. It was useful to validate the protocol but had corner cases when the prompt and response boundaries were tricky.
#!/usr/bin/env python3
"""
001_fetch_flag.py
Connects to the remote service and queries successive indices to reconstruct the flag.
Prints progress with simple emojis and separators for readability.
"""
import socket
import re
import time
HOST = '94.237.51.6'
PORT = 42537
def recv_until(sock, timeout=2.0):
sock.settimeout(timeout)
data = b''
try:
data = sock.recv(4096)
except socket.timeout:
pass
except Exception:
pass
return data.decode(errors='ignore')
def main():
print('🔎 Connecting to service {}:{}...'.format(HOST, PORT))
with socket.create_connection((HOST, PORT), timeout=10) as s:
intro = recv_until(s)
if intro:
print('\n' + '='*60)
print(intro.strip())
print('='*60 + '\n')
flag_chars = []
i = 1
print('🚀 Starting index enumeration...')
print('-'*60)
while True:
tosend = f"{i}\n"
s.sendall(tosend.encode())
time.sleep(0.05)
resp = recv_until(s, timeout=1.5)
line = resp.strip().replace('\n', ' | ')
print(f'[{i:3d}] ➜ {line}')
if 'Index out of range' in resp:
print('\n⚠️ Index out of range reached. Stopping enumeration.')
break
m = re.search(r'Character at Index\s*\d+\s*:\s*(.)', resp)
if m:
ch = m.group(1)
flag_chars.append(ch)
else:
if ':' in resp:
ch = resp.split(':')[-1].strip().split()[0]
if ch:
flag_chars.append(ch[0])
i += 1
print('\n' + '='*60)
flag = ''.join(flag_chars)
print('🎯 Flag reconstructed (so far):')
print(flag)
print('='*60)
if __name__ == '__main__':
main()
/home/noigel/HTB/MISC/Character/002_fetch_flag_per_connection.py
This is the robust script used to gather the full flag. It opens a new connection for each index and is resilient to prompt state issues.
#!/usr/bin/env python3
"""
002_fetch_flag_per_connection.py
More reliable flag fetcher: opens a fresh TCP connection for each index query
so we don't have to track server prompt state. Prints progress and the final flag.
"""
import socket
import re
import time
HOST = '94.237.51.6'
PORT = 42537
def query_index(idx, timeout=5.0):
try:
with socket.create_connection((HOST, PORT), timeout=timeout) as s:
s.settimeout(1.0)
try:
_ = s.recv(4096)
except Exception:
pass
s.sendall(f"{idx}\n".encode())
s.settimeout(2.0)
data = b''
try:
while True:
chunk = s.recv(4096)
if not chunk:
break
data += chunk
except socket.timeout:
pass
return data.decode(errors='ignore')
except Exception as e:
return f"__ERR__ {e}"
def main():
print('🔎 Per-connection fetcher starting...')
flag_chars = []
idx = 1
while True:
resp = query_index(idx)
short = resp.strip().splitlines()
short = ' | '.join(short[:2])
print(f'[{idx:3d}] ➜ {short}')
if 'Index out of range' in resp:
print('\n⚠️ Reached end of flag indices.')
break
m = re.search(r'Character at Index\s*\d+\s*:\s*(.+)', resp)
if m:
token = m.group(1).strip()
if token:
ch = token[0]
flag_chars.append(ch)
else:
if ':' in resp:
part = resp.rsplit(':', 1)[-1].strip()
if part:
flag_chars.append(part[0])
idx += 1
time.sleep(0.05)
flag = ''.join(flag_chars)
print('\n' + '='*60)
print('🎯 Flag assembled:')
print(flag)
print('='*60)
if __name__ == '__main__':
main()
>Reproduction / How to run
From the Character directory run the robust script:
python3 002_fetch_flag_per_connection.py
This will connect to the remote service repeatedly, print progress lines for each index, and finally output the assembled flag.
If you prefer a single-connection attempt (less reliable), you can run:
python3 001_fetch_flag.py
Permissions: ensure the scripts are executable or run with python3 <script>.
Network: these scripts perform outbound TCP connections to 94.237.51.6:42537. Run them from a host with internet access and allowed outbound tcp connections.
>Notes & observations
-
The challenge is intentionally tedious: you can only request one character of the flag per query. The easiest approach is automated enumeration.
-
The per-connection model is robust against prompt synchronization issues and is the recommended method when the server drives the prompt state heavily.
-
The server appears to index characters starting at 1.
>Full interaction log (trimmed)
Example lines collected while enumerating:
[ 1] ➜ Character at Index 1: T
[ 2] ➜ Character at Index 2: B
[ 3] ➜ Character at Index 3: {
...
[103] ➜ Character at Index 103: }
[104] ➜ Index out of range!
Final assembled string:
TB{tH15_1s_4_r3aLly_l0nG_fL4g_i_h0p3_f0r_y0Ur_s4k3_tH4t_y0U_sCr1pTEd_tH1s_oR_els3_iT_t0oK_qU1t3_l0ng!!}
Strip the leading T (index 1) and B (index 2) ordering yields the full formatted flag shown at the top of this writeup.