We are working on our own custom command and control protocol. Can you identify any hidden features in the service? We also included a packet capture of some old sessions so you can learn how it works.
我们正在开发自己的自定义命令与控制协议。您能发现该服务中有哪些隐藏功能吗?我们还提供了一些旧会话的数据包捕获文件,以便您了解其工作原理。
PCAP Investigation with Scapy
The first step was to examine the provided packet capture (custom_protocol_log.pcap) to understand the communication pattern. I used scapy to extract and decode raw data from the TCP streams.
#!/usr/bin/env python3from scapy.all import *from scapy.layers.inet import TCP
PCAP_FILE = "custom_protocol_log.pcap"
def print_hex(pcap_path): packets = rdpcap(pcap_path) for pkt in packets: if TCP in pkt and pkt.haslayer(Raw): raw = pkt[Raw].load try: # Attempt to decode or print as hex print(raw.hex()) except Exception as e: print(f"[-] Error: {e}")
if __name__ == "__main__": print_hex(PCAP_FILE)Protocol Reversal
By analyzing the hex data from the traffic between 172.17.0.1 and 172.17.0.2, a clear pattern emerged:
b925afc1 00 31 00 30 00 323430323435313235b925afc1 00 32 00 30 00 323032383630353038b925afc1 00 33 00 32 00 33383232383038323633The protocol structure appears to be:
[Session ID] [Null] [Counter] [Null] [Command] [Null] [Checksum]
- Session ID: A 4-byte identifier (e.g.,
b925afc1). - Counter: Increments with each request.
- Command: Hex-encoded numbers (e.g.,
30for0,31for1,32for2). - Checksum: A CRC32 calculation of the preceding bytes, where the result is converted to a decimal string and then hex-encoded.
Checksum Verification
Using Python to verify the CRC32 logic:
import zlib
def calc_custom_crc_hex(hex_data: str) -> str: data_bytes = bytes.fromhex(hex_data) crc_val = zlib.crc32(data_bytes) # Convert CRC32 integer to decimal string, then hex-encode that string return str(crc_val).encode().hex()
base_hex = "b925afc100310030"print(f"Payload: {base_hex}")print(f"Custom CRC Hex: {calc_custom_crc_hex(base_hex)}")# Output: 323430323435313235 (Matches the PCAP!)Solution
Exploit Script
The script enumerates the 15 single-byte command values 0x30..0x3e, corresponding to ASCII 0..9:;<=>. Encoding decimal strings 10..14 would produce different multi-byte fields and is not supported by the retained trace. CRC32 matches all three displayed request fixtures offline; the PCAP and remote command semantics have not been rerun.
#!/usr/bin/env python3import zlibfrom pwn import *
HOST = "35f9359cc67c7983.247ctf.com"PORT = 50385context.log_level = "info"
def calc_custom_crc_hex(hex_data: str) -> str: data_bytes = bytes.fromhex(hex_data) crc_val = zlib.crc32(data_bytes) return str(crc_val).encode().hex()
def solve(): sep = "00" counter = "31" # 原记录覆盖单字节 0x30..0x3e;0x3a..0x3e 是标点,不是十进制 10..14 commands = [bytes([value]).hex() for value in range(0x30, 0x3f)]
for cmd in commands:25 collapsed lines
conn = None try: conn = remote(HOST, PORT, timeout=5) # Receive Session ID session_id = conn.recvline(drop=True).decode(errors="replace") log.info(f"Session ID: {session_id} | Testing Command: {cmd}")
# Construct payload: [SessionID][00][Counter][00][Command][00][Checksum] base_hex = f"{session_id}{sep}{counter}{sep}{cmd}" crc_hex = calc_custom_crc_hex(base_hex) payload = f"{base_hex}{sep}{crc_hex}"
conn.sendline(payload.encode())
raw_response = conn.recvall(timeout=3).decode(errors="replace") try: # Server returns hex-encoded response decoded_response = bytes.fromhex(raw_response).decode(errors="replace") if "247CTF" in decoded_response: log.success(f"Flag found: {decoded_response}") conn.close() return except ValueError: log.warning(f"Failed to decode hex response: {raw_response}") print("-" * 40) except (EOFError, OSError, ValueError) as exc: log.warning(f"Command {cmd} failed: {exc}") finally: if conn is not None: conn.close()
if __name__ == "__main__": solve()Execution Output
[*] Starting brute-force with commands: ['30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e'][+] Opening connection to 35f9359cc67c7983.247ctf.com on port 50385: Done[*] Session ID: 9b2832a1[+] Receiving all data: Done (54B)[*] Closed connection to 35f9359cc67c7983.247ctf.com port 50385[*] Response: (2\x001\x00notroot \x003738085711----------------------------------------[*] Connection closed.[+] Opening connection to 35f9359cc67c7983.247ctf.com on port 50385: Done[*] Session ID: 3907e672[+] Receiving all data: Done (152B)[*] Closed connection to 35f9359cc67c7983.247ctf.com port 50385[*] Response: 9\x07r\x001\x00uid=1000(notroot) gid=1000(notroot) groups=1000(notroot) \x003051696603----------------------------------------[*] Connection closed.[+] Opening connection to 35f9359cc67c7983.247ctf.com on port 50385: Done[*] Session ID: edd0a465[+] Receiving all data: Done (96B)16 collapsed lines
[*] Closed connection to 35f9359cc67c7983.247ctf.com port 50385[*] Response: Фe\x001\x00Sun Mar 1 06:57:51 UTC 2026 \x003480231763----------------------------------------[*] Connection closed.[+] Opening connection to 35f9359cc67c7983.247ctf.com on port 50385: Done[*] Session ID: 705136bc[+] Receiving all data: Done (446B)[*] Closed connection to 35f9359cc67c7983.247ctf.com port 50385[*] Response: pQ6\x001\x00 total used free shared buff/cache available Mem: 7973384 1587756 3913000 2636 2472628 6135804 Swap: 2097148 0 2097148 \x002165499562----------------------------------------[*] Connection closed.[+] Opening connection to 35f9359cc67c7983.247ctf.com on port 50385: Done[*] Session ID: d0f8fe01[+] Receiving all data: Done (118B)[*] Closed connection to 35f9359cc67c7983.247ctf.com port 50385[*] Response: \x01\x001\x00247CTF{<flag-redacted>}\x001655731160[+] Flag found: \x01\x001\x00247CTF{<flag-redacted>}\x001655731160[*] Connection closed.
[Process exited 0]The returned flag belongs to the launched service instance; no cross-instance invariance is established. Record the generation/retrieval method and use the current instance response instead of a fixed-answer spoiler.