mobile solve

This commit is contained in:
2026-03-20 23:13:46 +01:00
parent 2b1546aa4f
commit 587a6fae98
62 changed files with 712 additions and 328 deletions

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""
Solve script for DNS 0x20 exfiltration challenge.
The webshell exfiltrates data via DNS by:
1. base64-encoding the content (with + and / replaced by -)
2. prepending a 3-char MD5 checksum
3. performing a DNS_A lookup for: {base64}.{checksum}.{attacker_domain}
DNS 0x20 encoding preserves the case of query labels in the response,
meaning the mixed-case payload in the DNS log IS the original base64.
However, intermediate resolvers may have corrupted some letter cases,
so we reconstruct the correct capitalisation by:
- splitting the payload into groups of 4 base64 characters
- trying all case variants per group
- keeping only variants that decode to printable ASCII
- finding the combination whose MD5[:3] matches the checksum in the log
"""
import base64
from hashlib import md5
from itertools import product
# ---------------------------------------------------------------------------
# Paste the data label and checksum from the 0x20-encoded DNS log entry here
# ---------------------------------------------------------------------------
PAYLOAD = "Su5tE1QwdDRSBHLfBJn2zXjFAgFWcdNUzWQTAxjSLtfLodhjMtUzfq"
CHECKSUM = "190" # first 3 hex chars of md5(flag)
# ---------------------------------------------------------------------------
def reverse_strtr(s: str) -> str:
"""The PHP replaces + and / with -. Reverse where possible (no-op if no dashes)."""
# If there are no dashes the base64 had neither + nor /, nothing to do.
# If there ARE dashes we'd need to try both — handled by the case-brute-force below.
return s
def case_variants(group: str):
"""Yield every capitalisation variant of a base64 group that decodes to printable ASCII."""
letter_positions = [(i, c) for i, c in enumerate(group) if c.isalpha()]
for bits in product([True, False], repeat=len(letter_positions)):
candidate = list(group)
for (idx, _), upper in zip(letter_positions, bits):
candidate[idx] = candidate[idx].upper() if upper else candidate[idx].lower()
s = "".join(candidate)
padding = "=" * ((4 - len(s) % 4) % 4)
try:
decoded = base64.b64decode(s + padding)
if all(32 <= b < 127 for b in decoded): # printable ASCII only
yield s, decoded
except Exception:
pass
def solve(payload: str, checksum: str) -> str:
payload = reverse_strtr(payload)
# Split into groups of 4 (standard base64 block size)
groups = [payload[i:i+4] for i in range(0, len(payload), 4)]
# Collect valid byte candidates for each group
group_candidates = []
for i, g in enumerate(groups):
cands = list(case_variants(g))
if not cands:
raise ValueError(f"No printable-ASCII candidate found for group {i} ({g!r})")
group_candidates.append([dec for _, dec in cands])
print(f" group[{i:02d}] {g!r:6s}{[dec for _, dec in cands]}")
print(f"\nSearching {' × '.join(str(len(c)) for c in group_candidates)} combinations "
f"for MD5[:3] == {checksum!r}\n")
# Enumerate combinations and check MD5 checksum
for combo in product(*group_candidates):
candidate_bytes = b"".join(combo)
if md5(candidate_bytes).hexdigest()[:3] == checksum:
return candidate_bytes.decode()
raise ValueError("No combination matched the checksum — check PAYLOAD / CHECKSUM values.")
if __name__ == "__main__":
print("=" * 60)
print("DNS 0x20 Exfiltration — Solve Script")
print("=" * 60)
print(f"Payload : {PAYLOAD}")
print(f"Checksum : {CHECKSUM}\n")
print("Candidate bytes per base64 group (printable ASCII filter):")
flag = solve(PAYLOAD, CHECKSUM)
print("=" * 60)
print(f"FLAG: {flag}")
print("=" * 60)