We created a service which can read and print the flag for you. To
use the application, you first need to enter a valid product key. Can
you reverse the algorithm and generate a valid key?
Reversing
The binary reads a product key, validates it, and prints the flag
only if validation succeeds.
int __fastcall main(int argc, char **argv, char **envp) { int is_valid; char input_key[136];
setbuf(stdout, 0); memset(input_key, 0, 129); puts("Enter a valid product key to gain access to the flag:"); fgets(input_key, 128, stdin); input_key[strcspn(input_key, "\n")] = 0;
from z3 import * from z3 import BitVec, BitVecVal, If, Solver, ZeroExt, sat
s = Solver()
# 1. 定义 32 个 8-bit 的符号变量(代表字符) flag = [BitVec(f"flag[{i}]", 8) for i inrange(32)]
# 2. 施加字符集约束 ('@' to 'Z') for i inrange(32): # Integer Promotion converts c to a 32-bit integer = 8 bits(char) + 24 bits s.add(flag[i] >= 65, flag[i] <= 90)
# 3. 核心转换逻辑 (transform_char) deftransform(c): # z3 的 If 语法:If(条件, 真值, 假值) return If(c <= 77, ZeroExt(24, c) + 181, ZeroExt(24, c) + 177)
# 4. 累加校验和计算 # 使用 32-bit 位向量防止整数溢出,就像 C 语言里的 int 一样 # Integer Promotion converts c to a 32-bit integer = 8 bits(char) + 24 bits checksum = BitVecVal(247, 32) for i inrange(1, 32): checksum += transform(flag[i]) - i + 247
if s.check() == sat: model = s.model() # 提取计算出的字符并拼接 flag = "".join(chr(model[flag[i]].as_long()) for i inrange(32)) print(f"[+] Valid Product Key: {flag}")
Solver output:
1
BUYRSCLZHPATAQZSLJMJMKOOBFRAOVUX
Remote verification
1
nc 892dc593a381aaea.247ctf.com 50478
1 2 3 4
Enter a valid product key to gain access to the flag: BUYRSCLZHPATAQZSLJMJMKOOBFRAOVUX Valid product key! 247CTF{********************************}
An important USB drive containing sensitive information has been
encrypted by some new ransomware variant. Can you reverse the ransomware
encryption function and recover the files?
1. Initial Analysis
We are provided with a BitLocker-encrypted USB image
(encrypted_usb.dd) and a large list of potential recovery
keys (recovery_keys_dump.txt).
The goal is to find the correct recovery key, mount the image, and
then deal with the “ransomware” that has encrypted the files inside.
2. BitLocker Decryption
Attempting John the Ripper
First, I tried using bitlocker2john to extract the hash
and then cracked it with the provided recovery keys.
1 2 3 4
❯ bitlocker2john -i encrypted_usb.dd > hash ❯ john --wordlist=./recovery_keys_dump.txt --fork=6 hash ... 0 password hashes cracked, 4 left
John didn’t seem to find the key directly (possibly due to format
mismatch or configuration). Instead of debugging the hash format, I
moved to a more direct approach: brute-forcing the mount command using
dislocker.
Brute-forcing with Dislocker
I wrote a simple bash script to iterate through the
recovery_keys_dump.txt and attempt to mount the image.
❯ sudo mount /mnt/bitlocker_img/dislocker-file /mnt/unlocked_usb
3. Ransomware Analysis
Inside the mounted drive, we find several encrypted files and the
ransomware binary itself:
1 2 3 4 5 6 7 8 9
❯ ls -lh /mnt/unlocked_usb/ total 3.2M -rwxrwxrwx 1 root root 474K Oct 8 2022 crypto_passphrase.png.xxx.crypt -rwxrwxrwx 1 root root 15K Oct 8 2022 cryptor -rwxrwxrwx 1 root root 9.2K Oct 8 2022 do_not_open.png.xxx.crypt -rwxrwxrwx 1 root root 133K Oct 8 2022 meeting_minutes.png.xxx.crypt -rwxrwxrwx 1 root root 889K Oct 8 2022 passwords.png.xxx.crypt -rwxrwxrwx 1 root root 386 Oct 8 2022 ransom.txt -rwxrwxrwx 1 root root 1.7M Oct 8 2022 salary_screenshot.png.xxx.crypt
The ransom.txt claims to use a “secure XOR encryption
algorithm”.
1 2
Your files have been encrypted using a secure xor encryption algorithm and are completely unrecoverable! To decrypt your files, you need your secret encryption key.
4. Recovery (Known Plaintext
Attack)
Since the files are PNGs, we can perform a Known Plaintext
Attack. We know that PNG files always start with the same
8-byte magic header: 89 50 4E 47 0D 0A 1A 0A.
By XORing the first 8 bytes of an encrypted file with this known PNG
header, we can recover the XOR key.
Using CyberChef:
Input the first few bytes of
do_not_open.png.xxx.crypt.
XOR with the PNG magic bytes
89 50 4E 47 0D 0A 1A 0A.
The result reveals the key repeats as 66 63 6f 79
(ASCII: fcoy).
Applying the XOR key fcoy to the entire file
do_not_open.png.xxx.crypt recovers the original image
containing the flag.
247CTF{494f7cceb2baf33a0879543fe673blae}
5. Deep Dive: Reversing
the cryptor Binary
I was curious about how the binary actually worked, so I threw it
into IDA Pro.
Main Logic
The program expects a 4-character key as a command-line argument. It
then iterates through the current directory, looking for files with the
.xxx extension.
We have a honey pot running on one of our internal networks. We
received an alert today that the machine was compromised, but we can’t
figure out what the attacker did. Can you find the flag hidden in the
attacker’s payload?
我们在内部网络中运行了一个蜜罐。今天我们收到警报,显示该机器已被入侵,但我们无法确定攻击者做了什么。你能找到隐藏在攻击者有效载荷中的
flag 吗?
Network Traffic
The provided logs show SMB (Server Message Block) traffic on port 445
(microsoft_ds). The sequence of
SMBNegotiate_Request and
SMB2_Negotiate_Protocol_Request suggests an attempt to
exploit an SMB vulnerability.
1 2 3 4
0001 Ether / IP / TCP 192.168.10.168:microsoft_ds > 10.0.5.15:42799 SA / Padding 0003 Ether / IP / TCP 10.0.5.15:42799 > 192.168.10.168:microsoft_ds PA / NBTSession / SMB_Header / SMBNegotiate_Request ... 0203 Ether / IP / TCP 10.0.5.15:43947 > 192.168.10.168:microsoft_ds PA / NBTSession / SMB2_Header / SMB2_Negotiate_Protocol_Request / Raw
Payload Extraction
Following the TCP stream and exporting the raw data with Wireshark,
we get the following hex dump:
flag = [BitVec(f"flag[{i}]", 32) for i inrange(40)]
for i inrange(40): s.add(flag[i] >= 32, flag[i] <= 126)
for line in raw_js.strip().split("&&"): line = line.strip() ifnot line: continue
# Convert: A ^ (B == N) -> (A ^ B) == N if"^"in line: line = re.sub(r"(.*)\^\s*\((.*)\s*==\s*(-?\d+)\)", r"(\1 ^ (\2)) == \3", line)
s.add(eval(line))
if s.check() == sat: m = s.model() result = "".join([chr(m[flag[i]].as_long()) for i inrange(40)]) print(f"\n[+] Result: {result}") else: print("\n[-] Unsatisfiable.")
Notes
^ in JavaScript is bitwise XOR (not exponent), so
constraints must be modeled as XOR equations.
32-bit BitVec variables are used to match JavaScript
bitwise operation behavior.
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.
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#!/usr/bin/env python3 from scapy.allimport * from scapy.layers.inet import TCP
PCAP_FILE = "custom_protocol_log.pcap"
defprint_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:
Our WiFi keeps disconnecting. We captured wireless traffic to try and
figure out what’s happening, but it’s all temporal zeros to us! I think
someone is trying to exploit a WiFi vulnerability.. Can you decrypt the
traffic and gain access to the flag?
The hint “temporal zeros” and the context of a WiFi vulnerability
strongly suggest the KRACK (Key Reinstallation Attack),
specifically CVE-2017-13077.
Vulnerability Analysis: Why
“Zeros”?
In a standard WPA2 4-way handshake, the client and AP negotiate a
PTK (Pairwise Transient Key). KRACK works by
intercepting and replaying Message 3 of the handshake, forcing the
client to reinstall an already in-use key. This resets nonces (packet
numbers) and replay counters.
For certain versions of wpa_supplicant (notably 2.4 and
2.5), a critical implementation bug exists: when the key is reinstalled,
the Temporal Key (TK) is not just reused, but
cleared to all zeros.
The captured 802.11 CCMP packets are encrypted using a
16-byte key of \x00 values.
The WPA2 4-way Handshake &
PTK
Message 1: AP sends a random number
(ANonce) to the Client.
Message 2: Client generates its own random number
(SNonce), derives the PTK using both Nonces, and sends
SNonce to the AP.
Message 3: AP derives the same PTK, sends the Group
Temporal Key (GTK), and instructs the Client to install the PTK.
Message 4: Client confirms installation with an
ACK.
The KRACK attack manipulates Message 3 to trigger the “all-zero” TK
bug.
Decryption Methods
Method 1: Wireshark GUI
If you prefer a visual approach, you can configure Wireshark to
decrypt the traffic using the zeroed key:
Open Preferences
(Ctrl + Shift + P).
Go to Protocols -> IEEE 802.11.
Check “Enable decryption”.
Click “Edit…” next to Decryption keys.
Add a new key:
Key type: tk
Key: 00000000000000000000000000000000
(32 zeros)
Method 2: Scapy Script (CLI)
The following script manually reconstructs the CCM Nonce and decrypts
the packets using the zeroed Temporal Key.