63 lines
1.9 KiB
Python
63 lines
1.9 KiB
Python
#!/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()
|