Skip to content

Commit 53a1910

Browse files
xusheng6claude
andauthored
Add script to remove old password from crackme descriptions (#142)
Crackmes imported from crackmes.de often contain text like: "The password of the archive is 'crackmes.de'" Since the archive passwords have been migrated to 'crackmes.one', this outdated information should be removed from descriptions. This script finds all crackmes with 'crackmes.de' in their description and removes common password reference patterns. Closes #140 Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ba502e2 commit 53a1910

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Migration script to remove old password references from crackme descriptions.
4+
5+
Crackmes imported from crackmes.de often contain text like:
6+
"The password of the archive is 'crackmes.de'"
7+
8+
Since the archive passwords have been migrated to 'crackmes.one', this outdated
9+
information should be removed from descriptions to avoid confusion.
10+
11+
Usage:
12+
python script/remove_old_password_from_descriptions.py # Dry run (default)
13+
python script/remove_old_password_from_descriptions.py --execute # Actually perform updates
14+
"""
15+
16+
import argparse
17+
import os
18+
import re
19+
import sys
20+
21+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
22+
23+
from dotenv import load_dotenv
24+
from pymongo import MongoClient
25+
26+
load_dotenv()
27+
28+
DEFAULT_MONGODB_HOST = os.getenv("MONGODB_HOST", "127.0.0.1")
29+
DEFAULT_MONGODB_PORT = int(os.getenv("MONGODB_PORT", "27017"))
30+
DEFAULT_DATABASE_NAME = os.getenv("DATABASE_NAME", "crackmesone")
31+
32+
# Patterns to match and remove from descriptions
33+
# These patterns match common ways the old password is mentioned
34+
PATTERNS_TO_REMOVE = [
35+
# Match variations of "The password of the archive is 'crackmes.de'"
36+
r"[Tt]he password (?:of the archive |for the archive |)is ['\"]?crackmes\.de['\"]?\.?\s*",
37+
# Match "Password: crackmes.de" or "password: crackmes.de"
38+
r"[Pp]assword:?\s*['\"]?crackmes\.de['\"]?\.?\s*",
39+
# Match "archive password is crackmes.de"
40+
r"[Aa]rchive password (?:is )?['\"]?crackmes\.de['\"]?\.?\s*",
41+
# Match "zip password: crackmes.de"
42+
r"[Zz]ip password:?\s*['\"]?crackmes\.de['\"]?\.?\s*",
43+
# Match standalone "crackmes.de" that appears to be a password reference
44+
# (preceded by "password" on same line)
45+
r"(?<=password[:\s])['\"]?crackmes\.de['\"]?",
46+
]
47+
48+
49+
def find_crackmes_with_old_password(db):
50+
"""Find all crackmes that mention 'crackmes.de' in their description."""
51+
return list(db.crackme.find({
52+
"info": {"$regex": "crackmes\\.de", "$options": "i"}
53+
}))
54+
55+
56+
def clean_description(info: str) -> str:
57+
"""Remove old password references from the description."""
58+
cleaned = info
59+
for pattern in PATTERNS_TO_REMOVE:
60+
cleaned = re.sub(pattern, "", cleaned)
61+
62+
# Clean up any resulting double newlines or trailing whitespace
63+
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned)
64+
cleaned = cleaned.strip()
65+
66+
return cleaned
67+
68+
69+
def main():
70+
parser = argparse.ArgumentParser(
71+
description="Remove old password references from crackme descriptions"
72+
)
73+
parser.add_argument(
74+
"--execute",
75+
action="store_true",
76+
help="Actually perform the updates (default is dry-run mode)"
77+
)
78+
parser.add_argument(
79+
"--mongodb-host",
80+
default=DEFAULT_MONGODB_HOST,
81+
help=f"MongoDB host (default: {DEFAULT_MONGODB_HOST})"
82+
)
83+
parser.add_argument(
84+
"--mongodb-port",
85+
type=int,
86+
default=DEFAULT_MONGODB_PORT,
87+
help=f"MongoDB port (default: {DEFAULT_MONGODB_PORT})"
88+
)
89+
parser.add_argument(
90+
"--database",
91+
default=DEFAULT_DATABASE_NAME,
92+
help=f"Database name (default: {DEFAULT_DATABASE_NAME})"
93+
)
94+
args = parser.parse_args()
95+
96+
dry_run = not args.execute
97+
98+
print("=" * 60)
99+
print("Remove Old Password References from Crackme Descriptions")
100+
print("=" * 60)
101+
if dry_run:
102+
print("\n*** DRY RUN MODE - No changes will be made ***\n")
103+
104+
# Connect to MongoDB
105+
try:
106+
client = MongoClient(args.mongodb_host, args.mongodb_port)
107+
db = client[args.database]
108+
print(f"Connected to MongoDB at {args.mongodb_host}:{args.mongodb_port}")
109+
print(f"Database: {args.database}")
110+
except Exception as e:
111+
print(f"Error connecting to MongoDB: {e}")
112+
sys.exit(1)
113+
114+
# Find crackmes with old password references
115+
crackmes = find_crackmes_with_old_password(db)
116+
print(f"\nFound {len(crackmes)} crackmes with 'crackmes.de' in description")
117+
118+
if not crackmes:
119+
print("No crackmes need to be updated.")
120+
return 0
121+
122+
updated_count = 0
123+
skipped_count = 0
124+
125+
print("\n" + "-" * 60)
126+
for crackme in crackmes:
127+
crackme_id = crackme["_id"]
128+
name = crackme.get("name", "Unknown")
129+
author = crackme.get("author", "Unknown")
130+
original_info = crackme.get("info", "")
131+
132+
cleaned_info = clean_description(original_info)
133+
134+
if cleaned_info == original_info:
135+
# No changes made (the pattern didn't match our removal patterns)
136+
print(f"\n[SKIP] {name} by {author} (ID: {crackme_id})")
137+
print(f" Contains 'crackmes.de' but not in a removable password pattern")
138+
skipped_count += 1
139+
continue
140+
141+
print(f"\n[UPDATE] {name} by {author} (ID: {crackme_id})")
142+
print(f" Original: {repr(original_info[:100])}...")
143+
print(f" Cleaned: {repr(cleaned_info[:100])}...")
144+
145+
if not dry_run:
146+
try:
147+
db.crackme.update_one(
148+
{"_id": crackme_id},
149+
{"$set": {"info": cleaned_info}}
150+
)
151+
updated_count += 1
152+
except Exception as e:
153+
print(f" Error updating: {e}")
154+
else:
155+
updated_count += 1
156+
157+
print("\n" + "=" * 60)
158+
print("SUMMARY")
159+
print("=" * 60)
160+
print(f"Total crackmes found with 'crackmes.de': {len(crackmes)}")
161+
print(f"Updated: {updated_count}")
162+
print(f"Skipped: {skipped_count}")
163+
164+
if dry_run:
165+
print("\n[DRY RUN] No changes were made to the database.")
166+
print("Run with --execute to apply updates.")
167+
168+
return 0
169+
170+
171+
if __name__ == "__main__":
172+
sys.exit(main())

0 commit comments

Comments
 (0)