commit 61f5bc4d176a9dcae7cbc654fce23cb3ec9c272f Author: Giulio Date: Tue May 14 16:29:52 2019 +0200 Import diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af6d502 --- /dev/null +++ b/.gitignore @@ -0,0 +1,123 @@ +# 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/ +pip-wheel-metadata/ +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 +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.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 + +# celery beat schedule file +celerybeat-schedule + +# 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/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..f7ae8ef --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +Basic challenges developed for Hackmeeting 0x14. +https://hm.capturetheflag.it diff --git a/circus.ini b/circus.ini new file mode 100644 index 0000000..ea0f3a9 --- /dev/null +++ b/circus.ini @@ -0,0 +1,9 @@ +[watcher:crypto2] +cmd = /usr/local/bin/gunicorn -w 3 --bin 192.168.0.12:5000 crypto2:app +working_dir = /home/ctf/rc4 +send_hup = true + +[watcher:web] +cmd = /usr/local/bin/gunicorn -w 3 --bin 192.168.0.12:7000 web:app +working_dir = /home/ctf/evilcorp +send_hup = true diff --git a/ctf.tgz b/ctf.tgz new file mode 100644 index 0000000..384f9d4 Binary files /dev/null and b/ctf.tgz differ diff --git a/ecb/crypto1/app.py b/ecb/crypto1/app.py new file mode 100644 index 0000000..ef8e7a8 --- /dev/null +++ b/ecb/crypto1/app.py @@ -0,0 +1,43 @@ +import socket +import base64 +import threading +from Crypto.Cipher import AES + +flag = 'HM{3cb_0r4cl3}' +key = '12345678abcdefgh' + +BIND_IP = '192.168.0.12' +BIND_PORT = 8000 + +def pad(raw): + + if (len(raw) % 16 == 0): + return raw + padding_required = 16 - (len(raw) % 16) + padChar = b'\x00' + data = raw.encode('ascii') + padding_required * padChar + return data + +def handle_client(client_socket): + client_socket.send(bytes('I will send you AES(+flag):\n'.encode('ascii'))) + request = client_socket.recv(1024) + string = request.decode('ascii').rstrip() + cipher = AES.AESCipher(key, AES.MODE_ECB) + ciphertext = base64.b64encode(cipher.encrypt(pad(string+flag))) + client_socket.send(ciphertext) + client_socket.close() + +def tcp_server(): + server = socket.socket( socket.AF_INET, socket.SOCK_STREAM) + server.bind(( BIND_IP, BIND_PORT)) + server.listen(5) + print("[*] Listening on %s:%d" % (BIND_IP, BIND_PORT)) + + while 1: + client, addr = server.accept() + print("[*] Accepted connection from: %s:%d" %(addr[0], addr[1])) + client_handler = threading.Thread(target=handle_client, args=(client,)) + client_handler.start() + +if __name__ == '__main__': + tcp_server() diff --git a/ecb/crypto1/solution.py b/ecb/crypto1/solution.py new file mode 100644 index 0000000..ccf0aa4 --- /dev/null +++ b/ecb/crypto1/solution.py @@ -0,0 +1,37 @@ +from base64 import b64decode +import socket +from Crypto.Cipher import AES + +server = '127.0.0.1' +port = 8000 + +chars = 'abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ{}_' +flag_blocks = 2 +block_size = 16 +index = {} + +flag = '' + +for i in range(block_size-1, 1, -1): + for j in chars: + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((server, port)) + data = s.recv(4096) + s.send(('a'*i).encode('ascii')) + value = b64decode(s.recv(4096)).hex()[0:32] + s.close() + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((server, port)) + data = s.recv(4096) + s.send(('a'*i+flag+j).encode('ascii')) + test = b64decode(s.recv(4096)).hex()[0:32] + s.close() + + if value == test: + flag += j + break + print(flag) + + diff --git a/evilcorp/evilcorp.sqlite3 b/evilcorp/evilcorp.sqlite3 new file mode 100755 index 0000000..cf313bc Binary files /dev/null and b/evilcorp/evilcorp.sqlite3 differ diff --git a/evilcorp/web/__init__.py b/evilcorp/web/__init__.py new file mode 100644 index 0000000..2ef0496 --- /dev/null +++ b/evilcorp/web/__init__.py @@ -0,0 +1,248 @@ +from flask import Flask, g, request, render_template, redirect, current_app, send_file +from flask_argon2 import Argon2 +from io import BytesIO +import jwt +import time +import sqlite3 +import secrets + +app = Flask(__name__) +app.config['PRIVATE_KEY'] = ''' +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCo0oR5UXusQs7tvgcT5EOLB+2JaKXmmip3ViGijLHku2Y+gyas +0KFYHEQjTgz2AH1N9sUu8tzLAxn+wun28qyF3Paswmx27J/pAmbX8v6g+oGur4Jz +V6ZoM5PA5iD5UvWYcLBczB84GcqhQkLHh8n/sZXP9jXMnxjTPD4nuPuQ3wIDAQAB +AoGAd0s6/RdNEu6qlmifS7kS2V2ixmRCRu9NbsJYRiqxUfXyS94VKCzMthxTMbdn +hTXXVY44y/IlfvcUGWfWOABHU7JK5NfWbwJfH0dU2kNEf8LzPmf1DzGy6vj01i/u +6KrSJMqJOW62NxQ1GjkvWVGgoy8RrHKzrkM7bnQ+i6JDVLECQQDX24V58LQwMmo2 +JDZHjEZLZlx4xQz3lzhrLOfn7B3zgspRrgufOp2SaL+nFaphUl4w8P9mo/FcDmqc +R402Z4yTAkEAyDfCiZiGBgPm7mDOLiJ1Wpyc21fsF6zwoc56xSbeK+a3t9LxT00M +1W+qZv6e89erUmGNl85CwFmoyMEPdIgmBQJAOOUZp2x0cge3yxF8ZRtqI9GVKhf2 +NQRc0JMDhTPNKTQeE61mTs/qXH7TlTy2rfRB83ByQSGRKox6OTr6044zlQJAKuCe +Hb93PESLqRM8NG8WuL//a43ptqxHoC9K5XvMapRvVcOr//KdQ/w0/veabNgMDYls +vEzkyLKqzctilu8tTQJBAMXgHKX62GhH54pB4GrLLS25JpxqUNChDuPGaMPilfCW +WOsFVC93MPtLA/YaAJHKZNoaXulkb5q3jhlWxCpDAKM= +-----END RSA PRIVATE KEY----- +''' + +app.config['PUBLIC_KEY'] = '''-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo0oR5UXusQs7tvgcT5EOLB+2J +aKXmmip3ViGijLHku2Y+gyas0KFYHEQjTgz2AH1N9sUu8tzLAxn+wun28qyF3Pas +wmx27J/pAmbX8v6g+oGur4JzV6ZoM5PA5iD5UvWYcLBczB84GcqhQkLHh8n/sZXP +9jXMnxjTPD4nuPuQ3wIDAQAB +-----END PUBLIC KEY-----''' + +argon2 = Argon2(app) + +flag1 = 'HM{sql_inj3ctions_4re_still_r3l3vant}' +flag2 = 'HM{y0u_th0ught_p4ssword_ha5h1ng_is_enough?}' +flag3 = 'HM{s3ssion_manag3ment_is_super_h4rd}' + +database = 'evilcorp.sqlite3' + +def check_session(cookies): + + if 'session' not in cookies: + return False + + session = cookies['session'] + try: + session_decoded = jwt.decode(session, app.config['PUBLIC_KEY']) + except jwt.InvalidTokenError: + return False + return session_decoded + +def get_db(): + db = getattr(g, '_database', None) + if db is None: + db = g._database = sqlite3.connect(database) + return db + + +@app.teardown_appcontext +def close_connection(exception): + db = getattr(g, '_database', None) + if db is not None: + db.close() + +@app.route('/', methods=['GET']) +def show_index(): + session = check_session(request.cookies) + if session: + authenticated = True + username = session['user'] + else: + authenticated = False + username = None + return render_template('index.html', authenticated=authenticated, username=username) + +@app.route('/news', methods=['GET']) +def show_news(): + session = check_session(request.cookies) + if session: + authenticated = True + username = session['user'] + else: + authenticated = False + username = None + + try: + cur = get_db().execute('SELECT uid, title, body, images FROM news ORDER BY uid DESC') + news = cur.fetchall() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + return render_template('news.html', news=news, authenticated=authenticated, username=username) + +@app.route('/images/', methods=['GET']) +def show_image(uid): + try: + cur = get_db().execute('SELECT uid, name, body FROM images WHERE uid = ' + uid) + image = cur.fetchone() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + if not image: + return 'No image found' + return send_file(BytesIO(image[2]), attachment_filename=image[1]+'.jpg', mimetype='image/jpg') + +@app.route('/login', methods=['GET']) +def show_login(): + return render_template('login.html') + +@app.route('/login', methods=['POST']) +def login(): + + if 'username' not in request.form or 'password' not in request.form: + return 'All fields are required!' + username = request.form['username'] + password = request.form['password'] + + #if not username.isalnum(): + # return 'Login failed' + + if len(password) < 6: + return 'Login failed' + + try: + cur = get_db().execute('SELECT uid, username, password FROM users WHERE username = ? LIMIT 1', (username,)) + cur_user = cur.fetchone() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + if not cur_user: + return 'Login failed' + + if not argon2.check_password_hash(cur_user[2], password): + return 'Login failed' + + session = jwt.encode({'user': cur_user[1], 'admin': False, 'iat': int(time.time())}, key=app.config['PRIVATE_KEY'] , algorithm='RS256') + + response = current_app.make_response(redirect('/user', 302)) + response.set_cookie('session', value=session, path='/', httponly=True) + return response + + +@app.route('/reset', methods=['GET']) +def show_reset(): + return render_template('reset.html') + + +@app.route('/reset', methods=['POST']) +def reset(): + if 'email' not in request.form: + return 'Email is required required!' + email = request.form['email'] + + if len(email) < 6: + return 'Email too short' + + try: + cur = get_db().execute('SELECT uid, email, username, recovered, admin FROM users WHERE email = ? LIMIT 1', (email,)) + cur_user = cur.fetchone() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + if not cur_user: + return 'No user found' + + if cur_user[4]: + return 'Nope' + + if cur_user[3]: + return 'Password for this user has already been reset' + + uid = cur_user[0] + token = secrets.token_hex() + + try: + #cur = get_db().execute('UPDATE user SET recovery = ?, recovered = 1 WHERE email = ?', (token, email,)) + cur = get_db().execute('UPDATE users SET recovery = ? WHERE uid = ?', (token, uid,)) + get_db().commit() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + return 'Reset token sent to user email' + +@app.route('/reset2', methods=['POST']) +def reset2(): + if 'email' not in request.form or 'token' not in request.form: + return 'Both fields are required!' + + email = request.form['email'] + token = request.form['token'] + + try: + cur = get_db().execute('SELECT * FROM users WHERE recovery = ? AND email = ? AND recovered != 1', (token, email,)) + cur_user = cur.fetchone() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + if not cur_user: + return 'Wrong email or token or password already reset one time' + + else: + newpassword = secrets.token_urlsafe(16) + try: + cur = get_db().execute('UPDATE users SET password = ?, recovered = 1 WHERE recovery = ? AND email = ?', (argon2.generate_password_hash(newpassword), token, email,)) + get_db().commit() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + return 'Your new password is ' + newpassword + + +@app.route('/user', methods=['GET']) +def show_user(): + session = check_session(request.cookies) + if not session: + return redirect('/login', 302) + else: + if not session['admin']: + return render_template('user.html', authenticated=True, username=session['user'], flag2=flag2) + return 'Good job! Here\'s the last flag: ' + flag3 + +@app.route('/logout', methods=['GET']) +def logout(): + response = current_app.make_response(redirect('/', 302)) + for cookie in request.cookies: + response.set_cookie(cookie, value='', expires=0) + return response + +@app.route('/robots.txt', methods=['GET']) +def show_robots(): + return 'Disallow: /pub.asc' + +@app.route('/pub.asc', methods=['GET']) +def show_pubkey(): + return app.config['PUBLIC_KEY'] + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=7000) + diff --git a/evilcorp/web/evilcorp.sqlite3 b/evilcorp/web/evilcorp.sqlite3 new file mode 100755 index 0000000..11383b8 Binary files /dev/null and b/evilcorp/web/evilcorp.sqlite3 differ diff --git a/evilcorp/web/pub.asc b/evilcorp/web/pub.asc new file mode 100644 index 0000000..7f4b0cf --- /dev/null +++ b/evilcorp/web/pub.asc @@ -0,0 +1,7 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo0oR5UXusQs7tvgcT5EOLB+2J +aKXmmip3ViGijLHku2Y+gyas0KFYHEQjTgz2AH1N9sUu8tzLAxn+wun28qyF3Pas +wmx27J/pAmbX8v6g+oGur4JzV6ZoM5PA5iD5UvWYcLBczB84GcqhQkLHh8n/sZXP +9jXMnxjTPD4nuPuQ3wIDAQAB +-----END PUBLIC KEY----- + diff --git a/evilcorp/web/static/menu.css b/evilcorp/web/static/menu.css new file mode 100644 index 0000000..7abd61c --- /dev/null +++ b/evilcorp/web/static/menu.css @@ -0,0 +1,248 @@ +body { + color: #777; +} + +.pure-img-responsive { + max-width: 100%; + height: auto; +} + +/* +Add transition to containers so they can push in and out. +*/ +#layout, +#menu, +.menu-link { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + -ms-transition: all 0.2s ease-out; + -o-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} + +/* +This is the parent `
` that contains the menu and the content area. +*/ +#layout { + position: relative; + left: 0; + padding-left: 0; +} + #layout.active #menu { + left: 150px; + width: 150px; + } + + #layout.active .menu-link { + left: 150px; + } +/* +The content `
` is where all your content goes. +*/ +.content { + margin: 0 auto; + padding: 0 2em; + max-width: 800px; + margin-bottom: 50px; + line-height: 1.6em; +} + +.header { + margin: 0; + color: #333; + text-align: center; + padding: 2.5em 2em 0; + border-bottom: 1px solid #eee; + } + .header h1 { + margin: 0.2em 0; + font-size: 3em; + font-weight: 300; + } + .header h2 { + font-weight: 300; + color: #ccc; + padding: 0; + margin-top: 0; + } + +.content-subhead { + margin: 50px 0 20px 0; + font-weight: 300; + color: #888; +} + + + +/* +The `#menu` `
` is the parent `
` that contains the `.pure-menu` that +appears on the left side of the page. +*/ + +#menu { + margin-left: -150px; /* "#menu" width */ + width: 150px; + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: 1000; /* so the menu or its navicon stays above all content */ + background: #191818; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + /* + All anchors inside the menu should be styled like this. + */ + #menu a { + color: #999; + border: none; + padding: 0.6em 0 0.6em 0.6em; + } + + /* + Remove all background/borders, since we are applying them to #menu. + */ + #menu .pure-menu, + #menu .pure-menu ul { + border: none; + background: transparent; + } + + /* + Add that light border to separate items into groups. + */ + #menu .pure-menu ul, + #menu .pure-menu .menu-item-divided { + border-top: 1px solid #333; + } + /* + Change color of the anchor links on hover/focus. + */ + #menu .pure-menu li a:hover, + #menu .pure-menu li a:focus { + background: #333; + } + + /* + This styles the selected menu item `
  • `. + */ + #menu .pure-menu-selected, + #menu .pure-menu-heading { + background: #1f8dd6; + } + /* + This styles a link within a selected menu item `
  • `. + */ + #menu .pure-menu-selected a { + color: #fff; + } + + /* + This styles the menu heading. + */ + #menu .pure-menu-heading { + font-size: 110%; + color: #fff; + margin: 0; + } + +/* -- Dynamic Button For Responsive Menu -------------------------------------*/ + +/* +The button to open/close the Menu is custom-made and not part of Pure. Here's +how it works: +*/ + +/* +`.menu-link` represents the responsive menu toggle that shows/hides on +small screens. +*/ +.menu-link { + position: fixed; + display: block; /* show this only on small screens */ + top: 0; + left: 0; /* "#menu width" */ + background: #000; + background: rgba(0,0,0,0.7); + font-size: 10px; /* change this value to increase/decrease button size */ + z-index: 10; + width: 2em; + height: auto; + padding: 2.1em 1.6em; +} + + .menu-link:hover, + .menu-link:focus { + background: #000; + } + + .menu-link span { + position: relative; + display: block; + } + + .menu-link span, + .menu-link span:before, + .menu-link span:after { + background-color: #fff; + width: 100%; + height: 0.2em; + } + + .menu-link span:before, + .menu-link span:after { + position: absolute; + margin-top: -0.6em; + content: " "; + } + + .menu-link span:after { + margin-top: 0.6em; + } + + +/* -- Responsive Styles (Media Queries) ------------------------------------- */ + +/* +Hides the menu at `48em`, but modify this based on your app's needs. +*/ +@media (min-width: 48em) { + + .header, + .content { + padding-left: 2em; + padding-right: 2em; + } + + #layout { + padding-left: 150px; /* left col width "#menu" */ + left: 0; + } + #menu { + left: 150px; + } + + .menu-link { + position: fixed; + left: 150px; + display: none; + } + + #layout.active .menu-link { + left: 150px; + } +} + +@media (max-width: 48em) { + /* Only apply this when the window is small. Otherwise, the following + case results in extra padding on the left: + * Make the window small. + * Tap the menu to trigger the active state. + * Make the window large again. + */ + #layout.active { + position: relative; + left: 150px; + } +} diff --git a/evilcorp/web/static/pure.css b/evilcorp/web/static/pure.css new file mode 100644 index 0000000..2ce0e15 --- /dev/null +++ b/evilcorp/web/static/pure.css @@ -0,0 +1,11 @@ +/*! +Pure v0.6.2 +Copyright 2013 Yahoo! +Licensed under the BSD License. +https://github.com/yahoo/pure/blob/master/LICENSE.md +*/ +/*! +normalize.css v^3.0 | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */.pure-button:focus,a:active,a:hover{outline:0}.pure-table,table{border-collapse:collapse;border-spacing:0}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}abbr[title]{border-bottom:1px dotted}b,optgroup,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre,textarea{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}.pure-button,input{line-height:normal}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}.pure-button,.pure-form input:not([type]),.pure-menu{box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend,td,th{padding:0}legend{border:0}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-align-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}@media all and (-ms-high-contrast:none),(-ms-high-contrast:active){table .pure-g{display:block}}.opera-only :-o-prefocus,.pure-g{word-spacing:-.43em}.pure-u,.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto;display:inline-block;zoom:1}.pure-g [class*=pure-u]{font-family:sans-serif}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;zoom:1;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-.43em}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:#444;color:rgba(0,0,0,.8);border:1px solid #999;border:transparent;background-color:#E6E6E6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{filter:alpha(opacity=90);background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000\9}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto;margin:0;border-radius:0;border-right:1px solid #111;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=tel],.pure-form input[type=color],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=text],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px}.pure-form input[type=color]{padding:.2em .5em}.pure-form input:not([type]):focus,.pure-form input[type=password]:focus,.pure-form input[type=email]:focus,.pure-form input[type=url]:focus,.pure-form input[type=date]:focus,.pure-form input[type=month]:focus,.pure-form input[type=time]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=week]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=color]:focus,.pure-form input[type=number]:focus,.pure-form input[type=search]:focus,.pure-form input[type=text]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129FEA}.pure-form input[type=file]:focus,.pure-form input[type=checkbox]:focus,.pure-form input[type=radio]:focus{outline:#129FEA auto 1px}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input:not([type])[disabled],.pure-form input[type=password][disabled],.pure-form input[type=email][disabled],.pure-form input[type=url][disabled],.pure-form input[type=date][disabled],.pure-form input[type=month][disabled],.pure-form input[type=time][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=week][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=color][disabled],.pure-form input[type=number][disabled],.pure-form input[type=search][disabled],.pure-form input[type=text][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input:not([type]),.pure-form-stacked input[type=password],.pure-form-stacked input[type=email],.pure-form-stacked input[type=url],.pure-form-stacked input[type=date],.pure-form-stacked input[type=month],.pure-form-stacked input[type=time],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=week],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=color],.pure-form-stacked input[type=file],.pure-form-stacked input[type=number],.pure-form-stacked input[type=search],.pure-form-stacked input[type=text],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-aligned .pure-help-inline,.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form .pure-help-inline,.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=tel],.pure-form input[type=color],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=text],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=password],.pure-group input[type=email],.pure-group input[type=url],.pure-group input[type=date],.pure-group input[type=month],.pure-group input[type=time],.pure-group input[type=datetime],.pure-group input[type=datetime-local],.pure-group input[type=week],.pure-group input[type=tel],.pure-group input[type=color],.pure-group input[type=number],.pure-group input[type=search],.pure-group input[type=text]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0}.pure-form .pure-help-inline,.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;zoom:1;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;padding:.5em 0}.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar{display:none}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-disabled,.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected .pure-menu-link,.pure-menu-selected .pure-menu-link:visited{color:#000}.pure-table{empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table td:first-child,.pure-table th:first-child{border-left-width:0}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td,.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} \ No newline at end of file diff --git a/evilcorp/web/static/robots.txt b/evilcorp/web/static/robots.txt new file mode 100644 index 0000000..4ea1a6d --- /dev/null +++ b/evilcorp/web/static/robots.txt @@ -0,0 +1 @@ +Disallow: /pub.asc diff --git a/evilcorp/web/static/ui.js b/evilcorp/web/static/ui.js new file mode 100644 index 0000000..6d7c20b --- /dev/null +++ b/evilcorp/web/static/ui.js @@ -0,0 +1,46 @@ +(function (window, document) { + + var layout = document.getElementById('layout'), + menu = document.getElementById('menu'), + menuLink = document.getElementById('menuLink'), + content = document.getElementById('main'); + + function toggleClass(element, className) { + var classes = element.className.split(/\s+/), + length = classes.length, + i = 0; + + for(; i < length; i++) { + if (classes[i] === className) { + classes.splice(i, 1); + break; + } + } + // The className is not found + if (length === classes.length) { + classes.push(className); + } + + element.className = classes.join(' '); + } + + function toggleAll(e) { + var active = 'active'; + + e.preventDefault(); + toggleClass(layout, active); + toggleClass(menu, active); + toggleClass(menuLink, active); + } + + menuLink.onclick = function (e) { + toggleAll(e); + }; + + content.onclick = function(e) { + if (menu.className.indexOf('active') !== -1) { + toggleAll(e); + } + }; + +}(this, this.document)); diff --git a/evilcorp/web/templates/index.html b/evilcorp/web/templates/index.html new file mode 100644 index 0000000..d536e08 --- /dev/null +++ b/evilcorp/web/templates/index.html @@ -0,0 +1,41 @@ + + + + + + + Evil Corp | Home + + + + + + + +
    + + {% include 'nav.html' %} + +
    +
    +

    Welcome to Evil Corp

    +

    We are a totally-legit, zero-bullshit company

    +
    + +
    +

    What's this site for?

    +

    + This is out completely secure company website. Valid credentials are required to access most resources, but you can still visit our News area freely. + As in any other CMS, admins have superpowers: they can, for example, read super secret flags! +

    +
    +
    +
    + + + + + + + + diff --git a/evilcorp/web/templates/login.html b/evilcorp/web/templates/login.html new file mode 100644 index 0000000..8034e2e --- /dev/null +++ b/evilcorp/web/templates/login.html @@ -0,0 +1,54 @@ + + + + + + + Evil Corp | Login + + + + + + + + +
    + + {% include 'nav.html' %} + +
    +
    +

    Evil Corp

    +

    Login to access our corporate resources

    +
    + +
    +
    + + + + + + + + diff --git a/evilcorp/web/templates/nav.html b/evilcorp/web/templates/nav.html new file mode 100644 index 0000000..542f4c6 --- /dev/null +++ b/evilcorp/web/templates/nav.html @@ -0,0 +1,22 @@ + + + + + + + diff --git a/evilcorp/web/templates/news.html b/evilcorp/web/templates/news.html new file mode 100644 index 0000000..34fb9b8 --- /dev/null +++ b/evilcorp/web/templates/news.html @@ -0,0 +1,41 @@ + + + + + + + Evil Corp | News + + + + + + + +
    + + {% include 'nav.html' %} + +
    +
    +

    Evil Corp News

    +

    Here we will keep you updated on how we improve the world!

    +
    + + {% for post in news %} +
    +

    {{ post[1] }}

    + {{ post[2]|safe }} + +
    + {% endfor %} +
    +
    + + + + + + + + diff --git a/evilcorp/web/templates/reset.html b/evilcorp/web/templates/reset.html new file mode 100644 index 0000000..4024f00 --- /dev/null +++ b/evilcorp/web/templates/reset.html @@ -0,0 +1,64 @@ + + + + + + + Evil Corp | Password reset + + + + + + + + +
    + + {% include 'nav.html' %} + +
    +
    +

    Evil Corp

    +

    Can't remember your password?

    +
    + +
    +

    Already got a token?

    +
    + +
    +
    + + + + + + + + diff --git a/evilcorp/web/templates/user.html b/evilcorp/web/templates/user.html new file mode 100644 index 0000000..151ff42 --- /dev/null +++ b/evilcorp/web/templates/user.html @@ -0,0 +1,46 @@ + + + + + + + Evil Corp | Home + + + + + + + +
    + + {% include 'nav.html' %} + +
    +
    +

    User Profile

    +

    Welcome back {{ username }}

    +
    + +
    +

    Congratulations

    +

    + Here's your second flag: {{ flag2 }} +

    +
    +
    +

    User options

    +

    + Unfortunately we have yet to implement user functionalities. If you are an admin, please login as such to change site configuration. +

    +
    +
    +
    + + + + + + + + diff --git a/evilcorp/wsgi.py b/evilcorp/wsgi.py new file mode 100644 index 0000000..8ee6d20 --- /dev/null +++ b/evilcorp/wsgi.py @@ -0,0 +1,4 @@ +from web import app + +if __name__ == "__main__": + app.run(host='192.168.0.12', port=7000) diff --git a/rc4/crypto2/__init__.py b/rc4/crypto2/__init__.py new file mode 100644 index 0000000..0fc90da --- /dev/null +++ b/rc4/crypto2/__init__.py @@ -0,0 +1,242 @@ +from flask import Flask, g, request, render_template, redirect, current_app +from uuid import uuid4 +from flask_argon2 import Argon2 +from Crypto.Cipher import ARC4 +from binascii import hexlify, unhexlify +import sqlite3 +import json +import secrets +app = Flask(__name__) +argon2 = Argon2(app) + +flag = 'HM{h3r35_y0ur_2time_p4d}' + +database = 'wep.sqlite3' + +def get_db(): + db = getattr(g, '_database', None) + if db is None: + db = g._database = sqlite3.connect(database) + return db + +@app.teardown_appcontext +def close_connection(exception): + db = getattr(g, '_database', None) + if db is not None: + db.close() + +def check_session(cookies): + + if 'session' not in cookies and 'iv' not in cookies or 'uid' not in cookies: + return False + + try: + cur = get_db().execute('SELECT uid, username, iv, key FROM users WHERE uid = ? AND iv = ? LIMIT 1', (cookies['uid'], cookies['iv'])) + cur_user = cur.fetchone() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + if not cur_user: + return False + + uid = cur_user[0] + username = cur_user[1] + iv = cur_user[2] + key = cur_user[3] + cipher = ARC4.new(bytes(iv) + key) + + try: + plaintext = cipher.decrypt(unhexlify(cookies['session'])) + session = json.loads(plaintext) + except: + return False + + session['key'] = key + session['iv'] = iv + return session + + +@app.route('/', methods=['GET']) +def index(): + return render_template('index.html') + + +@app.route('/register', methods=['GET']) +def show_register(): + return render_template('register.html') + +@app.route('/register', methods=['POST']) +def register(): + + if 'username' not in request.form or 'password' not in request.form or 'password2' not in request.form: + return 'All fields are required!' + username = request.form['username'] + password = request.form['password'] + password2 = request.form['password2'] + + if not username.isalnum(): + return 'Username must be alphanumeric' + + if password != password2: + return 'Password confirmation does not match' + + if len(password) < 6: + return 'Password must be at least 6 chars!' + + try: + cur = get_db().execute('SELECT username FROM users WHERE username= ? LIMIT 1', (username,)) + cur_user = cur.fetchone() + except: + return 'Something went wrong, ping the admins' + + if cur_user: + return 'Username already exist' + + try: + cur.execute('INSERT INTO users (uid, username, password, iv, key) VALUES(?, ?, ?, 0, ?)', (str(uuid4()), username, argon2.generate_password_hash(password), secrets.token_bytes(16),)) + get_db().commit() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + return redirect('/login', 302) + + +@app.route('/login', methods=['GET']) +def show_login(): + return render_template('login.html') + + +@app.route('/login', methods=['POST']) +def login(): + + if 'username' not in request.form or 'password' not in request.form: + return 'All fields are required!' + username = request.form['username'] + password = request.form['password'] + + if not username.isalnum(): + return 'Login failed' + + if len(password) < 6: + return 'Login failed' + + try: + cur = get_db().execute('SELECT uid, username, password, iv, key FROM users WHERE username = ? LIMIT 1', (username,)) + cur_user = cur.fetchone() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + if not cur_user: + return 'Login failed' + + if not argon2.check_password_hash(cur_user[2], password): + return 'Login failed' + + uid = cur_user[0] + username = cur_user[1] + iv = cur_user[3]+1 + key = cur_user[4] + + if iv >= 255: + iv = 0 + + data = { + 'username': username, + 'description': 'No description yet', + 'show_flag': False + } + + cipher = ARC4.new(bytes(iv) + key) + + try: + ciphertext = cipher.encrypt(json.dumps(data).encode('ascii')).hex() + except: + return 'Something went wrong, ping the admins' + + try: + get_db().execute('UPDATE users SET iv = ? WHERE username = ?', (iv, username,)) + get_db().commit() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + response = current_app.make_response(redirect('/user', 302)) + response.set_cookie('session', value=str(ciphertext), path='/', httponly=True) + response.set_cookie('uid', value=uid, path='/', httponly=True) + response.set_cookie('iv', value=str(iv), path='/', httponly=True) + return response + +@app.route('/user', methods=['GET']) +def show_user(): + session = check_session(request.cookies) + if not session: + return redirect('/login', 302) + if session['show_flag']: + return flag + + return render_template('user.html', authenticated=True, username=session['username'], description=session['description']) + +@app.route('/user', methods=['POST']) +def user(): + session = check_session(request.cookies) + if not session: + return redirect('/login', 302) + if session['show_flag']: + return flag + + if 'description' not in request.form: + return 'Description field is mandatory!' + + description = request.form['description'] + + if len(description) < 10 or len(description) > 200: + return 'Description either too short (<10) or too long (>200)' + + username = session['username'] + key = session['key'] + iv = session['iv']+1 + + if iv >= 255: + iv = 0 + + data = { + 'username': username, + 'description': description, + 'show_flag': False + } + + cipher = ARC4.new(bytes(iv) + key) + + try: + ciphertext = cipher.encrypt(json.dumps(data).encode('ascii')).hex() + except: + return 'Something went wrong, ping the admins' + + try: + get_db().execute('UPDATE users SET iv = ? WHERE username = ?', (iv, username,)) + get_db().commit() + except sqlite3.Error as e: + print(e) + return 'Something went wrong, ping the admins' + + response = current_app.make_response(redirect('/user', 302)) + response.set_cookie('session', value=str(ciphertext), path='/', httponly=True) + response.set_cookie('iv', value=str(iv), path='/', httponly=True) + return response + + + return render_template('user.html', authenticated=True, username=session['username'], description=session['description']) + +@app.route('/logout', methods=['GET']) +def logout(): + response = current_app.make_response(redirect('/', 302)) + for cookie in request.cookies: + response.set_cookie(cookie, value='', expires=0) + return response + + +if __name__ == "__main__": + app.run(host='0.0.0.0') diff --git a/rc4/crypto2/solution.py b/rc4/crypto2/solution.py new file mode 100644 index 0000000..46dc247 --- /dev/null +++ b/rc4/crypto2/solution.py @@ -0,0 +1,61 @@ +import requests +from binascii import hexlify, unhexlify + +url = 'http://192.168.0.222:5000' + +# Any registered username and password +username = 'myBLfLEDraYh3Dq' +password = '9GQLqu39EviKw' + +# default comment and any much longer string +original = 'No description yet' +description = 'stringadiversamapiulungastringadiversamapiulungastringa'.encode('ascii') + +# do login +s = requests.session() +r = s.post(url + '/login', data={'username': username, 'password': password}) + +cur_iv = s.cookies['iv'] + +ciphertext1 = s.cookies['session'] + +# loop trough 256 requests to obtain a two time pad +for i in range(int(cur_iv), int(cur_iv)+256): + s.post(url + '/user', data={'description': description}) + + if s.cookies['iv'] == cur_iv: + ciphertext2 = s.cookies['session'] + +for i in range(0, len(ciphertext1), 2): + if ciphertext1[i:i+1] != ciphertext2[i:i+1]: + break + +# obtain new cookis with the original comment to flip the fals in true +s.post(url + '/user', data={'description': original}) + +cur_iv = s.cookies['iv'] +uid = s.cookies['uid'] + +description = description.hex() +ciphertext1 = ciphertext1[i:] +ciphertext2 = ciphertext2[i:i+len(description)] + +# xor our known plaintext with the given ciphertext to recover part of the rc4 stream +key = '{:x}'.format(int(ciphertext2, 16) ^ int(description, 16))[0:len(ciphertext1)] +# xor our known rc4 stream with the default comment on the same iteration to decrypt the final parte of the cookie +plaintext = unhexlify('{:x}'.format(int(key, 16) ^ int(ciphertext1, 16))) + +# print the plaintext and notice it has a show_flag parameter +print('[*] Decrypted first cookie: ' + plaintext.decode('ascii')) + +# xor 'true ' with 'false' to calculate the value to use to flip the ciphertext +flip = '{:x}'.format(int('true '.encode('ascii').hex(), 16) ^ int('false'.encode('ascii').hex(), 16)) + +cur_session = s.cookies['session'] +# xor to obtain 'true ' from false' +flip = '{:x}'.format(int(flip, 16) ^ int(cur_session[-12:-2], 16)) +session = cur_session[0:-12] + flip + cur_session[-2:] +# get the flag! +flag = requests.get(url + '/user', cookies={'iv': cur_iv, 'uid': uid, 'session': session}) + +print('[*] Got the flag: ' + flag.text) diff --git a/rc4/crypto2/static/min.css b/rc4/crypto2/static/min.css new file mode 100644 index 0000000..cea3c38 --- /dev/null +++ b/rc4/crypto2/static/min.css @@ -0,0 +1 @@ +/* Copyright 2014 Owen Versteeg; MIT licensed */body,textarea,input,select{background:0;border-radius:0;font:16px sans-serif;margin:0}.smooth{transition:all .2s}.btn,.nav a{text-decoration:none}.container{margin:0 20px;width:auto}label>*{display:inline}form>*{display:block;margin-bottom:10px}.btn{background:#999;border-radius:6px;border:0;color:#fff;cursor:pointer;display:inline-block;margin:2px 0;padding:12px 30px 14px}.btn:hover{background:#888}.btn:active,.btn:focus{background:#777}.btn-a{background:#0ae}.btn-a:hover{background:#09d}.btn-a:active,.btn-a:focus{background:#08b}.btn-b{background:#3c5}.btn-b:hover{background:#2b4}.btn-b:active,.btn-b:focus{background:#2a4}.btn-c{background:#d33}.btn-c:hover{background:#c22}.btn-c:active,.btn-c:focus{background:#b22}.btn-sm{border-radius:4px;padding:10px 14px 11px}.row{margin:1% 0;overflow:auto}.col{float:left}.table,.c12{width:100%}.c11{width:91.66%}.c10{width:83.33%}.c9{width:75%}.c8{width:66.66%}.c7{width:58.33%}.c6{width:50%}.c5{width:41.66%}.c4{width:33.33%}.c3{width:25%}.c2{width:16.66%}.c1{width:8.33%}h1{font-size:3em}.btn,h2{font-size:2em}.ico{font:33px Arial Unicode MS,Lucida Sans Unicode}.addon,.btn-sm,.nav,textarea,input,select{outline:0;font-size:14px}textarea,input,select{padding:8px;border:1px solid #ccc}textarea:focus,input:focus,select:focus{border-color:#5ab}textarea,input[type=text]{-webkit-appearance:none;}.addon{padding:8px 12px;box-shadow:0 0 0 1px #ccc}.nav,.nav .current,.nav a:hover{background:#000;color:#fff}.nav{height:24px;padding:11px 0 15px}.nav a{color:#aaa;padding-right:1em;position:relative;top:-1px}.nav .pagename{font-size:22px;top:1px}.btn.btn-close{background:#000;float:right;font-size:25px;margin:-54px 7px;display:none}@media(min-width:1310px){.container{margin:auto;width:1270px}}@media(max-width:870px){.row .col{width:100%}}@media(max-width:500px){.btn.btn-close{display:block}.nav{overflow:hidden}.pagename{margin-top:-11px}.nav:active,.nav:focus{height:auto}.nav div:before{background:#000;border-bottom:10px double;border-top:3px solid;content:'';float:right;height:4px;position:relative;right:3px;top:14px;width:20px}.nav a{padding:.5em 0;display:block;width:50%}}.table th,.table td{padding:.5em;text-align:left}.table tbody>:nth-child(2n-1){background:#ddd}.msg{padding:1.5em;background:#def;border-left:5px solid #59d} \ No newline at end of file diff --git a/rc4/crypto2/templates/index.html b/rc4/crypto2/templates/index.html new file mode 100644 index 0000000..b3533c1 --- /dev/null +++ b/rc4/crypto2/templates/index.html @@ -0,0 +1,35 @@ + + + + + + + HM0x14 - WE(P)B | Home + + + + + + + {% include 'navbar.html' %} +
    +
    +

    Welcome

    +

    I learnt WEP is so secure that i decided to use it for my session management, but why waste 24bit for the IV? 8 should be enough, no user is going to do that many requests anyway...

    + Register +
    +
    + + diff --git a/rc4/crypto2/templates/login.html b/rc4/crypto2/templates/login.html new file mode 100644 index 0000000..ee7677a --- /dev/null +++ b/rc4/crypto2/templates/login.html @@ -0,0 +1,43 @@ + + + + + + + HM0x14 - WE(P)B | Login + + + + + + + {% include 'navbar.html' %} +
    +
    +
    +

    Login

    +
    +
    + + + +
    +
    +
    +
    + + diff --git a/rc4/crypto2/templates/navbar.html b/rc4/crypto2/templates/navbar.html new file mode 100644 index 0000000..d767aca --- /dev/null +++ b/rc4/crypto2/templates/navbar.html @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/rc4/crypto2/templates/register.html b/rc4/crypto2/templates/register.html new file mode 100644 index 0000000..1a84ee2 --- /dev/null +++ b/rc4/crypto2/templates/register.html @@ -0,0 +1,44 @@ + + + + + + + HM0x14 - WE(P)B | Register + + + + + + + {% include 'navbar.html' %} +
    +
    +
    +

    Register

    +
    +
    + + + + +
    +
    +
    +
    + + diff --git a/rc4/crypto2/templates/user.html b/rc4/crypto2/templates/user.html new file mode 100644 index 0000000..8c6b256 --- /dev/null +++ b/rc4/crypto2/templates/user.html @@ -0,0 +1,56 @@ + + + + + + + HM0x14 - WE(P)B | User + + + + + + + {% include 'navbar.html' %} +
    +
    +
    +

    Update description

    +
    +
    + + +
    +
    +
    +

    Profile description

    +
    +
    +

    {{ description }}

    +
    +
    +
    +
    + + diff --git a/rc4/crypto2/wep.sqlite3 b/rc4/crypto2/wep.sqlite3 new file mode 100755 index 0000000..7de187e Binary files /dev/null and b/rc4/crypto2/wep.sqlite3 differ diff --git a/rc4/wep.sqlite3 b/rc4/wep.sqlite3 new file mode 100644 index 0000000..971e130 Binary files /dev/null and b/rc4/wep.sqlite3 differ diff --git a/rc4/wsgi.py b/rc4/wsgi.py new file mode 100644 index 0000000..ed8b5b1 --- /dev/null +++ b/rc4/wsgi.py @@ -0,0 +1,4 @@ +from crypto2 import app + +if __name__ == "__main__": + app.run(host='192.168.0.12')