Skip to content

Instantly share code, notes, and snippets.

@Pir00t
Created November 4, 2025 16:18
Show Gist options
  • Select an option

  • Save Pir00t/82928425d9726398278b94b745410369 to your computer and use it in GitHub Desktop.

Select an option

Save Pir00t/82928425d9726398278b94b745410369 to your computer and use it in GitHub Desktop.
Script to decode final stage of SANDY, Huntress CTF 2025
import re, base64, os
OUTDIR = "decoded_b64_out"
LOGPATH = os.path.join(OUTDIR, "decode_log.txt")
MIN_QUADS = 3 # minimum number of 4-char groups (>=12 chars)
b64_simple = re.compile(
r"(?<![A-Za-z0-9+/=])" # not preceded by base64 char
r"(?:[A-Za-z0-9+/]{4}){" + str(MIN_QUADS) + r",}" # at least MIN_QUADS groups
r"(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})?" # optional padding
r"(?![A-Za-z0-9+/=])" # not followed by base64 char
)
def normalize_and_pad(s):
s = re.sub(r'[\s\r\n]+', '', s)
# If invalid chars inside, keep them for attempt but also prepare filtered fallback
filtered = re.sub(r'[^A-Za-z0-9+/=]', '', s)
pad = (-len(s)) % 4
if pad:
s_padded = s + ("=" * pad)
else:
s_padded = s
return s, filtered, s_padded
def try_decode_b64(s):
s_orig, filtered, s_padded = normalize_and_pad(s)
candidates = [s_padded, s_padded + "==", s.rstrip('=') + ("=" * ((4 - (len(s) % 4)) % 4)), filtered]
last_err = None
for c in candidates:
if len(c) < 8:
continue
try:
# first try strict validate
b = base64.b64decode(c, validate=True)
return b, c
except Exception as e:
last_err = e
try:
b = base64.b64decode(c)
return b, c
except Exception:
pass
raise ValueError(f"base64 decode failed (orig len {len(s_orig)}): {last_err}")
def is_text(bytestr):
# prefer utf-16le then utf-8, else no
for enc in ("utf-16le", "utf-8"):
try:
txt = bytestr.decode(enc)
return True, enc, txt
except Exception:
pass
return False, None, None
def find_candidates(text):
matches = []
for m in b64_simple.finditer(text):
matches.append(m.group(0))
# dedupe while preserving order (noted 20 vs 11 in testing)
seen = set(); uniq = []
for s in matches:
if s not in seen:
seen.add(s); uniq.append(s)
return uniq
def save_blob(idx, bytestr, textstr, enc):
os.makedirs(OUTDIR, exist_ok=True)
if textstr is not None:
fname = f"decoded_{idx:03d}.txt"
path = os.path.join(OUTDIR, fname)
with open(path, "w", encoding="utf-8", errors="replace") as f:
f.write(textstr)
else:
fname = f"decoded_{idx:03d}.bin"
path = os.path.join(OUTDIR, fname)
with open(path, "wb") as f:
f.write(bytestr)
return path, fname
def main():
text = open("decoded.txt", "r", encoding="utf-8", errors="ignore").read()
candidates = find_candidates(text)
log_lines = []
log_lines.append(f"Found {len(candidates)} candidate base64 strings.")
idx = 0
for i, cand in enumerate(candidates, start=1):
try:
decoded_bytes, used = try_decode_b64(cand)
except Exception as e:
log_lines.append(f"[{i}] FAIL decode len={len(cand)}: {e}")
continue
is_txt, enc, txt = is_text(decoded_bytes)
idx += 1
path, fname = save_blob(idx, decoded_bytes, txt if is_txt else None, enc)
desc = f"[{idx}] saved {fname} ({len(decoded_bytes)} bytes)"
if is_txt:
desc += f" as text (encoding guessed: {enc})"
else:
desc += " (binary)"
desc += f" -- original_candidate_len={len(cand)} used_pattern_len={len(used)}"
log_lines.append(desc)
with open(LOGPATH, "w", encoding="utf-8") as lf:
lf.write("\n".join(log_lines))
print(f"Done. {idx} decoded blobs saved to {OUTDIR}. See {LOGPATH}.")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment