Skip to content

Instantly share code, notes, and snippets.

@cetaSYN
Created May 11, 2020 02:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cetaSYN/4d6c8fa970ac3b8529bb665cca2a222f to your computer and use it in GitHub Desktop.
Save cetaSYN/4d6c8fa970ac3b8529bb665cca2a222f to your computer and use it in GitHub Desktop.
Finds uids for usernames on CTFtime
#!/usr/bin/env python3
"""Finds uids for usernames on CTFtime"""
# Seriously why is this not easier to do?
import argparse
import logging
import math
import re
import time
import requests
logging.basicConfig()
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
URL = "https://ctftime.org/user/{}"
USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) \
AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/81.0.4044.138 Safari/537.36})"
failed_attempts = 0
def main():
args = get_init_args()
usernames = args.usernames
if args.start:
i = args.start
else:
i = get_top_uid()
while i > 0 and len(usernames) > 0:
resp = delayed_get(URL.format(i), headers={"User-Agent": USER_AGENT})
if not req_success(resp):
log.error("Request failed")
continue
usermatch = has_usernames(resp.content, usernames)
if usermatch:
print(f"{usermatch} ({i}) - {URL.format(i)}")
usernames.remove(usermatch)
i -= 1
def has_usernames(content, usernames):
"""Returns username if found in title from usernames list"""
match = re.search(
r"<title>CTFtime.org / ([a-zA-Z0-9@._-]+)</title>",
str(content)
) # Format may change; not sure regex matches all possibilities
if not match:
return None
title = match.group(1)
for username in usernames:
log.debug(f"{username.lower()} : {str(title).lower()}")
if username.lower() in str(title).lower():
return username
return None
def get_top_uid():
"""Finds the current maximum uid"""
i = 100000
iter_by = 100000 # Multiple of 10
target_code = 404
swap_code = 200
while True:
resp = delayed_get(URL.format(i), headers={"User-Agent": USER_AGENT})
log.debug(URL.format(i))
if not req_success(resp):
log.error("Request failed")
continue
if abs(iter_by) == 1: # Final search
if resp.status_code == 404:
return i - 1
i += 1
else: # Main search
if resp.status_code == target_code:
target_code, swap_code = swap_code, target_code
iter_by = -(math.floor(iter_by/10))
i += iter_by
def delayed_get(url, delay=0.5, headers=None): # 2 req/sec default to be nice
"""Performs a delayed requests GET to the specified url"""
log.debug(f"Waiting for {delay} seconds")
time.sleep(delay)
log.debug(f"GET: {url}")
return requests.get(url, headers=headers)
def req_success(resp):
"""Returns True if status code represents a desirable status code
NOTE: 3 consecutive failures will terminate execution
"""
global failed_attempts
if resp.status_code not in [200, 404]:
failed_attempts += 1
if failed_attempts >= 3: # This logic probably doesn't belong here
log.critical("Failed 3 consecutive requests")
exit(-1)
return False
failed_attempts = 0
return True
def get_init_args():
"""Handles argparse argument processing"""
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("usernames", nargs="+")
parser.add_argument("--start", type=int, help="Starting index")
parser.add_argument("--debug", action="store_true")
args = parser.parse_args()
if args.debug:
log.setLevel(logging.DEBUG)
log.debug(args)
return args
if __name__ == "__main__":
main()
@cetaSYN
Copy link
Author

cetaSYN commented May 11, 2020

Example:

docker run --rm -t -v$(pwd):/srv python:3.8-slim bash -c "pip install -r /srv/requirements.txt && /srv/find_ctftime_profile.py username_replace"

Additional Options:

  • --start <int> : Index to begin decrementing at
  • --debug : Output additional debugging information

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment