init CTFd source
This commit is contained in:
498
populate.py
Normal file
498
populate.py
Normal file
@@ -0,0 +1,498 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
import random
|
||||
import argparse
|
||||
|
||||
from CTFd import create_app
|
||||
from CTFd.cache import clear_challenges, clear_config, clear_standings, clear_pages
|
||||
from CTFd.models import (
|
||||
Users,
|
||||
Teams,
|
||||
Challenges,
|
||||
Flags,
|
||||
Hints,
|
||||
Awards,
|
||||
ChallengeFiles,
|
||||
Fails,
|
||||
Solves,
|
||||
Tracking,
|
||||
Brackets,
|
||||
Solutions,
|
||||
Ratings,
|
||||
)
|
||||
from faker import Faker
|
||||
|
||||
fake = Faker()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument("--mode", help="Set user mode", default="teams")
|
||||
parser.add_argument("--users", help="Amount of users to generate", default=50, type=int)
|
||||
parser.add_argument("--teams", help="Amount of teams to generate", default=10, type=int)
|
||||
parser.add_argument(
|
||||
"--challenges", help="Amount of challenges to generate", default=20, type=int
|
||||
)
|
||||
parser.add_argument(
|
||||
"--awards", help="Amount of awards to generate", default=5, type=int
|
||||
)
|
||||
parser.add_argument(
|
||||
"--brackets", help="Amount of brackets to generate", default=5, type=int
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
app = create_app()
|
||||
|
||||
mode = args.mode
|
||||
USER_AMOUNT = args.users
|
||||
TEAM_AMOUNT = args.teams if args.mode == "teams" else 0
|
||||
CHAL_AMOUNT = args.challenges
|
||||
AWARDS_AMOUNT = args.awards
|
||||
BRACKETS_AMOUNT = args.brackets
|
||||
|
||||
categories = [
|
||||
"Exploitation",
|
||||
"Reversing",
|
||||
"Web",
|
||||
"Forensics",
|
||||
"Scripting",
|
||||
"Cryptography",
|
||||
"Networking",
|
||||
]
|
||||
companies = ["Corp", "Inc.", "Squad", "Team"]
|
||||
icons = [
|
||||
None,
|
||||
"shield",
|
||||
"bug",
|
||||
"crown",
|
||||
"crosshairs",
|
||||
"ban",
|
||||
"lightning",
|
||||
"code",
|
||||
"cowboy",
|
||||
"angry",
|
||||
]
|
||||
|
||||
|
||||
def gen_text():
|
||||
return fake.text()
|
||||
|
||||
|
||||
def gen_sentence():
|
||||
return fake.sentence()
|
||||
|
||||
|
||||
def gen_name():
|
||||
return fake.first_name()
|
||||
|
||||
|
||||
def gen_team_name():
|
||||
return fake.word().capitalize() + str(random.randint(1, 1000))
|
||||
|
||||
|
||||
def gen_email():
|
||||
return fake.email()
|
||||
|
||||
|
||||
def gen_category():
|
||||
return random.choice(categories)
|
||||
|
||||
|
||||
def gen_affiliation():
|
||||
return (fake.word() + " " + random.choice(companies)).title()
|
||||
|
||||
|
||||
def gen_value():
|
||||
return random.choice(range(100, 500, 50))
|
||||
|
||||
|
||||
def gen_cost():
|
||||
return random.choice([0, 50, 100])
|
||||
|
||||
|
||||
def gen_word():
|
||||
return fake.word()
|
||||
|
||||
|
||||
def gen_icon():
|
||||
return random.choice(icons)
|
||||
|
||||
|
||||
def gen_file():
|
||||
return fake.file_name()
|
||||
|
||||
|
||||
def gen_ip():
|
||||
return fake.ipv4()
|
||||
|
||||
|
||||
def gen_rating():
|
||||
return random.choice((-1, 1))
|
||||
|
||||
|
||||
def random_date(start, end):
|
||||
return start + datetime.timedelta(
|
||||
seconds=random.randint(0, int((end - start).total_seconds()))
|
||||
)
|
||||
|
||||
|
||||
def random_chance():
|
||||
return random.random() > 0.5
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with app.app_context():
|
||||
db = app.db
|
||||
|
||||
# Generating Challenges
|
||||
print("GENERATING CHALLENGES")
|
||||
for x in range(CHAL_AMOUNT):
|
||||
word = gen_word()
|
||||
chal = Challenges(
|
||||
name=word,
|
||||
description=gen_text(),
|
||||
attribution=f"Written by {gen_name()}",
|
||||
value=gen_value(),
|
||||
category=gen_category(),
|
||||
)
|
||||
db.session.add(chal)
|
||||
db.session.commit()
|
||||
f = Flags(challenge_id=x + 1, content=word, type="static")
|
||||
db.session.add(f)
|
||||
db.session.commit()
|
||||
|
||||
# Generating Solutions
|
||||
print("GENERATING SOLUTIONS")
|
||||
solutions = []
|
||||
for x in range(CHAL_AMOUNT):
|
||||
solution = Solutions(
|
||||
content=" ".join([gen_text() for _ in range(5)]),
|
||||
state="visible",
|
||||
challenge_id=x + 1,
|
||||
)
|
||||
db.session.add(solution)
|
||||
solutions.append(solution)
|
||||
db.session.commit()
|
||||
|
||||
# Generating Hints
|
||||
print("GENERATING HINTS")
|
||||
for challenge_id in range(1, CHAL_AMOUNT + 1):
|
||||
# Each challenge gets 0-3 hints (weighted towards having hints)
|
||||
num_hints = random.choices([0, 1, 2, 3], weights=[20, 30, 35, 15])[0]
|
||||
|
||||
for hint_num in range(num_hints):
|
||||
hint = Hints(
|
||||
title=gen_sentence(),
|
||||
content=gen_text(),
|
||||
cost=gen_cost(),
|
||||
challenge_id=challenge_id,
|
||||
type="standard",
|
||||
)
|
||||
db.session.add(hint)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Generating Files
|
||||
print("GENERATING FILES")
|
||||
AMT_CHALS_WITH_FILES = int(CHAL_AMOUNT * (3.0 / 4.0))
|
||||
for x in range(AMT_CHALS_WITH_FILES):
|
||||
chal = random.randint(1, CHAL_AMOUNT)
|
||||
filename = gen_file()
|
||||
md5hash = hashlib.md5(filename.encode("utf-8")).hexdigest()
|
||||
chal_file = ChallengeFiles(
|
||||
challenge_id=chal, location=md5hash + "/" + filename
|
||||
)
|
||||
db.session.add(chal_file)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Generating Brackets
|
||||
print("GENERATING BRACKETS")
|
||||
for x in range(BRACKETS_AMOUNT):
|
||||
bracket = Brackets(
|
||||
name=gen_team_name(), description=gen_sentence(), type="teams"
|
||||
)
|
||||
db.session.add(bracket)
|
||||
for x in range(BRACKETS_AMOUNT):
|
||||
bracket = Brackets(
|
||||
name=gen_team_name(), description=gen_sentence(), type="users"
|
||||
)
|
||||
db.session.add(bracket)
|
||||
|
||||
db.session.commit()
|
||||
TEAM_BRACKETS = [b.id for b in Brackets.query.filter_by(type="teams").all()]
|
||||
USER_BRACKETS = [b.id for b in Brackets.query.filter_by(type="users").all()]
|
||||
|
||||
# Generating Teams
|
||||
print("GENERATING TEAMS")
|
||||
used = []
|
||||
used_oauth_ids = []
|
||||
count = 0
|
||||
while count < TEAM_AMOUNT:
|
||||
name = gen_team_name()
|
||||
if name not in used:
|
||||
used.append(name)
|
||||
team = Teams(name=name, password="password")
|
||||
if random_chance():
|
||||
team.affiliation = gen_affiliation()
|
||||
if random_chance():
|
||||
oauth_id = random.randint(1, 1000)
|
||||
while oauth_id in used_oauth_ids:
|
||||
oauth_id = random.randint(1, 1000)
|
||||
used_oauth_ids.append(oauth_id)
|
||||
team.oauth_id = oauth_id
|
||||
if random_chance():
|
||||
team.bracket_id = random.choice(TEAM_BRACKETS)
|
||||
db.session.add(team)
|
||||
count += 1
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Generating Users
|
||||
print("GENERATING USERS")
|
||||
used = []
|
||||
used_oauth_ids = []
|
||||
count = 0
|
||||
while count < USER_AMOUNT:
|
||||
name = gen_name()
|
||||
if name not in used:
|
||||
used.append(name)
|
||||
try:
|
||||
user = Users(name=name, email=gen_email(), password="password")
|
||||
user.verified = True
|
||||
if random_chance():
|
||||
user.affiliation = gen_affiliation()
|
||||
if random_chance():
|
||||
oauth_id = random.randint(1, 1000)
|
||||
while oauth_id in used_oauth_ids:
|
||||
oauth_id = random.randint(1, 1000)
|
||||
used_oauth_ids.append(oauth_id)
|
||||
user.oauth_id = oauth_id
|
||||
if random_chance():
|
||||
user.bracket_id = random.choice(USER_BRACKETS)
|
||||
if mode == "teams":
|
||||
user.team_id = random.randint(1, TEAM_AMOUNT)
|
||||
db.session.add(user)
|
||||
db.session.flush()
|
||||
|
||||
track = Tracking(ip=gen_ip(), user_id=user.id)
|
||||
db.session.add(track)
|
||||
db.session.flush()
|
||||
count += 1
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
db.session.commit()
|
||||
|
||||
if mode == "teams":
|
||||
# Assign Team Captains
|
||||
print("GENERATING TEAM CAPTAINS")
|
||||
teams = Teams.query.all()
|
||||
for team in teams:
|
||||
captain = (
|
||||
Users.query.filter_by(team_id=team.id)
|
||||
.order_by(Users.id)
|
||||
.limit(1)
|
||||
.first()
|
||||
)
|
||||
if captain:
|
||||
team.captain_id = captain.id
|
||||
db.session.commit()
|
||||
|
||||
# Generating Challenge Open Tracking Events
|
||||
print("GENERATING CHALLENGE TRACKING EVENTS")
|
||||
base_time = datetime.datetime.utcnow() + datetime.timedelta(minutes=-12000)
|
||||
|
||||
for user_id in range(1, USER_AMOUNT + 1):
|
||||
user = Users.query.filter_by(id=user_id).first()
|
||||
if user:
|
||||
# Each user opens/views some random challenges (60-100% of challenges)
|
||||
num_challenges_to_open = random.randint(
|
||||
int(CHAL_AMOUNT * 0.6), CHAL_AMOUNT
|
||||
)
|
||||
challenges_to_open = random.sample(
|
||||
range(1, CHAL_AMOUNT + 1), num_challenges_to_open
|
||||
)
|
||||
|
||||
user_time = base_time + datetime.timedelta(
|
||||
minutes=random.randint(0, 1000)
|
||||
)
|
||||
|
||||
for challenge_id in challenges_to_open:
|
||||
# Create tracking event for challenge open
|
||||
track_event = Tracking(
|
||||
type="challenges.open",
|
||||
ip=gen_ip(),
|
||||
target=challenge_id,
|
||||
user_id=user_id,
|
||||
date=user_time,
|
||||
)
|
||||
db.session.add(track_event)
|
||||
|
||||
# Increment time by 1-30 minutes between challenge opens
|
||||
user_time += datetime.timedelta(minutes=random.randint(1, 30))
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Generating Solves
|
||||
print("GENERATING SOLVES")
|
||||
if mode == "users":
|
||||
for x in range(USER_AMOUNT):
|
||||
used = []
|
||||
base_time = datetime.datetime.utcnow() + datetime.timedelta(
|
||||
minutes=-10000
|
||||
)
|
||||
for y in range(random.randint(1, CHAL_AMOUNT)):
|
||||
chalid = random.randint(1, CHAL_AMOUNT)
|
||||
if chalid not in used:
|
||||
used.append(chalid)
|
||||
user = Users.query.filter_by(id=x + 1).first()
|
||||
solve = Solves(
|
||||
user_id=user.id,
|
||||
team_id=user.team_id,
|
||||
challenge_id=chalid,
|
||||
ip="127.0.0.1",
|
||||
provided=gen_word(),
|
||||
)
|
||||
|
||||
new_base = random_date(
|
||||
base_time,
|
||||
base_time
|
||||
+ datetime.timedelta(minutes=random.randint(30, 60)),
|
||||
)
|
||||
solve.date = new_base
|
||||
base_time = new_base
|
||||
|
||||
db.session.add(solve)
|
||||
db.session.commit()
|
||||
elif mode == "teams":
|
||||
for x in range(1, TEAM_AMOUNT):
|
||||
used_teams = []
|
||||
used_users = []
|
||||
base_time = datetime.datetime.utcnow() + datetime.timedelta(
|
||||
minutes=-10000
|
||||
)
|
||||
team = Teams.query.filter_by(id=x).first()
|
||||
members_ids = [member.id for member in team.members]
|
||||
for y in range(random.randint(1, CHAL_AMOUNT)):
|
||||
chalid = random.randint(1, CHAL_AMOUNT)
|
||||
user_id = random.choice(members_ids)
|
||||
if (chalid, team.id) not in used_teams:
|
||||
if (chalid, user_id) not in used_users:
|
||||
solve = Solves(
|
||||
user_id=user_id,
|
||||
team_id=team.id,
|
||||
challenge_id=chalid,
|
||||
ip="127.0.0.1",
|
||||
provided=gen_word(),
|
||||
)
|
||||
new_base = random_date(
|
||||
base_time,
|
||||
base_time
|
||||
+ datetime.timedelta(minutes=random.randint(30, 60)),
|
||||
)
|
||||
solve.date = new_base
|
||||
base_time = new_base
|
||||
db.session.add(solve)
|
||||
db.session.commit()
|
||||
used_teams.append((chalid, team.id))
|
||||
used_users.append((chalid, user_id))
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Generating Awards
|
||||
print("GENERATING AWARDS")
|
||||
for x in range(USER_AMOUNT):
|
||||
base_time = datetime.datetime.utcnow() + datetime.timedelta(minutes=-10000)
|
||||
for _ in range(random.randint(0, AWARDS_AMOUNT)):
|
||||
user = Users.query.filter_by(id=x + 1).first()
|
||||
award = Awards(
|
||||
user_id=user.id,
|
||||
team_id=user.team_id,
|
||||
name=gen_word(),
|
||||
value=random.randint(-10, 10),
|
||||
icon=gen_icon(),
|
||||
)
|
||||
new_base = random_date(
|
||||
base_time,
|
||||
base_time + datetime.timedelta(minutes=random.randint(30, 60)),
|
||||
)
|
||||
award.date = new_base
|
||||
base_time = new_base
|
||||
|
||||
db.session.add(award)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Generating Wrong Flags
|
||||
print("GENERATING WRONG FLAGS")
|
||||
for x in range(USER_AMOUNT):
|
||||
used = []
|
||||
base_time = datetime.datetime.utcnow() + datetime.timedelta(minutes=-10000)
|
||||
for y in range(random.randint(1, CHAL_AMOUNT * 20)):
|
||||
chalid = random.randint(1, CHAL_AMOUNT)
|
||||
if chalid not in used:
|
||||
used.append(chalid)
|
||||
user = Users.query.filter_by(id=x + 1).first()
|
||||
wrong = Fails(
|
||||
user_id=user.id,
|
||||
team_id=user.team_id,
|
||||
challenge_id=chalid,
|
||||
ip="127.0.0.1",
|
||||
provided=gen_word(),
|
||||
)
|
||||
|
||||
new_base = random_date(
|
||||
base_time,
|
||||
base_time + datetime.timedelta(minutes=random.randint(30, 60)),
|
||||
)
|
||||
wrong.date = new_base
|
||||
base_time = new_base
|
||||
|
||||
db.session.add(wrong)
|
||||
db.session.commit()
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Generating Ratings and Reviews for Solved Challenges
|
||||
print("GENERATING RATINGS AND REVIEWS")
|
||||
solves = Solves.query.all()
|
||||
for solve in solves:
|
||||
# Only some users will rate challenges they solve (60% chance)
|
||||
if random.random() < 0.6:
|
||||
try:
|
||||
# Check if this user has already rated this challenge
|
||||
existing_rating = Ratings.query.filter_by(
|
||||
user_id=solve.user_id, challenge_id=solve.challenge_id
|
||||
).first()
|
||||
|
||||
if not existing_rating:
|
||||
rating = Ratings(
|
||||
user_id=solve.user_id,
|
||||
challenge_id=solve.challenge_id,
|
||||
value=gen_rating(),
|
||||
review=gen_sentence()
|
||||
if random.random() < 0.7
|
||||
else None, # 70% chance of review
|
||||
date=solve.date
|
||||
+ datetime.timedelta(
|
||||
minutes=random.randint(
|
||||
5, 120
|
||||
) # Rate within 5 minutes to 2 hours after solving
|
||||
),
|
||||
)
|
||||
db.session.add(rating)
|
||||
except Exception:
|
||||
# Skip if there's any constraint violation
|
||||
pass
|
||||
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
|
||||
clear_config()
|
||||
clear_standings()
|
||||
clear_challenges()
|
||||
clear_pages()
|
||||
Reference in New Issue
Block a user