Абсолютная безопасность (Absolute Security)
Абсолютная безопасность (Absolute Security)
Platform: hackerlab | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-01-02 | Status: Solved Techniques: Statistical analysis of filtered XOR outputs, Missing byte detection through sampling, Information leak via output filtering, Byte-by-byte plaintext recovery
Summary
Service: 62.173.140.174:11000
Абсолютная безопасность (Absolute Security) — HackerLab
Description
"Я создал шифровщик флагов, который нельзя взломать" (I created a flag encryptor that cannot be hacked)
Service: 62.173.140.174:11000
Analysis
The service shows the encryption code:
python read-only • copy allowedasync def gen_enc_flag(): rand_bytes = urandom(23) enc = bytes(i ^ j for i, j in zip(flag, rand_bytes)) for i in range(23): assert enc[i] != flag[i], "leak detected" return enc.hex()
Vulnerability
The key vulnerability is in the check assert enc[i] != flag[i]:
- XOR property:
enc[i] = flag[i] ^ rand_bytes[i] - Rejection condition:
enc[i] == flag[i]occurs whenrand_bytes[i] == 0 - Information leak: The server rejects results where
rand_bytes[i] == 0
This means that for each position i:
- If
rand_bytes[i] == 0, thenenc[i] = flag[i] ^ 0 = flag[i]— rejected - All other values of
rand_bytes[i](1-255) — accepted
Statistical Attack
With enough samples, for each position:
- All 256 possible values of
enc[i]can appear, except one - The value
flag[i]will never appear at positioni(since such cases are rejected)
Formula: By collecting ~2700 samples, with high probability we will see all 255 "allowed" values for each position. The only missing value is the flag byte.
Mathematical Justification
Probability of NOT seeing a specific value after N samples:
- P(not seen) = (254/255)^N
- For N=2700: P ≈ (254/255)^2700 ≈ 2.4 × 10^-5
The probability of seeing all 255 values for a single position is very high when N≈2700.
Solution
python read-only • copy allowed#!/usr/bin/env python3 """ Абсолютная безопасность (Absolute Security) - HackerLab Crypto Statistical attack on filtered XOR oracle """ from pwn import * from collections import defaultdict HOST = "62.173.140.174" PORT = 11000 FLAG_LEN = 23 SAMPLES_NEEDED = 2700 # Enough to see all 255 possible values per position def collect_samples(): """Collect encrypted flag samples from the server""" samples = [] conn = remote(HOST, PORT) # Read initial banner/menu conn.recvuntil(b"choice") for i in range(SAMPLES_NEEDED): conn.sendline(b"1") # Request encrypted flag response = conn.recvline().decode().strip() # Parse hex-encoded ciphertext if len(response) == FLAG_LEN * 2: # 23 bytes = 46 hex chars enc_bytes = bytes.fromhex(response) samples.append(enc_bytes) if (i + 1) % 500 == 0: print(f"Collected {i + 1} samples...") conn.recvuntil(b"choice") conn.close() return samples def find_missing_bytes(samples): """For each position, find the byte value that never appears""" flag = bytearray(FLAG_LEN) for pos in range(FLAG_LEN): # Collect all seen values at this position seen = set() for sample in samples: seen.add(sample[pos]) # Find the missing value (should be exactly one) all_values = set(range(256)) missing = all_values - seen if len(missing) == 1: flag[pos] = missing.pop() print(f"Position {pos}: missing byte = {flag[pos]} ('{chr(flag[pos])}')") elif len(missing) == 0: print(f"Position {pos}: ERROR - all 256 values seen (need more samples)") else: print(f"Position {pos}: WARNING - {len(missing)} values missing: {missing}") return bytes(flag) def main(): print(f"[*] Collecting {SAMPLES_NEEDED} samples...") samples = collect_samples() print(f"[+] Collected {len(samples)} valid samples") print("\n[*] Analyzing missing bytes per position...") flag = find_missing_bytes(samples) print(f"\n[+] Recovered flag: {flag.decode()}") if __name__ == "__main__": main()
Alternative Solution (faster)
python read-only • copy allowed#!/usr/bin/env python3 """ Faster version - stop early when all positions have exactly one missing byte """ from pwn import * HOST = "62.173.140.174" PORT = 11000 FLAG_LEN = 23 def solve(): conn = remote(HOST, PORT) conn.recvuntil(b"choice") # Track seen values for each position seen = [set() for _ in range(FLAG_LEN)] sample_count = 0 while True: conn.sendline(b"1") response = conn.recvline().decode().strip() if len(response) == FLAG_LEN * 2: enc = bytes.fromhex(response) for i in range(FLAG_LEN): seen[i].add(enc[i]) sample_count += 1 conn.recvuntil(b"choice") # Check if we can determine the flag if sample_count % 100 == 0: missing_counts = [256 - len(s) for s in seen] if all(c == 1 for c in missing_counts): break print(f"Samples: {sample_count}, Missing per pos: {missing_counts[:5]}...") conn.close() # Extract flag flag = bytearray(FLAG_LEN) for i in range(FLAG_LEN): missing = (set(range(256)) - seen[i]).pop() flag[i] = missing return bytes(flag) print(solve().decode())
Output
[*] Collecting 2700 samples...
Collected 500 samples...
Collected 1000 samples...
Collected 1500 samples...
Collected 2000 samples...
Collected 2500 samples...
[+] Collected 2700 valid samples
[*] Analyzing missing bytes per position...
Position 0: missing byte = 67 ('C')
Position 1: missing byte = 79 ('O')
Position 2: missing byte = 68 ('D')
...
Position 22: missing byte = 125 ('}')
[+] Recovered flag: CODEBY{REDACTED}
Theoretical Background
The "Protection" Paradox
The author tried to prevent leaks by checking enc[i] != flag[i]. But this check:
- Rejects cases when
rand_bytes[i] == 0 - Creates a statistical anomaly — one byte never appears
- Allows flag recovery through analysis of missing values
Coupon Collector Problem
The task reduces to the "coupon collector problem":
- Need to collect 255 different values for each position
- Expected number of attempts: 255 × H(255) ≈ 255 × 6.1 ≈ 1555
- We take ~2700 with margin for high reliability
Title Irony
"Absolute Security" — an ironic name because:
- The attempt to "detect leaks" created a leak
- The "protection" became a vulnerability
- The flag
REDACTEDconfirms the irony