From dd458333228a0f67a011e8423334b78f9ced80ea Mon Sep 17 00:00:00 2001 From: asepharyana Date: Sun, 16 Aug 2026 21:55:22 +0700 Subject: [PATCH] CTF toolkit: lib (net, crypto_utils), templates per category, scaffold, cheatsheet, ps_and_qs example --- __pycache__/scaffold.cpython-312.pyc | Bin 0 -> 3705 bytes cheatsheets/CHEATSHEET.md | 63 ++++++++ examples/cipher | Bin 0 -> 512 bytes examples/ps_and_qs.py | 43 ++++++ examples/pub1.pub | 14 ++ examples/pub2.pub | 14 ++ lib/__init__.py | 0 lib/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 112 bytes lib/__pycache__/crypto_utils.cpython-312.pyc | Bin 0 -> 5838 bytes lib/__pycache__/net.cpython-312.pyc | Bin 0 -> 7984 bytes lib/crypto_utils.py | 133 +++++++++++++++++ lib/net.py | 140 ++++++++++++++++++ scaffold.py | 62 ++++++++ .../crypto_skeleton.cpython-312.pyc | Bin 0 -> 803 bytes .../forensics_skeleton.cpython-312.pyc | Bin 0 -> 1353 bytes .../__pycache__/misc_skeleton.cpython-312.pyc | Bin 0 -> 2664 bytes .../__pycache__/pwn_skeleton.cpython-312.pyc | Bin 0 -> 2373 bytes .../__pycache__/re_skeleton.cpython-312.pyc | Bin 0 -> 2303 bytes .../__pycache__/web_skeleton.cpython-312.pyc | Bin 0 -> 1122 bytes templates/crypto_skeleton.py | 26 ++++ templates/forensics_skeleton.py | 35 +++++ templates/misc_skeleton.py | 57 +++++++ templates/pwn_skeleton.py | 55 +++++++ templates/re_skeleton.py | 65 ++++++++ templates/web_skeleton.py | 29 ++++ 25 files changed, 736 insertions(+) create mode 100644 __pycache__/scaffold.cpython-312.pyc create mode 100644 cheatsheets/CHEATSHEET.md create mode 100644 examples/cipher create mode 100644 examples/ps_and_qs.py create mode 100644 examples/pub1.pub create mode 100644 examples/pub2.pub create mode 100644 lib/__init__.py create mode 100644 lib/__pycache__/__init__.cpython-312.pyc create mode 100644 lib/__pycache__/crypto_utils.cpython-312.pyc create mode 100644 lib/__pycache__/net.cpython-312.pyc create mode 100644 lib/crypto_utils.py create mode 100644 lib/net.py create mode 100644 scaffold.py create mode 100644 templates/__pycache__/crypto_skeleton.cpython-312.pyc create mode 100644 templates/__pycache__/forensics_skeleton.cpython-312.pyc create mode 100644 templates/__pycache__/misc_skeleton.cpython-312.pyc create mode 100644 templates/__pycache__/pwn_skeleton.cpython-312.pyc create mode 100644 templates/__pycache__/re_skeleton.cpython-312.pyc create mode 100644 templates/__pycache__/web_skeleton.cpython-312.pyc create mode 100644 templates/crypto_skeleton.py create mode 100644 templates/forensics_skeleton.py create mode 100644 templates/misc_skeleton.py create mode 100644 templates/pwn_skeleton.py create mode 100644 templates/re_skeleton.py create mode 100644 templates/web_skeleton.py diff --git a/__pycache__/scaffold.cpython-312.pyc b/__pycache__/scaffold.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5e16f2f36bcde4546b9b23b8ad82645fe78f726 GIT binary patch literal 3705 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 + string-length change lets you inject into serialized object. Bypass validation + with arrays (`nickname[]=`). +- **Read provided source/PHP** — vuln is almost always visible. SQLi/filter bypass/ + SSRF/LFI derive from the source, not black-box. +- **Tools**: requests, beautifulsoup, burp, sometimes selenium. + +## BINARY / PWN +- **Leak first**: format string `%p %p %p...` or GOT leak, then ROP. +- **ret2libc**: leak libc base -> one_gadget / system("/bin/sh"). +- **Stack overflow + CET (smash)**: emulator CET config block at fixed offset from + libc; write 0 to disable, then free ROP. +- **Arbitrary write primitive**: overwrite saved RBP to control a later frame pointer. +- **Debugger harness**: script a remote debugger (breakpoints, read/mod registers) + to dump memory (registers_matter). +- **Tools**: pwntools (remote/ELF/ROP/context), gdb+gef/pwndbg, checksec, ROPgadget. + +## RE (reverse engineering) +- **Static-first**: IDA/Ghidra; extract `.rodata` bytes -> often just RSA params. +- **RSA-from-dump (reversing_is_amazing)**: parse `db XXh` lines -> `RSA.importKey` + -> decrypt given ciphertext. +- **Symbolic execution**: angr to reach a "win" state, avoiding "fail" states. +- **Emulation / patching**: unicorn to emulate a function; lief to patch binaries. +- **Tools**: IDA, ghidra, radare2, lief, pyelftools, angr, unicorn, capstone. + +## MISC +- **Oracle byte-by-byte (heXdump)**: `xxd -r -ps` does NOT truncate -> overwrite 1 + byte, match output, recover flag char-by-char over CHARSET. +- **Encoding chains**: brute b64/b32/b16/hex until "flag"/"CTF" appears. +- **PRNG reversing (xor_and_shift)**: linear PRNG over GF(2) -> symbolic exec + + matrix exponentiation in sage to "jump" the state. +- **Constraint solving**: z3 when inputs must satisfy arithmetic conditions. + +## FORENSICS +- **PCAP**: tshark/scapy to extract streams; look for exfil/TLS keys. +- **Memory**: volatility (imageinfo, pslist, dump). +- **Stego**: PIL for pixel work; binwalk for appended data; audio via spectrogram. +- **Tools**: scapy, tshark/wireshark, volatility, PIL, binwalk. + +## CRYPTO (bonus — most common, 123 challenges in p4) +- **RSA recover n (lost_modulus)**: have e,d,ipmq=inv(p,q),iqmp=inv(q,p), not n -> + derive quadratic in phi -> `gmpy2.iroot` -> p,q. (lib.crypto_utils.recover_n_from_keys) +- **Common modulus**: same m, same n, coprime e -> CRT combine. +- **Wiener**: small d -> continued fractions on e/n. (lib.crypto_utils.wiener) +- **Håstad broadcast**: same small m^e across moduli with small e -> CRT + e-th root. +- **Lattice/LLL**: small roots, Coppersmith, hidden-number problem. +- **Reduced-round block cipher (a2s)**: differential cryptanalysis; 2^16-bit DeltaSet. +- **Tools**: pycryptodome, gmpy2, sage, numpy, z3, angr (rare). + +## QUICK SETUP +pip install pwntools pycryptodome gmpy2 requests beautifulsoup4 pillow scapy +# + sage, z3-solver, angr, capstone, unicorn, lief (as needed) diff --git a/examples/cipher b/examples/cipher new file mode 100644 index 0000000000000000000000000000000000000000..12fe8c418a458ad2ed09954c24cb93aa1ac3a517 GIT binary patch literal 512 zcmV+b0{{J?P2af77|)9$=$PluY9LUu_cG*SxrfbJBw${wXU5XbE|uzNf|;(u)3mu% zO>8i$Z68(0Fx#}Kz1J`br5w-*65Av!i&&K-IPv3jImb=FWB6Q2m&K>ung2|> zLn69+&y1FOTup^w-J)Q0RA5s%qKw=O1a48Rf{tkuScM6`9+Z-=Lf{;%Y6AfzP0HHq z`{+P{AlDOY^F#wk30sMl!=o^4OSbTpoPrTI#!ek>w!5qL1t}J5cYbfcD*geKXf`7A zLI8IOeR+p?Z3gxM`6Cv0G!7_;DDIk8=H+@{lr=_*B`0Mlo>sW7p6qdnte{U-ZmbIP z!w-feK=vEj54+hZb;$TTc($!rZNq7KRsWK*BDIA9e~VZVmj}jPA}roCJ*f2XShtNO zRQ+N~BHvbgQT(i5QAvm>$91BndS1Y$;R-)8ohv><&lYATix*C-6i^+B|Eg(2z*+bk z0L&@)C+|p~nas&d)3taBB=TH#$X&3uescn7PRUu$YE>RRz2ht2;HHe_|NsaXRVkS`a0;t;7{A CNc4sP literal 0 HcmV?d00001 diff --git a/examples/ps_and_qs.py b/examples/ps_and_qs.py new file mode 100644 index 0000000..996f3e9 --- /dev/null +++ b/examples/ps_and_qs.py @@ -0,0 +1,43 @@ +""" +EXAMPLE: SECCON 2017 Quals — "Ps and Qs" (Crypto, 200p) +https://github.com/p4-team/ctf/tree/master/2017-12-09-seccon-quals/crypto_ps_and_qs + +VULN: Two RSA public keys (pub1.pub, pub2.pub) share a prime (common factor). +FACT: gcd(n1, n2) = p -> recover q1 = n1/p, q2 = n2/p -> private keys -> decrypt. + +Run: + cd /home/code/ctfkit + python3 examples/ps_and_qs.py +Expected flag: SECCON{1234567890ABCDEF} +""" +import sys +from Crypto.PublicKey import RSA +from Crypto.Util.number import long_to_bytes + +sys.path.insert(0, "/home/code/ctfkit") +from lib.crypto_utils import gcd, modinv + +HERE = __file__.rsplit("/", 1)[0] + + +def main(): + pub1 = RSA.importKey(open(f"{HERE}/pub1.pub").read()) + pub2 = RSA.importKey(open(f"{HERE}/pub2.pub").read()) + p = gcd(pub1.n, pub2.n) + q1 = pub1.n // p + q2 = pub2.n // p + assert p * q1 == pub1.n and p * q2 == pub2.n, "common-factor failed" + msg = int.from_bytes(open(f"{HERE}/cipher", "rb").read(), "big") + + d1 = modinv(pub1.e, (p - 1) * (q1 - 1)) + pt = long_to_bytes(pow(msg, d1, pub1.n)).decode(errors="replace") + import re + m = re.search(r"SECCON\{[^}]+\}", pt) + flag = m.group(0) if m else pt + print("shared prime p =", p) + print("FLAG =", flag) + return flag + + +if __name__ == "__main__": + main() diff --git a/examples/pub1.pub b/examples/pub1.pub new file mode 100644 index 0000000..08921c6 --- /dev/null +++ b/examples/pub1.pub @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAz8+77qffFDqKwgixqh0v +hlRaxMtYjJSj+xwUrZGk8Lk2FXxaS4acGKi4ZPRya/j83AIMtBBCuslnhKt9A/k3 +SUfvsLw9Zlgxl0NAFZ/8PbfI50tjkP2m7sMLgcb/Yk6NP1sXv7elx//Y7PTmUYs5 +Or793Q+uukMIdGumP4EGtZ1+BYlDoAExp9TlOMRksnBXdkftvEeMwc6Vhe/odzBb +OnwufETbVHXt2tw0WiyQqUZ3HKwKRUzby0YfKEDnYTyD6c7MlAN/oJu52qPxgFYs +Ad8L5sUfDAbo8OLW4aXlDQoow4gRQHcKn0WTQUa381m5Oc4j8PpQem9ORUVxQwlS +ADwg8dl6ZxQLbl/L+zs3bk4klprrHUic/HKvTxWkeIoaqXyJdW0dTZSqR+fNOoGu +y5JEjMksd9LvV2qg28E1CGKszdrdvOgDV/DNW4VN0PjEYn/ktxiyTs/hHtJMO+Iv +AGQ7vtTuXjRa8Xblt20jovgODsbzTlcYxipw/lVwwouAe0TyLq3r2bX/kG9qhb6I +wMj25fiApR8X+E2xwu7+qK80BAREztGjffDk9fcsw/ULfkJ8jC2LYYbq12LwxESz +yjoBA+0SqTvOnK50eaIp67wKZI6qb5flBRpm6wnr1zSOkvdfEl69w2fip9Had1nU +H64uJjW/S3p/kb7Ks6x9Bb0CAwEAAQ== +-----END PUBLIC KEY----- diff --git a/examples/pub2.pub b/examples/pub2.pub new file mode 100644 index 0000000..66f466b --- /dev/null +++ b/examples/pub2.pub @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuzPMf8yOyvO/ntlcWDeS +4exrgO6HXsIGTbzwdZXINEkjv1NlJNTgp1V0x3mMc7GX3SsbQgVLHknLRfvwTm8R +TPijZcPfNkVST3eCaAOKP6JoAunR7b+7Xt+1oMN1Nw1/EPV9q71Pdx2tNjLwG5vO +EEiZZu6ILasXozt4aqX3MWWlQFEwCx35KAOSo+3p0/ycTYpqBjUfbvNZjo3is507 +Ga9koXFs0Vgmw/JMsT3rciw6A+8dK+LQpabiEP9dAYNnvjv5nqJroAblFkpN1Vqr +zUSd5c4YZIJdwWDlDVCesOb+cj7xgmge3blAhLg+yeLpQ+h8uHUJqw/Zscoiwc6v +85/Kz2cp/A4FeGcNh9fw+cy+Ccs+Es64lVcqmXnRC/2/r6JgVo2NsYS+ErPjGT4H +cpzjwdnNgoPtaYOgY4gDagpwKU8jOSlEd4KA596fYBY6gVDjD/Sk6gJ5LL6DBbqi +6Zr+UeF9r8Vr4NOEFHvNOOnRKTTscSYiIXdzpLOFGpsMbHw+AfYRGh4aVX9OKuSi +R86bdczMsYGYJfMFSqHAVb0+I0AJOuLvHQ+loXaCXv33lQcCf1EECAAJFC8NQ+Lx +DPrSIIE7u5AU1PQyXtrFOPtegrdT4q07JGB9c4CqZPy5i1nqi1pza4CTgySM7OCx +clXqVZ6QEn93ivbX6KZtrZECAwEAAQ== +-----END PUBLIC KEY----- diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/__pycache__/__init__.cpython-312.pyc b/lib/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2dca0e9d29cd00d4093c10caef127d746eb47c1c GIT binary patch literal 112 zcmX@j%ge<81kHOJvq1D?5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!;?K!U(vOeN%*!l^ lkJl@x{Ka9Do1apelWJGQ3Y2FA;$jfvBQql-V-Yiu1pt4Z6`=qC literal 0 HcmV?d00001 diff --git a/lib/__pycache__/crypto_utils.cpython-312.pyc b/lib/__pycache__/crypto_utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f9e36800b9dcd0976df9ad2e97ab189553f9335 GIT binary patch literal 5838 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 0: + r, correct = gmpy2.iroot(delta, 2) + if correct: + for x in [(-b - r) // (2 * a), (-b + r) // (2 * a)]: + if gmpy2.is_prime(x + 1): + q = x + 1 + p = possible_phi // x + 1 + return int(p), int(q) + return None + + for phi in find_phi(e, d): + res = solve(ipmq, iqmp, phi) + if res: + return res + return None + + +def common_modulus_attack(c1, c2, e1, e2, n): + """Same message encrypted with same n, coprime exponents.""" + g, a, b = egcd(e1, e2) + if g != 1: + raise ValueError("e1,e2 not coprime") + if a < 0: + c1, a = modinv(c1, n), -a + if b < 0: + c2, b = modinv(c2, n), -b + m = (pow(c1, a, n) * pow(c2, b, n)) % n + return m + + +def hastad_broadcast(cts, es, n, mlen=1): + """CRT-combine same small message raised to small exponents e across moduli. + cts[k] = m^es[k] mod n[k]. Returns m if m^max(e) < n_prod.""" + from functools import reduce + N = reduce(lambda a, b: a * b, n) + result = 0 + for c, ni in zip(cts, n): + Ni = N // ni + result = (result + c * Ni * modinv(Ni, ni)) % N + k = max(es) + return int(round(result ** (1.0 / k))) + + +def wiener(e, n): + """Wiener's attack: small d. Returns d or None.""" + def cf(a, b): + while b: + yield a // b + a, b = b, a % b + def convergents(cf_gen): + h0, h1 = 0, 1 + k0, k1 = 1, 0 + for q in cf_gen: + h0, h1 = h1, q * h1 + h0 + k0, k1 = k1, q * k1 + k0 + yield h1, k1 + for k, d in convergents(cf(e, n)): + if k == 0: + continue + if (e * d - 1) % k == 0: + phi = (e * d - 1) // k + s = n - phi + 1 + disc = s * s - 4 * n + if disc >= 0: + r = isqrt(disc) + if r * r == disc and (s + r) % 2 == 0: + return d + return None diff --git a/lib/net.py b/lib/net.py new file mode 100644 index 0000000..2e66e23 --- /dev/null +++ b/lib/net.py @@ -0,0 +1,140 @@ +""" +lib/net.py — Connection helpers (p4-team style: nc / receive_until_match / send). + +Works with pwntools if installed; otherwise falls back to a raw-socket +implementation so your solvers run even on a bare Python. +""" +import os +import re +import socket +import sys +import time + +try: + from pwn import remote, context, p64, u64, ELF, ROP # noqa + HAVE_PWN = True +except Exception: + HAVE_PWN = False + + +class Conn: + """Thin wrapper around pwntools.remote, or a raw socket if pwntools is missing.""" + + def __init__(self, host, port, timeout=10, use_pwntools=True): + self.host = host + self.port = port + self.timeout = timeout + self.use_pwntools = use_pwntools and HAVE_PWN + if self.use_pwntools: + context.log_level = os.environ.get("CTF_LOG", "info") + self.s = remote(host, port, timeout=timeout) + else: + self.s = socket.create_connection((host, port), timeout=timeout) + self._buf = b"" + + # ---- low level ---- + def recv_raw(self, n=4096): + if self.use_pwntools: + return self.s.recv(n) + data = b"" + try: + while len(data) < n: + chunk = self.s.recv(n - len(data)) + if not chunk: + break + data += chunk + except socket.timeout: + pass + return data + + def recv_until(self, marker, timeout=None): + """Receive until `marker` (bytes) appears. Returns everything including marker.""" + if self.use_pwntools: + return self.s.recvuntil(marker) + marker = marker.encode() if isinstance(marker, str) else marker + end = time.time() + (timeout or self.timeout) + buf = self._buf + while marker not in buf and time.time() < end: + try: + self.s.settimeout(max(0.1, end - time.time())) + chunk = self.s.recv(4096) + if not chunk: + break + buf += chunk + except socket.timeout: + break + self._buf = b"" + return buf + + def recv_until_match(self, pat, timeout=None): + """Receive until regex `pat` matches. Returns the matched prefix+match.""" + if self.use_pwntools: + return self.s.recvline_regex(pat) if hasattr(self.s, "recvline_regex") else self.s.recvuntil(pat.encode()) + rx = re.compile(pat.encode() if isinstance(pat, str) else pat) + end = time.time() + (timeout or self.timeout) + buf = self._buf + while time.time() < end: + m = rx.search(buf) + if m: + self._buf = buf[m.end():] + return buf[:m.end()] + try: + self.s.settimeout(max(0.1, end - time.time())) + chunk = self.s.recv(4096) + if not chunk: + break + buf += chunk + except socket.timeout: + break + return buf + + def send(self, data): + data = data.encode() if isinstance(data, str) else data + if self.use_pwntools: + self.s.sendline(data) if data.endswith(b"\n") else self.s.send(data) + else: + self.s.sendall(data) + + def sendline(self, data): + data = data.encode() if isinstance(data, str) else data + if self.use_pwntools: + self.s.sendline(data) + else: + self.s.sendall(data + b"\n") + + def interactive(self): + if self.use_pwntools: + self.s.interactive() + else: + print("[!] interactive needs pwntools; dropping to manual mode") + import select + while True: + r, _, _ = select.select([self.s, sys.stdin], [], []) + if self.s in r: + print(self.recv_raw(4096).decode(errors="replace"), end="") + if sys.stdin in r: + self.s.sendall(sys.stdin.readline().encode()) + + def close(self): + try: + self.s.close() + except Exception: + pass + + +def nc(host, port, timeout=10): + """Drop-in for the p4 pattern `nc(host, port)`.""" + return Conn(host, port, timeout=timeout) + + +# convenience re-exports for templates +def receive_until(s, marker, timeout=None): + return s.recv_until(marker, timeout) + + +def receive_until_match(s, pat, timeout=None): + return s.recv_until_match(pat, timeout) + + +def send(s, data): + s.send(data) diff --git a/scaffold.py b/scaffold.py new file mode 100644 index 0000000..97a853f --- /dev/null +++ b/scaffold.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +""" +scaffold.py — generate a new CTF / task skeleton (p4-team style). + +Usage: + python3 scaffold.py new hitcon "HITCON CTF 2024" 5 700 + python3 scaffold.py add hitcon "Safe Bank" pwn +""" +from pathlib import Path +from datetime import datetime +import argparse +import re + +TEMPLATES = { + "pwn": "pwn_skeleton.py", + "crypto": "crypto_skeleton.py", + "web": "web_skeleton.py", + "re": "re_skeleton.py", + "reverse": "re_skeleton.py", + "forensics": "forensics_skeleton.py", + "misc": "misc_skeleton.py", +} + +ROOT = Path(__file__).parent + + +def new_ctf(args): + stamp = datetime.now().isoformat()[:10] + dirname = f"{stamp}-{args.slug}" + newdir = ROOT / dirname + newdir.mkdir(exist_ok=True) + (newdir / "README.md").write_text(f"# {args.name}\n\n### Table of contents\n") + print(f"[+] created {dirname}/") + + +def add_task(args): + matches = sorted(ROOT.glob(f"*-{args.slug}")) + if not matches: + print("no such CTF slug"); return + d = matches[-1] + slug = re.sub(r"[^a-z0-9]+", "-", args.task.lower()) + taskdir = d / slug + taskdir.mkdir(exist_ok=True) + tmpl = TEMPLATES.get(args.category, "pwn_skeleton.py") + src = (ROOT / "templates" / tmpl).read_text() + (taskdir / "solve.py").write_text(src) + rd = d / "README.md" + rd.write_text(rd.read_text() + f"* [{args.task} ({args.category})]({slug})\n") + print(f"[+] added {taskdir}/solve.py") + + +def main(): + p = argparse.ArgumentParser() + sub = p.add_subparsers(dest="cmd", required=True) + n = sub.add_parser("new"); n.add_argument("slug"); n.add_argument("name"); n.add_argument("place"); n.add_argument("teams"); n.set_defaults(func=new_ctf) + a = sub.add_parser("add"); a.add_argument("slug"); a.add_argument("task"); a.add_argument("category"); a.set_defaults(func=add_task) + args = p.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/templates/__pycache__/crypto_skeleton.cpython-312.pyc b/templates/__pycache__/crypto_skeleton.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d948b76ca5c57934a04d3b9ea91a6e578004230e GIT binary patch literal 803 zcmZ8fO>5LZ7@kS8yJ>b8OGQK}WlqwCb-f7IB0?z^1X)YfgSiZ|$=ltynVB$|C@Bb4 z6#M~#2Y-R|H~0&@6niokzfRt&)k{y#q+1XN=9zcick+HbPd;?J0W$0D`)7~lU4*{d z%jt4v#?c*Pyg@MvQH*0J#oP&9Y;8C7cq44^X4vGdu*Ln*H-7I03g;k*8`EWkl#zJV=uMSys9K3IMRSN| zbk&EE1=)SHXu!yTpCXOxh=JJIo7vBC&mAThVYGAJvPTk84|Ou_2~D^$N}hv45jh32rLeq@67v7&vq>#y_0~_WpDbUiyrN2Wa@K c*~Zro7m?R_we!)v@X7OEZXCMEyIAM@3y1;bssI20 literal 0 HcmV?d00001 diff --git a/templates/__pycache__/forensics_skeleton.cpython-312.pyc b/templates/__pycache__/forensics_skeleton.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff7fb13e91bf81d1b2d465701e1eb3cb74d276f2 GIT binary patch literal 1353 zcmZ8h&u<$=6rSEZ@M&e+k9*k6};Z{xvRpLM3FX)9dAku9P5uCUgazM(789QzSPrGm4d-Lskv+sSo z&r&H3aC!REm-l@cfIoyYl-P-JROiNHV1Y8QpcQkV8!N|LsVup2S$37O;>OEyDE1^r zEvs0)pB|<-U3ql;)Ein&ZdkI=WzAA8*4racGes4cQrXMuWWmL5tr+{XIx8@i&Ttr#J!3)SIhUsAbT&2PA zT&2M;hOcUYX#^-R2*p=960iG(yrvcFh8uA3Y?b&fnyKMWtk4Z+@(YO7Frt`j+9pOJ z#T4DE*DW+h&|E<8p{<&2){y1%`0A~@h>_4U4N-;qsNsjm-^65#*bJj<{bL}5Apb2BoxF}8vLIvy?N8Cmyb{GcGfEL` zVtW(op~q||%KpdIU4xmmNT%4cqJ+?n?cvqt_=9bI?)CRBO~3K^aT4}V1|O~pFE2Njm$8S5 zZ4Oz$TOww$z`4Tv?%_J?cbg9o6FdYBdW#kpQN^HGw5f0mw~yhw%>-qkxxuHGk45Uz z2iNbEigzO=AhyTyYBVO`y6JnKPB9CEXk;)fz3N5D{`B;K;Hq7Zl)e`whWR6CMl!>c zQI&TJ4di%=Va`U*BRXI83HGRMQhiALLePk^g35ZO!7siJijf@8Fvf5C1y^lwn4D-A z_q9}aZC9Iok$r7vZ7+MNlQ_ss{0&A%#yi*hu_w2`{d6xo)48!fmhIN}#-{euxt+{6 zT30zp=eqb&@K#DuCjxMIp~{v9A9B$T<-`%hhq>ZP&L#2fst6 zxGUI7m$~Xm5W>G=DwO|81NagA6@2&-qz<+8Z`#FO?c$Sj-(UFd!md{M`C4&TE4J06 Z_!!Lerh%O7G@nc759LHp0rF(O(7%3Jc%c9Q literal 0 HcmV?d00001 diff --git a/templates/__pycache__/misc_skeleton.cpython-312.pyc b/templates/__pycache__/misc_skeleton.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ebf28b85a468c840995c5583a3c5adfe2d74fc99 GIT binary patch literal 2664 zcmcIm&2JmW6`$EJk}FbQ6m8j#m2G7^Vk^^?Z8&Y>x>YSYO6m%Ml@^K6bg|qWQY$TY znc1N%sY`$89txfMX4jPJ#6S*7 z2IQNWowx7vH}CD=G8q-YNPY0;+@^xizxbvz#7B$$MOdsO3(X)4TY`gUa5M`uf+fC% zW<(;)B|9%2&G0mmee{H{mf>$noX#j#(vsi8GYOKkl=%^aXi`8VWhEY9&WX@(@RE>E z)kWBl&7+3tkWr1e zrf1o1WfTrb#T(U1w9ZIgRcn{EXR5YuJ_B+V2=z5Xb3FE(R$5}@d}#@Oj69N}jm(l) zt$MYVKa!_0YMb-^sQU7)sTVX#xQIxNT{SACqs;VNpHahhndW=W0x%S`X+pHJM~Um( zrmuHAD%6%#c&f2kqE(1XKzc{}EOBat7SzvpNT$q3vEds88ACiv`~L>Ajw+~u1Jn@M zA&-SSv7A7!zl=~rw4$7q1xte1BbXH{5zR>}#pkjFoIgP5mjZB04LOAb6v%?9cMANXO86aP3 z_5kPX=A;J*SUGm(8?kci%2%;6_c<$P$>L*HN^l+}U=kL951{dQ(gg_6lfd4&7Dw2L z<1B_UuQ7g@C{wQribLhAcybKv^0_+z6Y_rbUiJR?UnVyvAH1|P^lURug3rC-x54MI zzy8XNycEjRa4RH~9q%@wp<>i(#I?dyanU3-W_xZv5lSV;D}@P4{JO*ZP_hhWgjf$H z*Qk&c5}x6BFfCom z^56a8g@VbW+8Mm+@%{dLK-LjdWib!}aRJjCP)~6ncHq#EEZmTffCbeTu1-UVxdMfE z$_{TJ@h<7()qbd$GDa?jdMW$K0IK>j#I_-(P#{jjKak@+JGALYBh=g261;kYbLYk7 zn{m6;%kD^xlCr&+hnB|b)a7l_XGX~(8ZUK@?egA6p^@<(28R`Ufw)m#>_|?MH z0saxx#>Dl>@mtfyS3VQRTei2&T z6zV**Ts;;-FG7$wzWg_V0JhS{o5hyey+*dxQ@h!x?*4oyJG_!?^$x7apY#pxAu)Aq zWqh}1@NRCS@7{@>p5cwfot_J=!4qrpZf~xgfb)k*)SGLi`_`uK+*~QbqOG8GU-M>L zMyc+#+?{R^mCUS=)pYwf>grh|AE~F>gAq$y@she;h32H|%V%ovsQ|I!O+aJBoOSIt z>d?JhEBbiRY^K?69v|RFUJVnvZh5Az$5mK~gB@L-Fk|?pZR_v_#NcZK%7tCAnNRcJ za}tS}vV=qGUAywisVihU%=w@66qRR>Wr|MaChg_pq;dS1x{AnP7d5j+l z3YPY}5$^s6%Iqckakf2*y81p=hql$Bjbnd2{fE=rYGG@vxUCkOiG8I9_qER;DZR4% Zk$7TPJ-szFv7;8Zq+(k{(#a?q{{vEHi^%{0 literal 0 HcmV?d00001 diff --git a/templates/__pycache__/pwn_skeleton.cpython-312.pyc b/templates/__pycache__/pwn_skeleton.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a7d6422d8b7bd6581f0586b338dc4fa4b69dd2e GIT binary patch literal 2373 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 step 1 + 2. Build ROP / overwrite -> step 2 + 3. Get shell / read flag -> step 3 +""" +from lib.net import nc, receive_until, receive_until_match, send, sendline # noqa +from pwn import * # noqa (ELF, ROP, p64, u64, context) + +context.log_level = 'info' +context.arch = 'amd64' # or 'i386' + +HOST, PORT = "challenge.host", 1337 +# binary = ELF('./challenge') +# libc = ELF('./libc.so.6') + + +def step1_leak(s): + """Leak libc/stack/PIE base. Adapt to the vuln (format string shown here).""" + s.recv_until(b"name > ") + # classic format-string leak + sendline(s, b"%9$p|%11$p") + line = s.recv_until(b"\n") + leak = int(line.split(b"|")[0], 16) + log.info("leak = %#x", leak) + return leak + + +def step2_exploit(s, leak): + """Construct payload. Fill with your gadgets/ROP.""" + payload = b"A" * 40 # padding to saved RIP + payload += p64(leak) # example: overwrite with leaked addr + # payload += rop.chain(...) + s.recv_until(b"message > ") + sendline(s, payload) + + +def step3(s): + s.recv_until(b"$ ") # shell prompt, or just: + sendline(s, b"cat flag*; cat /flag*") + print(s.recvall(timeout=3).decode(errors='replace')) + + +def main(): + s = nc(HOST, PORT) + leak = step1_leak(s) + step2_exploit(s, leak) + step3(s) + s.close() + + +if __name__ == "__main__": + main() diff --git a/templates/re_skeleton.py b/templates/re_skeleton.py new file mode 100644 index 0000000..66c763a --- /dev/null +++ b/templates/re_skeleton.py @@ -0,0 +1,65 @@ +""" +RE (reverse engineering) skeleton — copy & fill. (p4-team style) + +Common p4 patterns: + * Extract bytes from a .rodata / data dump (IDA "db 30h" lines) -> often just + RSA key material. See 'reversing_is_amazing': parse the bytes, RSA.importKey, + then decrypt the given ciphertext. + * Symbolic execution / path constraint solving with angr. + * Binary parsing / patching with lief; emulation with unicorn. +""" +import re + +# ---- pattern A: RSA key from an IDA/Ghidra byte dump ---- +def bytes_from_rodata(dump_text): + """Parse lines like: .rodata:0000 db 30h, 82h, 2, 5Ch ; comment""" + out = [] + for line in dump_text.splitlines(): + m = re.search(r"\bdb\b\s+(.*)", line) + if not m: + continue + body = m.group(1).split(";")[0] + for tok in re.findall(r"([0-9a-fA-F]+)h?|(\d+)", body): + val = tok[0] or tok[1] + try: + out.append(int(val, 16) if (tok[0] and val.endswith("h")) or + (tok[0] and not val.isdigit()) else int(val)) + except ValueError: + pass + return bytes(out) + + +def solve_rsa_from_dump(dump_text, ciphertext_int): + from Crypto.PublicKey import RSA + from Crypto.Util.number import long_to_bytes + data = bytes_from_rodata(dump_text) + key = RSA.importKey(bytearray(data)) + return long_to_bytes(pow(ciphertext_int, key.e, key.n)) + + +# ---- pattern B: angr symbolic execution (uncomment & adapt) ---- +""" +import angr, claripy +def solve_with_angr(binary, find_addr, avoid_addr, input_len=20): + proj = angr.Project(binary, auto_load_libs=False) + sim = proj.factory.entry_state() + flag = sim.solver.BVS('flag', input_len*8) + for i in range(input_len): + sim.memory.store(sim.regs.rsp + i, flag.get_byte(i)) + sim = proj.factory.simgr(sim) + sim.explore(find=find_addr, avoid=avoid_addr) + if sim.found: + return sim.found[0].solver.eval(flag, cast_to=bytes) +""" + +# ---- pattern C: lief parse / patch ---- +""" +import lief +def parse(binary): + f = lief.parse(binary) + for sym in f.symbols: print(sym.name, hex(sym.value)) +""" + + +if __name__ == "__main__": + raise NotImplementedError("pick a pattern above and fill it in") diff --git a/templates/web_skeleton.py b/templates/web_skeleton.py new file mode 100644 index 0000000..ae4deb6 --- /dev/null +++ b/templates/web_skeleton.py @@ -0,0 +1,29 @@ +""" +WEB skeleton — copy & fill. (p4-team style: read the source, find the +sanitization-order bug, then script the request.) + +Key lesson from 'piapiapia': filter() ran AFTER serialize() -> length +manipulation / object injection. Always diff the order of sanitize vs use. +""" +import requests + +BASE = "http://challenge.host:port" +S = requests.Session() + + +def get_token(): + r = S.get(BASE + "/login") + # parse CSRF / cookies as needed + return None + + +def exploit(): + # 1) Find the input that bypasses validation (array, encoding, filter order). + # 2) Craft payload. + # 3) Send & parse response for flag. + r = S.post(BASE + "/endpoint", data={"nickname[]": "PAYLOAD"}) + print(r.text) + + +if __name__ == "__main__": + exploit()