diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4424a86 --- /dev/null +++ b/.gitignore @@ -0,0 +1,173 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +#*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +/services/service3/src/cmd_tree.h +/services/service3/src/rpn +/services/service3/src/rpn_with_debug +/services/service3/src/rpn_with_symbols +/services/service3/src/rpn_with_asan +/services/service3/src/cmd_paths.py +/services/service3/src/s_keys.py +/services/service3/src/v_keys.c +/services/service3/src/sk? +/services/service3/src/vk? + +.vscode diff --git a/README.md b/README.md new file mode 100644 index 0000000..3a4a6fa --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# ICC2022 A/D Services + +Attack/Defense services for the International Cybersecurity Challenge 2022 - Athens + +organized by the team of the [CINI - Cybersecurity National Laboratory](https://cybersecnatlab.it) + +
+ +Event coordinator: [Gaspare Ferraro](https://github.com/GaspareG) + +Platform and system administrators: [Giovanni Minotti](https://github.com/Giotino) and [Gaspare Ferraro](https://github.com/GaspareG) + +## Services + +| # | service | store | vulns | port(s) | categories | author(s) | +| :-: | :------------------------------ | :---: | :---: | :----------: | ----------------------- | --------------------------------------------------------------------------------------- | +| 1 | [ClosedSea](/services/service1) | 2 | 5 | 300{3,4} | web / crypto | [@bonaff](https://github.com/RiccardoBonafede) [@drago-96](https://github.com/drago-96) | +| 2 | [CyberUni](/services/service2) | 3 | 8 | 123{4,5,6,7} | rev / web / crypto | [@mr-96](https://github.com/mr-96) [@drago-96](https://github.com/drago-96) | +| 3 | [RPN](/services/service3) | 2 | 9 | 1337 | pwn / pwn | [@zxgio](https://github.com/zxgio) | +| 4 | [Trademark](/services/service4) | 1 | 4 | 5000 | windows / misc / crypto | [@TheNodi](https://github.com/TheNodi) [@mr-96](https://github.com/mr-96) | + +## Checkers & exploits + +See [checkers/](/checkers) and [exploits/](/exploits) + +## Final scoreboard + +![scoreboard](/img/scoreboard.png) + +## Competition data + +- Team info: [team.csv](/data/team.csv) +- Service info: [service.csv](/data/service.csv) +- Flags generated: [flag.csv](/data/flag.csv) +- Stolen flags: [stolen_flags.csv](/data/stolen_flags.csv) +- Team service: [team_service.csv](/data/team_service.csv) +- Checks: [checks.csv](/data/checks.csv) + +## Network diagram + +![network](/img/network.svg) diff --git a/checkers/0/checker.py b/checkers/0/checker.py new file mode 100644 index 0000000..3ff642d --- /dev/null +++ b/checkers/0/checker.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python3 + +# Do not make modification to checklib.py (except for debug), it can be replaced at any time +from string import hexdigits +import checklib +import requests +from service1_common import * +import socketio +import re +import random + +data = checklib.get_data() +action = data['action'] +team_id = data['teamId'] +service_addr = '10.60.' + team_id + '.1' +service_name = 'ClosedSea-1' + +nft_id_regex = re.compile('/view/(.*)\'> 1: + headless_host = 'http://localhost:3000' + team_ip = 'localhost' +else: + headless_host = 'http://headless_team_{}:3000/'.format(data['teamId']) + team_ip = '10.60.' + data['teamId'] + '.1' + +closedsea_baseurl = 'http://{}:3003'.format(team_ip) +minter_baseurl = 'http://{}:3004'.format(team_ip) + +nft_adj = [ +'Candid', +'Canine', +'Capital', +'Carefree', +'Careful', +'Difficult', +'Dizzy', +'Focused', +'Fond', +'Foolhardy', +'Impressive', +'Improbable', +'Meaty', +'Medical', +'Mediocre' +] + +nft_animals = [ +'Albatross', +'Alligator', +'Alpaca', +'Angelfish', +'Armadillo', +'Beaver', +'Bee', +'Beetle', +'Coati', +'Cobra', +'Cockroach', +'Eagle', +'Earwig', +'Echidna', +'Gopher', +'Gorilla', +'Goshawk', +'Lemming', +'Lemur', +'Leopard', +'Squid', +'Squirrel', +'Starfish', +'Starling', +'Wolf', +'Wolverine', +'Wombat', +'Woodcock' +] + +def check_response_ok(resp, status=200, in_resp=False, msg='', sio=False): + + status_check = resp and status == resp.status_code + + in_resp_check = resp and in_resp and in_resp in resp.text + + if not resp or not status_check or not in_resp_check: + msg_debug = resp.text if resp else 'Requests connection failed' + if sio: + sio.disconnect() + checklib.quit( + checklib.Status.DOWN, + msg, + msg_debug + ) + + +def randstr(n, l=False,dictionary=string.ascii_letters): + if l: + n = random.randint(n, l) + return ''.join([random.choice(dictionary) for _ in range(n)]) + +def set_seed(seed): + random.seed(seed) + +def register_user(sess,username, password, key): + URL = closedsea_baseurl + '/register' + + data = { + 'username': username, + 'password': password, + 'public_key': key + } + try: + return sess.post(URL, data=data) + except: + return False + + +def login_user_closedsea(sess, username, password): + URL = closedsea_baseurl + '/login' + + data = { + 'username': username, + 'password': password + } + try: + return sess.post(URL, data=data) + except: + return False + + +def login_user_minter(sess, username, password): + URL = minter_baseurl + '/login.php' + data = { + 'username': username, + 'password': password + } + try: + return sess.post(URL, data=data) + except: + return False + + +def mint(sess, title, data, cost, is_public): + URL = minter_baseurl + '/index.php' + data ={ + 'title': title, + 'data': data, + 'price': cost, + 'public': is_public + } + try: + return sess.post(URL, data=data) + except: + return False + +def logout(sess): + URL = closedsea_baseurl + '/logout' + try: + return sess.get(URL) + except: + return False + +def list_all_nfts(sess): + URL = closedsea_baseurl + '/listing' + pass + +def list_all_nfts_websockets(sess): + pass + +def create_user_cred(): + username = randstr(6,9) + password = randstr(6,9) + return username, password + +def set_rand_ua(sess): + with open('user_agents.txt', 'r') as f: + ua_list = f.readlines() + + sess.headers.update({'User-Agent': random.choice(ua_list)[:-1]}) diff --git a/checkers/0/user_agents.txt b/checkers/0/user_agents.txt new file mode 100644 index 0000000..ff9b336 --- /dev/null +++ b/checkers/0/user_agents.txt @@ -0,0 +1 @@ +python-requests/2.28.0 diff --git a/checkers/1/checker.py b/checkers/1/checker.py new file mode 100644 index 0000000..a1e287c --- /dev/null +++ b/checkers/1/checker.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python3 + +# Do not make modification to checklib.py (except for debug), it can be replaced at any time +from string import hexdigits +import checklib +import requests +from service1_common import * +import socketio +from time import sleep +import re + +data = checklib.get_data() +action = data['action'] +team_id = data['teamId'] +service_addr = '10.60.' + team_id + '.1' +service_name = 'ClosedSea-2' + + +# Check SLA +def check_sla(): + + sess = requests.Session() + set_rand_ua(sess) + username, password = create_user_cred() + username_mint, password_mint = create_user_cred() + user_data = { + 'username': username, + 'password': password + } + + # Because socketio spawns another thread, we have to save the exit informations here. I feel dirty + global status, msg_public, msg_debug + status, msg_public, msg_debug = checklib.Status.ERROR, 'error', 'No exit code provided' + sio = socketio.Client(reconnection_attempts=3) + try: + sio.connect(headless_host) + except: + checklib.quit(checklib.Status.ERROR, + 'Could not check.', 'Headless down') + + nft_data = randstr(10, 20) + + def check_response_ok_global(resp, status_to_check=200, in_resp=False, msg='', sio=False): + global status, msg_public, msg_debug + + status_check = resp and status_to_check == resp.status_code + in_resp_check = resp and in_resp and in_resp in resp.text + + if not status_check or not in_resp_check: + msg_debug = resp.text if resp else 'Requests connection failed' + status, msg_public, msg_debug = checklib.Status.DOWN, msg, msg_debug + sio.disconnect() + quit() + + @sio.event + def ready(): + def check(msg): + global status, msg_public, msg_debug + if not msg or msg['error']: + status, msg_public, msg_debug = checklib.Status.DOWN, 'Website down', 'headless:' + \ + str(msg) + sio.disconnect() + quit() + #checklib.quit(checklib.Status.DOWN, 'Website down.', 'headless:' + str(msg)) + return True + # Sometimes this throws an exception when one of + try: + sio.emit('register', user_data, callback=lambda x: check(x)) + except: + pass + + @sio.event + def user_registered(): + global status, msg_public, msg_debug + # Create a "minter" user, that will mint a dummy nft + resp = register_user(sess, username_mint, password_mint, + randstr(128, dictionary=hexdigits)) + check_response_ok_global( + resp, in_resp=username_mint, msg='Could not register user', sio=sio) + resp = login_user_minter(sess, username_mint, password_mint) + check_response_ok_global( + resp, in_resp='Mint an nft', msg='Could not login user on minter', sio=sio) + + # Mint a dummy NFT + rand_title1 = randstr(1, dictionary=nft_adj) + ' ' + randstr(1, + dictionary=nft_animals) + ' n:' + randstr(2, 8, string.digits) + + resp = mint(sess, rand_title1, nft_data, random.randint(2, 6), 'true') + check_response_ok_global( + resp, in_resp='Check your new NFT!', msg='Minting not working', sio=sio) + + # Retrieve the id of the nft + nft_id_regex = re.compile('/view/(.*)\'> 1: + headless_host = 'http://localhost:3000' + team_ip = 'localhost' +else: + headless_host = 'http://headless_team_{}:3000/'.format(data['teamId']) + team_ip = '10.60.' + data['teamId'] + '.1' + +closedsea_baseurl = 'http://{}:3003'.format(team_ip) +minter_baseurl = 'http://{}:3004'.format(team_ip) + +nft_adj = [ +'Candid', +'Canine', +'Capital', +'Carefree', +'Careful', +'Difficult', +'Dizzy', +'Focused', +'Fond', +'Foolhardy', +'Impressive', +'Improbable', +'Meaty', +'Medical', +'Mediocre' +] + +nft_animals = [ +'Albatross', +'Alligator', +'Alpaca', +'Angelfish', +'Armadillo', +'Beaver', +'Bee', +'Beetle', +'Coati', +'Cobra', +'Cockroach', +'Eagle', +'Earwig', +'Echidna', +'Gopher', +'Gorilla', +'Goshawk', +'Lemming', +'Lemur', +'Leopard', +'Squid', +'Squirrel', +'Starfish', +'Starling', +'Wolf', +'Wolverine', +'Wombat', +'Woodcock' +] + +def check_response_ok(resp, status=200, in_resp=False, msg='', sio=False): + + status_check = resp and status == resp.status_code + + in_resp_check = resp and in_resp and in_resp in resp.text + + if not resp or not status_check or not in_resp_check: + msg_debug = resp.text if resp else 'Requests connection failed' + if sio: + sio.disconnect() + checklib.quit( + checklib.Status.DOWN, + msg, + msg_debug + ) + + +def randstr(n, l=False,dictionary=string.ascii_letters): + if l: + n = random.randint(n, l) + return ''.join([random.choice(dictionary) for _ in range(n)]) + +def set_seed(seed): + random.seed(seed) + +def register_user(sess,username, password, key): + URL = closedsea_baseurl + '/register' + + data = { + 'username': username, + 'password': password, + 'public_key': key + } + try: + return sess.post(URL, data=data) + except: + return False + + +def login_user_closedsea(sess, username, password): + URL = closedsea_baseurl + '/login' + + data = { + 'username': username, + 'password': password + } + try: + return sess.post(URL, data=data) + except: + return False + + +def login_user_minter(sess, username, password): + URL = minter_baseurl + '/login.php' + data = { + 'username': username, + 'password': password + } + try: + return sess.post(URL, data=data) + except: + return False + + +def mint(sess, title, data, cost, is_public): + URL = minter_baseurl + '/index.php' + data ={ + 'title': title, + 'data': data, + 'price': cost, + 'public': is_public + } + try: + return sess.post(URL, data=data) + except: + return False + +def logout(sess): + URL = closedsea_baseurl + '/logout' + try: + return sess.get(URL) + except: + return False + +def list_all_nfts(sess): + URL = closedsea_baseurl + '/listing' + pass + +def list_all_nfts_websockets(sess): + pass + +def create_user_cred(): + username = randstr(6,9) + password = randstr(6,9) + return username, password + +def set_rand_ua(sess): + with open('user_agents.txt', 'r') as f: + ua_list = f.readlines() + + sess.headers.update({'User-Agent': random.choice(ua_list)[:-1]}) diff --git a/checkers/1/user_agents.txt b/checkers/1/user_agents.txt new file mode 100644 index 0000000..ff9b336 --- /dev/null +++ b/checkers/1/user_agents.txt @@ -0,0 +1 @@ +python-requests/2.28.0 diff --git a/checkers/2/authlib.cpython-39-x86_64-linux-gnu.so b/checkers/2/authlib.cpython-39-x86_64-linux-gnu.so new file mode 100755 index 0000000..3b6ed49 Binary files /dev/null and b/checkers/2/authlib.cpython-39-x86_64-linux-gnu.so differ diff --git a/checkers/2/checker.py b/checkers/2/checker.py new file mode 100644 index 0000000..8fc7dfb --- /dev/null +++ b/checkers/2/checker.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python3 + +# Do not make modification to checklib.py (except for debug), it can be replaced at any time +import checklib +import random +import string +import sys +import os +os.environ["PWNLIB_NOTERM"] = "1" + +from pwn import * +from service2_client import Client + +context.timeout = 5 +context.log_level = "error" + +data = checklib.get_data() +action = data['action'] +auth_port = 1234 +store1_port = 1235 +service_name = 'ExamNotes' + +team_id = data['teamId'] +team_addr = '10.60.' + team_id + '.1' + + +def get_random_string(n): + alph = string.ascii_letters + string.digits + return "".join(random.choice(alph) for _ in range(n)) + +# Check SLA + + +def check_sla(): + number_of_notes = random.randint(1, 5) + username = get_random_string(random.randint(10, 16)) + password = get_random_string(random.randint(10, 16)) + note_titles = [get_random_string(random.randint(10, 16)) + for _ in range(number_of_notes)] + note_contents = [get_random_string(random.randint( + 20, 100)) for _ in range(number_of_notes)] + + c = Client(team_addr, auth_port, username, password, + "1") # last param is flagstore id + try: + c.register() + except Exception as e: + checklib.quit(checklib.Status.DOWN, "Cannot register", str(e)) + + try: + c.login() + login_token = c.get_token() + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot get login token from auth", str(e)) + + try: + r = remote(team_addr, store1_port) + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot connect to ExamNotes", str(e)) + + try: + r.recvuntil(b": ") + r.sendline(login_token.encode()) + r.recvline() + assert r.recvline().startswith(b"Successfully authenticated") + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot login via token on ExamNotes", str(e)) + + try: + for i in range(number_of_notes): + r.recvuntil(b'0. Exit\n') + r.sendline(b"1") + r.recvuntil(b": ") + r.sendline(note_titles[i].encode()) + r.recvuntil(b": ") + r.sendline(note_contents[i].encode()) + assert b"added" in r.recvline() + r.close() + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot create a new note on ExamNotes", str(e)) + + try: + r = remote(team_addr, store1_port) + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot connect to ExamNotes", str(e)) + + try: + r.recvuntil(b": ") + r.sendline(login_token.encode()) + r.recvline() + assert r.recvline().startswith(b"Successfully authenticated") + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot login via token on ExamNotes", str(e)) + + try: + for _ in range(random.randint(1, 3)): + if random.randint(0, 1) == 0: + r.recvuntil(b'0. Exit\n') + r.sendline(b"2") + recovered_notes = r.recvlines(number_of_notes) + assert all([note_titles[i].encode() in b"".join( + recovered_notes) for i in range(number_of_notes)]) + else: + r.recvuntil(b'0. Exit\n') + r.sendline(b"3") + r.recvuntil(b": ") + note_id = random.randint(0, number_of_notes-1) + r.sendline(str(note_id).encode()) + assert note_titles[note_id].encode() in r.recvline() + assert note_contents[note_id].encode() in r.recvline() + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot list or read notes on ExamNotes", str(e)) + + checklib.quit(checklib.Status.OK, 'OK') + + +# Put the flag using the flag as the seed for random stuff +def put_flag(): + flag = data['flag'] + + random.seed(int.from_bytes(flag.encode(), "big")) + username = get_random_string(random.randint(10, 16)) + password = get_random_string(random.randint(10, 16)) + + c = Client(team_addr, auth_port, username, password, + "1") # last param is flagstore id + try: + c.register() + except Exception as e: + checklib.quit(checklib.Status.DOWN, "Cannot register", str(e)) + + try: + c.login() + login_token = c.get_token() + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot get login token from auth", str(e)) + + try: + r = remote(team_addr, store1_port) + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot connect to ExamNotes", str(e)) + + try: + r.recvuntil(b": ") + r.sendline(login_token.encode()) + r.recvline() + assert r.recvline().startswith(b"Successfully authenticated") + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot login via token on ExamNotes", str(e)) + + try: + r.recvuntil(b'0. Exit\n') + r.sendline(b"1") + r.recvuntil(b": ") + r.sendline(b"flag") + r.recvuntil(b": ") + r.sendline(flag.encode()) + assert b"added" in r.recvline() + r.close() + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot create a new note on ExamNotes", str(e)) + + checklib.post_flag_id(service_name, team_addr, username) + + # If OK + checklib.quit(checklib.Status.OK, 'OK') + +# Check if the flag still exists, use the flag as the seed for random stuff as for put flag + + +def get_flag(): + + flag = data['flag'] + + random.seed(int.from_bytes(flag.encode(), "big")) + username = get_random_string(random.randint(10, 16)) + password = get_random_string(random.randint(10, 16)) + + c = Client(team_addr, auth_port, username, password, + "1") # last param is flagstore id + try: + c.login() + login_token = c.get_token() + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot get login token from auth", str(e)) + + try: + r = remote(team_addr, store1_port) + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot connect to ExamNotes", str(e)) + + try: + r.recvuntil(b": ") + r.sendline(login_token.encode()) + r.recvline() + assert r.recvline().startswith(b"Successfully authenticated") + except Exception as e: + checklib.quit(checklib.Status.DOWN, + "Cannot login via token on ExamNotes", str(e)) + + try: + r.recvuntil(b'0. Exit\n') + r.sendline(b"3") + r.recvuntil(b": ") + note_id = 0 + r.sendline(str(note_id).encode()) + assert b"flag" in r.recvline() + assert flag.encode() in r.recvline() + except Exception as e: + checklib.quit(checklib.Status.DOWN, "Cannot retrieve flag", str(e)) + + checklib.quit(checklib.Status.OK, 'OK') + + +if __name__ == "__main__": + + if action == checklib.Action.CHECK_SLA.name: + check_sla() + elif action == checklib.Action.PUT_FLAG.name: + put_flag() + elif action == checklib.Action.GET_FLAG.name: + get_flag() diff --git a/checkers/2/checklib.py b/checkers/2/checklib.py new file mode 100644 index 0000000..4c573fa --- /dev/null +++ b/checkers/2/checklib.py @@ -0,0 +1,53 @@ +import sys +from enum import Enum +import requests +import os + +TOKEN = 'PRIVATE-TOKEN' + + +class Status(Enum): + OK = 101 + DOWN = 104 + ERROR = 110 + + +class Action(Enum): + CHECK_SLA = 'CHECK_SLA' + PUT_FLAG = 'PUT_FLAG' + GET_FLAG = 'GET_FLAG' + + def __str__(self): + return str(self.value) + + +def get_data(): + data = { + 'action': os.environ['ACTION'], + 'teamId': os.environ['TEAM_ID'], + 'round': os.environ['ROUND'] + } + + if data['action'] == Action.PUT_FLAG.name or data['action'] == Action.GET_FLAG.name: + data['flag'] = os.environ['FLAG'] + + return data + + +def quit(exit_code, comment='', debug=''): + if isinstance(exit_code, Status): + exit_code = exit_code.value + + print(comment) + print(debug, file=sys.stderr) + exit(exit_code) + + +def post_flag_id(service_id, team_id, flag_id): + requests.post('http://flagid:8081/postFlagId', json={ + 'token': TOKEN, + 'serviceId': service_id, + 'teamId': team_id, + 'round': int(os.environ['ROUND']), + 'flagId': flag_id + }) diff --git a/checkers/2/service2_client.py b/checkers/2/service2_client.py new file mode 100644 index 0000000..497c204 --- /dev/null +++ b/checkers/2/service2_client.py @@ -0,0 +1,66 @@ +import authlib +from pwn import * + +context.log_level = "error" + +class Client(): + def __init__(self, server_ip, server_port, username, password, service): + self.server_addr = server_ip + self.server_port = server_port + self.username = username + self.password = password + self.service = service + self.auth_token = None + self.shared_key = None + + def open_connection(self): + s = remote(self.server_addr, self.server_port) + assert s.recvline(False) == b"HELLO" + s.sendline(b"HELLO") + assert s.recvline(False) == b"OK" + return s + + def register(self): + s = self.open_connection() + s.sendline(b"REG") + assert s.recvline(False) == b"OK" + s.sendline(self.username.encode()) + resp = s.recvline(False) + if resp != b"OK": + return False + s.sendline(self.password.encode()) + resp = s.recvline(False) + s.close() + if resp != b"OK": + return False + return True + + def login(self): + auth = authlib.AuthClient(self.username, self.password) + initial_values = auth.get_starting_values() + s = self.open_connection() + s.sendline(b"AUTH") + assert s.recvline(False) == b"OK" + s.sendline(self.username.encode()) + resp = s.recvline(False) + if resp != b"OK": + return False + s.sendline(initial_values.encode()) + intermediate_values = s.recvline(False).decode() + final_values = auth.get_final_values(intermediate_values) + s.sendline(final_values.encode()) + self.auth_token = s.recvline(False).decode() + self.shared_key = auth.get_shared_key() + s.close() + return True + + def get_token(self): + token_client = authlib.TicketClient(self.username, self.service, self.auth_token, self.shared_key) + user_token = token_client.get_user_token() + s = self.open_connection() + s.sendline(b"TOKEN") + assert s.recvline(False) == b"OK" + s.sendline(user_token.encode() + b"." + self.auth_token.encode()) + key_token, service_token = s.recvline(False).decode().split(".") + s.close() + return token_client.finalize_token(key_token, service_token) diff --git a/checkers/3/authlib.cpython-39-x86_64-linux-gnu.so b/checkers/3/authlib.cpython-39-x86_64-linux-gnu.so new file mode 100755 index 0000000..3b6ed49 Binary files /dev/null and b/checkers/3/authlib.cpython-39-x86_64-linux-gnu.so differ diff --git a/checkers/3/checker.py b/checkers/3/checker.py new file mode 100644 index 0000000..787ae55 --- /dev/null +++ b/checkers/3/checker.py @@ -0,0 +1,389 @@ +#!/usr/bin/env python3 + +# Do not make modification to checklib.py (except for debug), it can be replaced at any time +import checklib +import random +import string +from hashlib import sha256 +import zlib +import base64 +import json +from Crypto.Cipher import AES +import os +os.environ["PWNLIB_NOTERM"] = "1" + +from pwn import * +from service2_client import Client as AuthClient + +context.timeout = 5 +context.log_level = "error" + +data = checklib.get_data() +action = data['action'] +rd = data['round'] +team_id = data['teamId'] +team_addr = '10.60.' + team_id + '.1' +port = 1236 +auth_port = 1234 +service_name = "EncryptedNotes" + + +def random_string(min, max): + letters = string.ascii_letters + string.digits + return ''.join(random.choice(letters) for i in range(random.randint(min, max))) + + +# Auth stuff +def get_random_creds(): + username = random_string(8, 24) + password = random_string(8, 24) + return username, password + + +def register_random_user(): + try: + username, password = get_random_creds() + c = AuthClient(team_addr, auth_port, username, password, "2") + c.register() + return username, password + except Exception as e: + checklib.quit(checklib.Status.DOWN, + 'Can\'t register user on auth service', str(e)) + + +def get_token(username, password): + try: + c = AuthClient(team_addr, auth_port, username, password, "2") + c.login() + token = c.get_token() + return token + except Exception as e: + checklib.quit(checklib.Status.DOWN, + 'Can\'t get a token from auth service', str(e)) + + +# OT stuff +class Receiver: + def __init__(self, b): + self.b = b + + def round2(self, pk, x): + N, e = pk + n = len(x) + assert n == len(self.b) + + v = [] + k = [] + for i in range(n): + kk = random.randrange(1 << 2048) + k.append(kk) + cur = x[i][self.b[i]] + pow(kk, e, N) + v.append(cur % N) + self.k = k + self.N = N + return v + + def decode(self, c): + n = len(c) + assert n == len(self.b) + + m = [] + for i in range(n): + mm = (c[i][self.b[i]]-self.k[i]) % self.N + m.append(mm) + return m + + +# GC evaluation +VAL_LENGTH = 5 +PAD_LENGTH = 3 + + +def xor(a, b): + return bytes(x ^ y for x, y in zip(a, b)) + + +def H(k): + return sha256(k).digest() + + +def dec(k, x): + k = H(k) + val = xor(k, x) + if val[:PAD_LENGTH] == b"\0"*PAD_LENGTH: + return val[PAD_LENGTH:] + + +def decode_gate(opv, g, ins, to_hex): + if to_hex: + ins = [bytes.fromhex(x) for x in ins] + g = [bytes.fromhex(r) for r in g] + if opv == "INV": + res = ins[0] + elif opv == "XOR": + res = xor(ins[0], ins[1]) + else: + for x in g: + k = b"".join(ins) + val = dec(k, x) + if val is not None: + res = val + break + if to_hex: + return res.hex() + return res + + +def evaluate(garbled_circuit, inputs, wires_out, to_hex=True): + enc_A, enc_B = inputs + vals = enc_A+enc_B+[None]*len(garbled_circuit) + for g in garbled_circuit: + idx, opv, ins, gate = g + k = [vals[i] for i in ins] + cur = decode_gate(opv, gate, k, to_hex) + assert cur is not None + vals[idx] = cur + + n_outs = len(wires_out) + vals_out = vals[-n_outs:] + out = [w.index(v) for v, w in zip(vals_out, wires_out)] + return out + + +# Client +def bytes2bits(b): + bits = ''.join(f'{x:08b}' for x in b) + return list(map(int, bits)) + + +def bits2bytes(arr): + n = len(arr) + assert n % 8 == 0 + nbytes = n // 8 + s = "".join(map(str, arr)) + return int.to_bytes(int(s, 2), nbytes, "big") + + +class Client: + def __init__(self, host, token): + self.r = remote(host, port) + self.r.recvlines(1) + self.r.sendlineafter(b"token: ", token.encode()) + + def set_keyword(self, keyword): + self.r.recvlines(6) + self.r.sendlineafter(b"> ", b"1") + self.r.sendlineafter(b"secret: ", keyword.encode()) + + def set_public(self, data): + self.r.recvlines(6) + self.r.sendlineafter(b"> ", b"2") + self.r.sendlineafter(b"text: ", data.encode()) + + def get_public(self, user): + self.r.recvlines(6) + self.r.sendlineafter(b"> ", b"4") + self.r.sendlineafter(b"user: ", user.encode()) + return self.r.recvline(False).decode() + + def run_function(self, choice, user, my_in): + self.r.recvlines(6) + self.r.sendlineafter(b"> ", b"3") + self.r.sendlineafter(b"user: ", user.encode()) + self.r.recvlines(3) + self.r.sendlineafter(b"> ", str(choice).encode()) + + data = self.r.recvline(False) + tmp = zlib.decompress(base64.b64decode(data)) + obj = json.loads(tmp.decode()) + circ = obj["circuit"] + + N, e, x = obj["ot"]["N"], obj["ot"]["e"], obj["ot"]["x"] + in_B = bytes2bits(my_in) + assert len(in_B) == 128 + + if choice == 2: + in_B = in_B[::-1] + receiver = Receiver(in_B) + v = receiver.round2((N, e), x) + vdict = json.dumps({"v": v}) + self.r.sendline(vdict.encode()) + data = self.r.recvline(False) + obj = json.loads(data.decode()) + c = obj["c"] + m = receiver.decode(c) + + enc_B = [f'{x:010x}' for x in m] + + res = evaluate(circ["gates"], (circ["enc_A"], + enc_B), circ["wires_out"]) + if choice == 2: + res = res[::-1] + return bits2bytes(res) + + +# SLA functions +def sla_user_setkeyword(): + try: + username, password = register_random_user() + token = get_token(username, password) + c = Client(team_addr, token) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t login', str(e)) + try: + key = random_string(16, 16) + rdata = random_string(32, 32) + c.set_keyword(key) + c.set_public(rdata) + return username, key + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t put data', str(e)) + + +def sla_user_echo(target_user, key): + try: + username, password = register_random_user() + token = get_token(username, password) + c = Client(team_addr, token) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t login', str(e)) + try: + rdata = os.urandom(16) + res = c.run_function(1, target_user, rdata) + if res != rdata: + checklib.quit(checklib.Status.DOWN, 'Echo function not working', + f"sla_user_echo input={rdata.hex()} output={res.hex()}") + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t run echo function', str(e)) + + +def sla_user_encrypt(target_user, key): + try: + username, password = register_random_user() + token = get_token(username, password) + c = Client(team_addr, token) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t login', str(e)) + try: + rdata = os.urandom(16) + #print("running GC") + res = c.run_function(2, target_user, rdata) + cipher = AES.new(key.encode(), AES.MODE_ECB) + pt = cipher.decrypt(res) + if pt != rdata: + checklib.quit(checklib.Status.DOWN, 'Encrypt function not working', + f"sla_user_encrypt input={rdata.hex()} key={key} output={res.hex()}") + except Exception as e: + checklib.quit(checklib.Status.DOWN, + 'Can\'t run encrypt function', str(e)) + + +def sla_user_readpublic(target_user, key): + try: + username, password = register_random_user() + token = get_token(username, password) + c = Client(team_addr, token) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t login', str(e)) + try: + res = c.get_public(target_user) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t read public data', str(e)) + + +# Check SLA +def check_sla(): + + user, key = sla_user_setkeyword() + slas = [sla_user_echo, sla_user_encrypt, sla_user_readpublic] + + f = random.choice(slas) + f(user, key) + + # If OK + checklib.quit(checklib.Status.OK, 'OK') + + +# Put the flag using the flag as the seed for random stuff +def put_flag(): + flag = data["flag"] + try: + random.seed("123"+flag+"789") + key = random_string(16, 16) + username, password = register_random_user() + token = get_token(username, password) + c = Client(team_addr, token) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t login', str(e)) + + try: + #print("putting flag", username, key) + c.set_keyword(key) + c.set_public(flag) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t put data', str(e)) + + checklib.post_flag_id(service_name, team_addr, username) + + # If OK + checklib.quit(checklib.Status.OK, 'OK') + + +# Get flag functions +def get_flag_pubdata(c, target_user, key): + try: + pubdata = c.get_public(target_user) + ct = bytes.fromhex(pubdata) + cipher = AES.new(key.encode(), AES.MODE_ECB) + pt = cipher.decrypt(ct) + if pt.decode() != data["flag"]: + checklib.quit(checklib.Status.DOWN, 'Incorrect public data for flag', + f"get_flag_pubdata pubdata={pubdata.hex()} key={key} pt={pt.hex()}") + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t read public data', str(e)) + + +def get_flag_AES(c, target_user, key): + flag = data["flag"].encode() + try: + ct = c.run_function(2, target_user, flag[:16]) + #print("run GC") + pubdata = c.get_public(target_user) + if ct.hex() != pubdata[:32]: + checklib.quit(checklib.Status.DOWN, 'Incorrect encryption function for flag', + f"get_flag_AES pubdata={pubdata.hex()} ct={ct.hex()}") + except Exception as e: + checklib.quit(checklib.Status.DOWN, + 'Can\'t run encryption function', str(e)) + + +# Check if the flag still exists, use the flag as the seed for random stuff as for put flag +def get_flag(): + flag = data["flag"] + try: + username, password = register_random_user() + token = get_token(username, password) + c = Client(team_addr, token) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Can\'t login', str(e)) + + checks = [get_flag_pubdata, get_flag_AES] + check = random.choice(checks) + + random.seed("123"+flag+"789") + key = random_string(16, 16) + username, password = get_random_creds() + check(c, username, key) + + # If OK + checklib.quit(checklib.Status.OK, 'OK') + + +if __name__ == "__main__": + + if action == checklib.Action.CHECK_SLA.name: + check_sla() + elif action == checklib.Action.PUT_FLAG.name: + put_flag() + elif action == checklib.Action.GET_FLAG.name: + get_flag() diff --git a/checkers/3/checklib.py b/checkers/3/checklib.py new file mode 100644 index 0000000..4c573fa --- /dev/null +++ b/checkers/3/checklib.py @@ -0,0 +1,53 @@ +import sys +from enum import Enum +import requests +import os + +TOKEN = 'PRIVATE-TOKEN' + + +class Status(Enum): + OK = 101 + DOWN = 104 + ERROR = 110 + + +class Action(Enum): + CHECK_SLA = 'CHECK_SLA' + PUT_FLAG = 'PUT_FLAG' + GET_FLAG = 'GET_FLAG' + + def __str__(self): + return str(self.value) + + +def get_data(): + data = { + 'action': os.environ['ACTION'], + 'teamId': os.environ['TEAM_ID'], + 'round': os.environ['ROUND'] + } + + if data['action'] == Action.PUT_FLAG.name or data['action'] == Action.GET_FLAG.name: + data['flag'] = os.environ['FLAG'] + + return data + + +def quit(exit_code, comment='', debug=''): + if isinstance(exit_code, Status): + exit_code = exit_code.value + + print(comment) + print(debug, file=sys.stderr) + exit(exit_code) + + +def post_flag_id(service_id, team_id, flag_id): + requests.post('http://flagid:8081/postFlagId', json={ + 'token': TOKEN, + 'serviceId': service_id, + 'teamId': team_id, + 'round': int(os.environ['ROUND']), + 'flagId': flag_id + }) diff --git a/checkers/3/service2_client.py b/checkers/3/service2_client.py new file mode 100644 index 0000000..497c204 --- /dev/null +++ b/checkers/3/service2_client.py @@ -0,0 +1,66 @@ +import authlib +from pwn import * + +context.log_level = "error" + +class Client(): + def __init__(self, server_ip, server_port, username, password, service): + self.server_addr = server_ip + self.server_port = server_port + self.username = username + self.password = password + self.service = service + self.auth_token = None + self.shared_key = None + + def open_connection(self): + s = remote(self.server_addr, self.server_port) + assert s.recvline(False) == b"HELLO" + s.sendline(b"HELLO") + assert s.recvline(False) == b"OK" + return s + + def register(self): + s = self.open_connection() + s.sendline(b"REG") + assert s.recvline(False) == b"OK" + s.sendline(self.username.encode()) + resp = s.recvline(False) + if resp != b"OK": + return False + s.sendline(self.password.encode()) + resp = s.recvline(False) + s.close() + if resp != b"OK": + return False + return True + + def login(self): + auth = authlib.AuthClient(self.username, self.password) + initial_values = auth.get_starting_values() + s = self.open_connection() + s.sendline(b"AUTH") + assert s.recvline(False) == b"OK" + s.sendline(self.username.encode()) + resp = s.recvline(False) + if resp != b"OK": + return False + s.sendline(initial_values.encode()) + intermediate_values = s.recvline(False).decode() + final_values = auth.get_final_values(intermediate_values) + s.sendline(final_values.encode()) + self.auth_token = s.recvline(False).decode() + self.shared_key = auth.get_shared_key() + s.close() + return True + + def get_token(self): + token_client = authlib.TicketClient(self.username, self.service, self.auth_token, self.shared_key) + user_token = token_client.get_user_token() + s = self.open_connection() + s.sendline(b"TOKEN") + assert s.recvline(False) == b"OK" + s.sendline(user_token.encode() + b"." + self.auth_token.encode()) + key_token, service_token = s.recvline(False).decode().split(".") + s.close() + return token_client.finalize_token(key_token, service_token) diff --git a/checkers/4/.gitignore b/checkers/4/.gitignore new file mode 100644 index 0000000..07f43b8 --- /dev/null +++ b/checkers/4/.gitignore @@ -0,0 +1 @@ +data/* \ No newline at end of file diff --git a/checkers/4/authlib.cpython-39-x86_64-linux-gnu.so b/checkers/4/authlib.cpython-39-x86_64-linux-gnu.so new file mode 100755 index 0000000..3b6ed49 Binary files /dev/null and b/checkers/4/authlib.cpython-39-x86_64-linux-gnu.so differ diff --git a/checkers/4/checker.py b/checkers/4/checker.py new file mode 100644 index 0000000..2ac7baf --- /dev/null +++ b/checkers/4/checker.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python3 + +# Do not make modification to checklib.py (except for debug), it can be replaced at any time +import checklib +import requests +import random +import string +from hashlib import sha256 +import json +import os +import functools +from bs4 import BeautifulSoup +import errno +os.environ["PWNLIB_NOTERM"] = "1" +from service2_client import Client as AuthClient + +data = checklib.get_data() +action = data['action'] +rd = data['round'] +team_id = data['teamId'] +service_name = "ExamPortal" + +port = 1237 +auth_port = 1234 +team_ip = f"10.60.{team_id}.1" +team_addr = f"http://{team_ip}:{port}" + + +# Create directory to store round data. +data_dir = 'data' +try: + os.makedirs(data_dir) +except OSError as e: + if e.errno != errno.EEXIST: + raise + + +# Read stored data for team-round +def read_round_data(): + try: + fl = sha256(data['flag'].encode()).hexdigest() + with open(f'{data_dir}/{team_id}-{fl}.json', 'r') as f: + raw = f.read() + return json.loads(raw) + except (FileNotFoundError, json.decoder.JSONDecodeError): + return {} + + +# Store data for team-round +def store_round_data(d): + raw = json.dumps(d) + fl = sha256(data['flag'].encode()).hexdigest() + + with open(f'{data_dir}/{team_id}-{fl}.json', 'w') as f: + f.write(raw) + + +def random_string(min, max): + letters = string.ascii_letters + string.digits + return ''.join(random.choice(letters) for i in range(random.randint(min, max))) + + +# Auth stuff +def get_random_creds(): + username = random_string(8, 24) + password = random_string(8, 24) + return username, password + + +def register_random_user(): + username, password = get_random_creds() + register_user(username, password) + return username, password + + +def register_user(username, password): + try: + c = AuthClient(team_ip, auth_port, username, password, "3") + c.register() + except Exception as e: + checklib.quit(checklib.Status.DOWN, + 'Can\'t register user on auth service', str(e)) + + +def get_token(username, password): + try: + c = AuthClient(team_ip, auth_port, username, password, "3") + c.login() + token = c.get_token() + return token + except Exception as e: + checklib.quit(checklib.Status.DOWN, + 'Can\'t get a token from auth service', str(e)) + + +def get_sess(): + s = requests.Session() + s.request = functools.partial(s.request, timeout=5) + with open('user_agents.txt', 'r') as f: + ua_list = f.readlines() + s.headers.update({'User-Agent': random.choice(ua_list)[:-1]}) + return s + + +def do_login(sess, token, tfa=None): + try: + r = sess.post(team_addr+"/login.php", + data={"token": token, "2fa": tfa}) + if "Token verification failed" in r.text: + checklib.quit(checklib.Status.DOWN, 'Token decoding failed', + f"do_login {team_addr}/login.php (token={token}, 2fa={tfa}): {r.text}") + if "Authentication failed!" in r.text: + checklib.quit(checklib.Status.DOWN, 'Can\'t login', + f"do_login {team_addr}/login.php (token={token}, 2fa={tfa}): {r.text}") + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Login failed', + f"do_login (token={token}, tfa={tfa}): {e}") + + +# Check SLA +def check_sla(): + + sess = get_sess() + username, password = register_random_user() + token = get_token(username, password) + tfa = None + if random.randrange(2) == 0: + tfa = random_string(10, 10) + + try: + do_login(sess, token, tfa) + + r = sess.get(team_addr) + if username not in r.text: + checklib.quit(checklib.Status.DOWN, 'Can\'t login', + f"do_login {team_addr}/index.php : {r.text}") + + r = sess.get(team_addr+"/exam_list.php") + if r.status_code != 200: + checklib.quit(checklib.Status.DOWN, 'Can\'t list exams', + f"check_sla {team_addr}/exam_list.php : {r.status_code}\n{r.text}") + + if tfa is not None: + sess = get_sess() + do_login(sess, token, tfa) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Login failed', f"check_sla : {e}") + + checklib.quit(checklib.Status.OK, 'OK') + + +QQ = [ + {"question": "What is the answer?", "answers": [ + "42", "The onion", "Yes", "answer"]}, + {"question": "Is pwn funny?", "answers": ["no", "no", "no", "no"]}, + {"question": "What's the best post-quantum candidate?", + "answers": ["NTRU", "McEliece", "SIKE", "Rainbow"]}, + {"question": "What's the best pwning tool?", "answers": [ + "gef", "AFL++", "pwntools", "calc.exe"]}, + {"question": "This website is vulnerable to:", "answers": [ + "SQLi", "XSS", "CSRF", "Social engineering"]}, + {"question": "What's your favourite CVE?", "answers": [ + "CVE-2021-44228", "CVE-2022-0778", "CVE-2021-26855", "CVE-2014-0160"]}, + {"question": "Which one of the following describes spam?", "answers": ["Gathering information about a person or organisation without their knowledge", + "Performing an unauthorised, usually malicious, action such as erasing files", "Putting unnecessary load on the network by making copies of files", "Sending unwanted bulk messages"]}, + {"question": "Which one of the following describes a computer hacker?", "answers": ["A skilled programmer who uses authorised access to exploit information available on a computer", + "A skilled programmer who secretly invades computers without authorisation", "A skilled programmer who writes programs to train new employees", "A skilled programmer who helps in the installation of new software for an organisation"]}, + {"question": "Which one of the following should be used to change your password?", "answers": [ + "Control Panel", "Anti-virus software", "Windows Firewall", "Internet Explorer"]}, + {"question": "Which one of the following would be considered the BEST way to store a PIN number?", "answers": [ + "Keep a written note of it with you bank cards", "Store it on your computer", "Memorise it", "Keep a written note of it in your office drawer"]}, + {"question": "What information do you need to set up a wireless access point?", + "answers": ["SSID", "MAC address", "IP address", "ARP"]}, + {"question": "Which of the following is a password hashing algorithm?", + "answers": ["AES", "MD4", "PGP", "3DES"]}, + {"question": "Which of the following is a private search engine?", + "answers": ["Bing", "Google", "Duckduckgo", "Yahoo"]}, + {"question": "Which cybersecurity principle is most important when attempting to trace the source of malicious activity?", + "answers": ["Availability", "Integrity", "Nonrepudiation", "Confidentiality"]}, + {"question": "Updates in cloud-computing environments can be rolled out quickly because the environment is:", + "answers": ["Homogeneous", "Distributed", "Diversified", "Secure"]}, + {"question": "The attack mechanism directed against a system is commonly called a(n):", "answers": [ + "Exploit", "Vulnerability", "Payload", "Attack vector"]}, + {"question": "If security is a real concern on your network, what length do most experts recommend as the minimum for password length?", + "answers": ["10", "9", "8", "6"]}, + {"question": "Which of the following is an asymmetric algorithm based on calculating logarithms?", + "answers": ["ECC", "Diffie-Hellman", "El Gamal", "RSA"]} +] +NAMES = ["Machine learning", "Quantum cryptography", "Biometrics", "Network Security", "Law and data", "Digital forensics", "Mobile and IOT security", "Smart contracts hacking", "Wireless networks", + "Big data computing", "APT monitoring", "Cyber threat analysis", "Advanced reverse engineering", "Financial Accounting", "Virtualization & Cloud Security", "Ethical Hacking", "Malware Analysis"] + + +def create_random_exam(): + name = random.choice(NAMES) + " " + str(random.randint(1, 4)) + questions = random.sample(QQ, 10) + correct = random.choices(["A", "B", "C", "D"], k=10) + return name, questions, correct + + +def check_correct_in_page(html, answers, correct): + # print(html) + soup = BeautifulSoup(html, 'html.parser') + html_ans = soup.find_all("ul")[1:] + assert len(html_ans) == len(answers) + for i, x in enumerate(html_ans): + cur_ans = x.find_all("li") + # print(cur_ans) + assert len(cur_ans) == 4 + for j, y in enumerate(cur_ans): + val = y.text.strip() + assert val == answers[i][j] + if ["A", "B", "C", "D"].index(correct[i]) == j: + exp_class = "bg-success" + else: + exp_class = "bg-danger" + assert exp_class == y["class"][2] + + +# Put the flag using the flag as the seed for random stuff +def put_flag(): + flag = data['flag'] + + sess = get_sess() + username, password = register_random_user() + token = get_token(username, password) + tfa = random_string(10, 10) + do_login(sess, token, tfa) + + name, questions, correct = create_random_exam() + + exam = {} + exam["name"] = name + exam["prize"] = flag + for i in range(10): + exam[f"question_{i}"] = questions[i]["question"] + exam[f"correct_{i}"] = correct[i] + assert len(questions[i]["answers"]) == 4 + for j in range(4): + exam[f"answer_{i}_{j}"] = questions[i]["answers"][j] + try: + r = sess.post(team_addr+"/exam_create.php", data=exam) + if r.status_code != 200: + checklib.quit(checklib.Status.DOWN, 'Can\'t create exam', + f"put_flag {team_addr}/exam_create.php (exam={exam}): {r.status_code}\n{r.text}") + id = r.url.split("=")[1] + if "Unauthorized" in r.text or "No such exam" in r.text: + checklib.quit(checklib.Status.DOWN, 'Can\'t view exam', + f"put_flag {team_addr}/exam_create.php : {r.status_code}\n{r.text}") + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Error in creating exam', str(e)) + + store_round_data({"creds": {"username": username, "password": password, "tfa": tfa}, "exam": { + "id": id, "name": name, "questions": questions, "correct": correct}}) + + try: + check_correct_in_page(r.text, [x["answers"] + for x in questions], correct) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Malformed exam', str(e)) + + checklib.post_flag_id(service_name, team_ip, id) + + # If OK + checklib.quit(checklib.Status.OK, 'OK') + + +def get_flag_view_exam(exam, creds): + id, name, questions, correct = exam["id"], exam["name"], exam["questions"], exam["correct"] + token, tfa = creds + sess = get_sess() + do_login(sess, token, tfa) + r = sess.get(team_addr+"/exam_view.php", params={"id": id}) + if "Unauthorized" in r.text or "No such exam" in r.text: + checklib.quit(checklib.Status.DOWN, 'Can\'t view exam', + f"get_flag_view_exam {team_addr}/exam_view.php (id={id}): {r.status_code}\n{r.text}") + try: + check_correct_in_page(r.text, [x["answers"] + for x in questions], correct) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Malformed exam', str(e)) + + +def get_flag_solve_exam(exam, creds): + id, name, questions, correct = exam["id"], exam["name"], exam["questions"], exam["correct"] + username, password = creds + token = get_token(username, password) + sess = get_sess() + do_login(sess, token, None) + exam_ans = {"id": id, "answers": correct} + r = sess.post(team_addr+"/exam_submit.php", json=exam_ans) + if data["flag"] not in r.json()["msg"]: + checklib.quit(checklib.Status.DOWN, 'Can\'t solve exam', + f"get_flag_solve_exam {team_addr}/exam_submit.php (exam_ans={exam_ans}): {r.status_code}\n{r.text}") + + +# Check if the flag still exists, use the flag as the seed for random stuff as for put flag +def get_flag(): + round_data = read_round_data() + username = round_data["creds"]["username"] + password = round_data["creds"]["password"] + tfa = round_data["creds"]["tfa"] + exam = round_data["exam"] + check = random.randrange(2) + + try: + if check == 0: + token = get_token(username, password) + get_flag_view_exam(exam, (token, tfa)) + if check == 1: + u2, p2 = register_random_user() + get_flag_solve_exam(exam, (u2, p2)) + except Exception as e: + checklib.quit(checklib.Status.DOWN, 'Get flag failed', str(e)) + + # If OK + checklib.quit(checklib.Status.OK, 'OK') + + +if __name__ == "__main__": + + if action == checklib.Action.CHECK_SLA.name: + check_sla() + elif action == checklib.Action.PUT_FLAG.name: + put_flag() + elif action == checklib.Action.GET_FLAG.name: + get_flag() diff --git a/checkers/4/checklib.py b/checkers/4/checklib.py new file mode 100644 index 0000000..4c573fa --- /dev/null +++ b/checkers/4/checklib.py @@ -0,0 +1,53 @@ +import sys +from enum import Enum +import requests +import os + +TOKEN = 'PRIVATE-TOKEN' + + +class Status(Enum): + OK = 101 + DOWN = 104 + ERROR = 110 + + +class Action(Enum): + CHECK_SLA = 'CHECK_SLA' + PUT_FLAG = 'PUT_FLAG' + GET_FLAG = 'GET_FLAG' + + def __str__(self): + return str(self.value) + + +def get_data(): + data = { + 'action': os.environ['ACTION'], + 'teamId': os.environ['TEAM_ID'], + 'round': os.environ['ROUND'] + } + + if data['action'] == Action.PUT_FLAG.name or data['action'] == Action.GET_FLAG.name: + data['flag'] = os.environ['FLAG'] + + return data + + +def quit(exit_code, comment='', debug=''): + if isinstance(exit_code, Status): + exit_code = exit_code.value + + print(comment) + print(debug, file=sys.stderr) + exit(exit_code) + + +def post_flag_id(service_id, team_id, flag_id): + requests.post('http://flagid:8081/postFlagId', json={ + 'token': TOKEN, + 'serviceId': service_id, + 'teamId': team_id, + 'round': int(os.environ['ROUND']), + 'flagId': flag_id + }) diff --git a/checkers/4/service2_client.py b/checkers/4/service2_client.py new file mode 100644 index 0000000..497c204 --- /dev/null +++ b/checkers/4/service2_client.py @@ -0,0 +1,66 @@ +import authlib +from pwn import * + +context.log_level = "error" + +class Client(): + def __init__(self, server_ip, server_port, username, password, service): + self.server_addr = server_ip + self.server_port = server_port + self.username = username + self.password = password + self.service = service + self.auth_token = None + self.shared_key = None + + def open_connection(self): + s = remote(self.server_addr, self.server_port) + assert s.recvline(False) == b"HELLO" + s.sendline(b"HELLO") + assert s.recvline(False) == b"OK" + return s + + def register(self): + s = self.open_connection() + s.sendline(b"REG") + assert s.recvline(False) == b"OK" + s.sendline(self.username.encode()) + resp = s.recvline(False) + if resp != b"OK": + return False + s.sendline(self.password.encode()) + resp = s.recvline(False) + s.close() + if resp != b"OK": + return False + return True + + def login(self): + auth = authlib.AuthClient(self.username, self.password) + initial_values = auth.get_starting_values() + s = self.open_connection() + s.sendline(b"AUTH") + assert s.recvline(False) == b"OK" + s.sendline(self.username.encode()) + resp = s.recvline(False) + if resp != b"OK": + return False + s.sendline(initial_values.encode()) + intermediate_values = s.recvline(False).decode() + final_values = auth.get_final_values(intermediate_values) + s.sendline(final_values.encode()) + self.auth_token = s.recvline(False).decode() + self.shared_key = auth.get_shared_key() + s.close() + return True + + def get_token(self): + token_client = authlib.TicketClient(self.username, self.service, self.auth_token, self.shared_key) + user_token = token_client.get_user_token() + s = self.open_connection() + s.sendline(b"TOKEN") + assert s.recvline(False) == b"OK" + s.sendline(user_token.encode() + b"." + self.auth_token.encode()) + key_token, service_token = s.recvline(False).decode().split(".") + s.close() + return token_client.finalize_token(key_token, service_token) diff --git a/checkers/4/user_agents.txt b/checkers/4/user_agents.txt new file mode 100644 index 0000000..ff9b336 --- /dev/null +++ b/checkers/4/user_agents.txt @@ -0,0 +1 @@ +python-requests/2.28.0 diff --git a/checkers/5/checker.py b/checkers/5/checker.py new file mode 100644 index 0000000..949beac --- /dev/null +++ b/checkers/5/checker.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 +import os + +os.environ["FLAG_STORE"] = "1" +os.environ["PWNLIB_NOTERM"] = "1" + +from service3_common import * diff --git a/checkers/5/checklib.py b/checkers/5/checklib.py new file mode 100644 index 0000000..4c573fa --- /dev/null +++ b/checkers/5/checklib.py @@ -0,0 +1,53 @@ +import sys +from enum import Enum +import requests +import os + +TOKEN = 'PRIVATE-TOKEN' + + +class Status(Enum): + OK = 101 + DOWN = 104 + ERROR = 110 + + +class Action(Enum): + CHECK_SLA = 'CHECK_SLA' + PUT_FLAG = 'PUT_FLAG' + GET_FLAG = 'GET_FLAG' + + def __str__(self): + return str(self.value) + + +def get_data(): + data = { + 'action': os.environ['ACTION'], + 'teamId': os.environ['TEAM_ID'], + 'round': os.environ['ROUND'] + } + + if data['action'] == Action.PUT_FLAG.name or data['action'] == Action.GET_FLAG.name: + data['flag'] = os.environ['FLAG'] + + return data + + +def quit(exit_code, comment='', debug=''): + if isinstance(exit_code, Status): + exit_code = exit_code.value + + print(comment) + print(debug, file=sys.stderr) + exit(exit_code) + + +def post_flag_id(service_id, team_id, flag_id): + requests.post('http://flagid:8081/postFlagId', json={ + 'token': TOKEN, + 'serviceId': service_id, + 'teamId': team_id, + 'round': int(os.environ['ROUND']), + 'flagId': flag_id + }) diff --git a/checkers/5/cmd_paths.py b/checkers/5/cmd_paths.py new file mode 100644 index 0000000..bdafd51 --- /dev/null +++ b/checkers/5/cmd_paths.py @@ -0,0 +1,41 @@ +cmd_paths = { + "or" : "lllll", + "add" : "llll", + "dec" : "lllr", + "and" : "lll", + "dup" : "llrll", + "hex" : "llrl", + "oct" : "llrr", + "mul" : "llr", + "div" : "ll", + "ver" : "lrlll", + "xor" : "lrll", + "eval" : "lrlr", + "drop" : "lrl", + "pick" : "lrrll", + "quit" : "lrrl", + "time" : "lrrr", + "swap" : "lrr", + "help" : "lr", + "sub" : "l", + "clear" : "rllll", + "ps_on" : "rlll", + "random" : "rllr", + "ps_off" : "rll", + "set_var" : "rlrll", + "debug_on" : "rlrl", + "next_base" : "rlrr", + "debug_off" : "rlr", + "get_var" : "rl", + "todohxd" : "rrlll", + "id_to_ints" : "rrll", + "int_to_str" : "rrlr", + "ints_to_id" : "rrl", + "ints_to_str" : "rrrl", + "time_to_str" : "rrrr", + "str_to_ints" : "rrr", + "str_to_int" : "rr", + "ps_status" : "r", + "vars" : "", +} + diff --git a/checkers/5/cmd_tree.py b/checkers/5/cmd_tree.py new file mode 100644 index 0000000..c7f56cb --- /dev/null +++ b/checkers/5/cmd_tree.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 + +commands = [ + ("dup", " [n]", "Duplicate the top of the stack n times (default n=1)"), + ("swap", "", "Swap the top two items"), + ("pick", " n", "Place a copy of the n-th item on the top"), + ("help", "", "Print this help"), + ("drop", "", "Drop the top element"), + ("clear", "", "Drop all elements"), + ("get_var", "", "Get the value of a variable; the identifier is the top element"), + ("set_var", "", "Set the value of a variable; the identifier is the top element, the value is the 2nd element",), + ("[todo]hxd", "", None), + ("debug_on", "", "Enable debug-mode (only available in development-mode)"), + ("debug_off", "", "Disable debug-mode (only available in development-mode)"), + ("vars", "", "Print all the variables (only available in debug-mode)"), + ("add", "", "Add the top two elements"), + ("sub", "", "Subtract the top two elements"), + ("div", "", "Divide the top two elements"), + ("mul", "", "Multiply the top two elements"), + ("and", "", "Bitwise-And of the top two elements"), + ("or", "", "Bitwise-Or of the top two elements"), + ("xor", "", "Bitwise-Xor of the top two elements"), + ("quit", " (or{{nft['data']}}
+ View +You own this nft!
+{{nft['data']}}
+ + +An nft. Buy it to see its secrets!
+ + + See owner profile + +An nft. Only the owner can see its contents!
+ View +All this user's transactions
+