Import
This commit is contained in:
commit
61f5bc4d17
123
.gitignore
vendored
Normal file
123
.gitignore
vendored
Normal file
@ -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/
|
2
README.md
Normal file
2
README.md
Normal file
@ -0,0 +1,2 @@
|
||||
Basic challenges developed for Hackmeeting 0x14.
|
||||
https://hm.capturetheflag.it
|
9
circus.ini
Normal file
9
circus.ini
Normal file
@ -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
|
43
ecb/crypto1/app.py
Normal file
43
ecb/crypto1/app.py
Normal file
@ -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(<input>+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()
|
37
ecb/crypto1/solution.py
Normal file
37
ecb/crypto1/solution.py
Normal file
@ -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)
|
||||
|
||||
|
BIN
evilcorp/evilcorp.sqlite3
Executable file
BIN
evilcorp/evilcorp.sqlite3
Executable file
Binary file not shown.
248
evilcorp/web/__init__.py
Normal file
248
evilcorp/web/__init__.py
Normal file
@ -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/<uid>', 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)
|
||||
|
BIN
evilcorp/web/evilcorp.sqlite3
Executable file
BIN
evilcorp/web/evilcorp.sqlite3
Executable file
Binary file not shown.
7
evilcorp/web/pub.asc
Normal file
7
evilcorp/web/pub.asc
Normal file
@ -0,0 +1,7 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo0oR5UXusQs7tvgcT5EOLB+2J
|
||||
aKXmmip3ViGijLHku2Y+gyas0KFYHEQjTgz2AH1N9sUu8tzLAxn+wun28qyF3Pas
|
||||
wmx27J/pAmbX8v6g+oGur4JzV6ZoM5PA5iD5UvWYcLBczB84GcqhQkLHh8n/sZXP
|
||||
9jXMnxjTPD4nuPuQ3wIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
|
248
evilcorp/web/static/menu.css
Normal file
248
evilcorp/web/static/menu.css
Normal file
@ -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 `<div>` 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 `<div>` 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` `<div>` is the parent `<div>` 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 `<li>`.
|
||||
*/
|
||||
#menu .pure-menu-selected,
|
||||
#menu .pure-menu-heading {
|
||||
background: #1f8dd6;
|
||||
}
|
||||
/*
|
||||
This styles a link within a selected menu item `<li>`.
|
||||
*/
|
||||
#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;
|
||||
}
|
||||
}
|
11
evilcorp/web/static/pure.css
Normal file
11
evilcorp/web/static/pure.css
Normal file
File diff suppressed because one or more lines are too long
1
evilcorp/web/static/robots.txt
Normal file
1
evilcorp/web/static/robots.txt
Normal file
@ -0,0 +1 @@
|
||||
Disallow: /pub.asc
|
46
evilcorp/web/static/ui.js
Normal file
46
evilcorp/web/static/ui.js
Normal file
@ -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));
|
41
evilcorp/web/templates/index.html
Normal file
41
evilcorp/web/templates/index.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="A layout example with a side menu that hides on mobile, just like the Pure website.">
|
||||
<title>Evil Corp | Home</title>
|
||||
|
||||
<link rel="stylesheet" href="/static/pure.css">
|
||||
<link rel="stylesheet" href="/static/menu.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="layout">
|
||||
|
||||
{% include 'nav.html' %}
|
||||
|
||||
<div id="main">
|
||||
<div class="header">
|
||||
<h1>Welcome to Evil Corp</h1>
|
||||
<h2>We are a totally-legit, zero-bullshit company</h2>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h2 class="content-subhead">What's this site for?</h2>
|
||||
<p>
|
||||
This is out completely secure company website. Valid credentials are required to access most resources, but you can still visit our <a href="/news">News</a> area freely.
|
||||
As in any other CMS, admins have superpowers: they can, for example, read super secret flags!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/static/ui.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
54
evilcorp/web/templates/login.html
Normal file
54
evilcorp/web/templates/login.html
Normal file
@ -0,0 +1,54 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="A layout example with a side menu that hides on mobile, just like the Pure website.">
|
||||
<title>Evil Corp | Login</title>
|
||||
|
||||
<link rel="stylesheet" href="/static/pure.css">
|
||||
<link rel="stylesheet" href="/static/menu.css">
|
||||
|
||||
<style>
|
||||
.login {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.pure-input {
|
||||
width: 240px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="layout">
|
||||
|
||||
{% include 'nav.html' %}
|
||||
|
||||
<div id="main">
|
||||
<div class="header">
|
||||
<h1>Evil Corp</h1>
|
||||
<h2>Login to access our corporate resources</h2>
|
||||
</div>
|
||||
<div class="login">
|
||||
<form class="pure-form" method="post" action="">
|
||||
<fieldset class="pure-group">
|
||||
<input type="text" class="pure-input" placeholder="Username" name="username">
|
||||
<input type="password" class="pure-input" placeholder="Password" name="password">
|
||||
</fieldset>
|
||||
<button type="submit" class="pure-button pure-input pure-button-primary">Sign in</button>
|
||||
<p><a href="/reset">Forgot password?</a></p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/static/ui.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
22
evilcorp/web/templates/nav.html
Normal file
22
evilcorp/web/templates/nav.html
Normal file
@ -0,0 +1,22 @@
|
||||
<!-- Menu toggle -->
|
||||
<a href="#menu" id="menuLink" class="menu-link">
|
||||
<!-- Hamburger icon -->
|
||||
<span></span>
|
||||
</a>
|
||||
|
||||
<div id="menu">
|
||||
<div class="pure-menu">
|
||||
<a class="pure-menu-heading" href="#">Evil Corp</a>
|
||||
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-item"><a href="/" class="pure-menu-link">Home</a></li>
|
||||
<li class="pure-menu-item"><a href="/news" class="pure-menu-link">News</a></li>
|
||||
{% if authenticated %}
|
||||
<li class="pure-menu-item"><a href="/user" class="pure-menu-link">{{ username }}</a></li>
|
||||
<li class="pure-menu-item"><a href="/logout" class="pure-menu-link">Logout</a></li>
|
||||
{% else %}
|
||||
<li class="pure-menu-item"><a href="/login" class="pure-menu-link">Login</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
41
evilcorp/web/templates/news.html
Normal file
41
evilcorp/web/templates/news.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="A layout example with a side menu that hides on mobile, just like the Pure website.">
|
||||
<title>Evil Corp | News</title>
|
||||
|
||||
<link rel="stylesheet" href="/static/pure.css">
|
||||
<link rel="stylesheet" href="/static/menu.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="layout">
|
||||
|
||||
{% include 'nav.html' %}
|
||||
|
||||
<div id="main">
|
||||
<div class="header">
|
||||
<h1>Evil Corp News</h1>
|
||||
<h2>Here we will keep you updated on how we improve the world!</h2>
|
||||
</div>
|
||||
|
||||
{% for post in news %}
|
||||
<div class="content">
|
||||
<h2 class="content-subhead">{{ post[1] }}</h2>
|
||||
{{ post[2]|safe }}
|
||||
<img class="pure-img-responsive" src="/images/{{ post[3] }}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/static/ui.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
64
evilcorp/web/templates/reset.html
Normal file
64
evilcorp/web/templates/reset.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="A layout example with a side menu that hides on mobile, just like the Pure website.">
|
||||
<title>Evil Corp | Password reset</title>
|
||||
|
||||
<link rel="stylesheet" href="/static/pure.css">
|
||||
<link rel="stylesheet" href="/static/menu.css">
|
||||
|
||||
<style>
|
||||
.login {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.pure-input {
|
||||
width: 240px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="layout">
|
||||
|
||||
{% include 'nav.html' %}
|
||||
|
||||
<div id="main">
|
||||
<div class="header">
|
||||
<h1>Evil Corp</h1>
|
||||
<h2>Can't remember your password?</h2>
|
||||
</div>
|
||||
<div class="login">
|
||||
<form class="pure-form" method="post" action="">
|
||||
<fieldset class="pure-group">
|
||||
<input type="email" class="pure-input" placeholder="Email" name="email">
|
||||
</fieldset>
|
||||
<button type="submit" class="pure-button pure-input pure-button-primary">Send</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="header">
|
||||
<h2>Already got a token?</h2>
|
||||
</div>
|
||||
<div class="login">
|
||||
<form class="pure-form" method="post" action="/reset2">
|
||||
<fieldset class="pure-group">
|
||||
<input type="email" class="pure-input" placeholder="Email" name="email">
|
||||
<input type="text" class="pure-input" placeholder="Reset Token" name="token">
|
||||
</fieldset>
|
||||
<button type="submit" class="pure-button pure-input pure-button-primary">Reset</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/static/ui.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
46
evilcorp/web/templates/user.html
Normal file
46
evilcorp/web/templates/user.html
Normal file
@ -0,0 +1,46 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="A layout example with a side menu that hides on mobile, just like the Pure website.">
|
||||
<title>Evil Corp | Home</title>
|
||||
|
||||
<link rel="stylesheet" href="/static/pure.css">
|
||||
<link rel="stylesheet" href="/static/menu.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="layout">
|
||||
|
||||
{% include 'nav.html' %}
|
||||
|
||||
<div id="main">
|
||||
<div class="header">
|
||||
<h1>User Profile</h1>
|
||||
<h2>Welcome back <italic>{{ username }}</italic></h2>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h2 class="content-subhead">Congratulations</h2>
|
||||
<p>
|
||||
Here's your second flag: <strong>{{ flag2 }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2 class="content-subhead">User options</h2>
|
||||
<p>
|
||||
Unfortunately we have yet to implement user functionalities. If you are an admin, please login as such to change site configuration.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/static/ui.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
4
evilcorp/wsgi.py
Normal file
4
evilcorp/wsgi.py
Normal file
@ -0,0 +1,4 @@
|
||||
from web import app
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host='192.168.0.12', port=7000)
|
242
rc4/crypto2/__init__.py
Normal file
242
rc4/crypto2/__init__.py
Normal file
@ -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')
|
61
rc4/crypto2/solution.py
Normal file
61
rc4/crypto2/solution.py
Normal file
@ -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)
|
1
rc4/crypto2/static/min.css
Normal file
1
rc4/crypto2/static/min.css
Normal file
@ -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}
|
35
rc4/crypto2/templates/index.html
Normal file
35
rc4/crypto2/templates/index.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>HM0x14 - WE(P)B | Home</title>
|
||||
|
||||
<link href="static/min.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
background: #eee;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'navbar.html' %}
|
||||
<div class="container">
|
||||
<div class="hero">
|
||||
<h1>Welcome</h1>
|
||||
<p>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...</p>
|
||||
<a class="btn btn-b" href="/register">Register</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
43
rc4/crypto2/templates/login.html
Normal file
43
rc4/crypto2/templates/login.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>HM0x14 - WE(P)B | Login</title>
|
||||
|
||||
<link href="static/min.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<style>
|
||||
input {
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: inset 0 1px 3px #ddd;
|
||||
border-radius: 2px;
|
||||
vertical-align: middle;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
width: 240px;
|
||||
max-width: 100%;
|
||||
padding: 0.5em 0.6em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'navbar.html' %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col c6">
|
||||
<h1>Login</h1>
|
||||
<hr>
|
||||
<form method="post" action="">
|
||||
<input type="text" name="username" id="username" placeholder="Username">
|
||||
<input type="password" name="password" id="password" placeholder="Password">
|
||||
<input type="submit" class="btn btn-a btn-sm smooth" value="Login">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
13
rc4/crypto2/templates/navbar.html
Normal file
13
rc4/crypto2/templates/navbar.html
Normal file
@ -0,0 +1,13 @@
|
||||
<nav class="nav" tabindex="-1" onclick="this.focus()">
|
||||
<div class="container">
|
||||
<a class="pagename current" href="#">HM0x14 - WE(P)B</a>
|
||||
{% if authenticated %}
|
||||
<a href="/user">{{ username }}</a>
|
||||
<a href="/logout">Logout</a>
|
||||
{% else %}
|
||||
<a href="/">Home</a>
|
||||
<a href="/login">Login</a>
|
||||
<a href="/register">Register</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
44
rc4/crypto2/templates/register.html
Normal file
44
rc4/crypto2/templates/register.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>HM0x14 - WE(P)B | Register</title>
|
||||
|
||||
<link href="static/min.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<style>
|
||||
input {
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: inset 0 1px 3px #ddd;
|
||||
border-radius: 2px;
|
||||
vertical-align: middle;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
width: 240px;
|
||||
max-width: 100%;
|
||||
padding: 0.5em 0.6em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'navbar.html' %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col c6">
|
||||
<h1>Register</h1>
|
||||
<hr>
|
||||
<form method="post" action="">
|
||||
<input type="text" name="username" id="username" placeholder="Username">
|
||||
<input type="password" name="password" id="password" placeholder="Password">
|
||||
<input type="password" name="password2" id="password2" placeholder="repeat Password">
|
||||
<input type="submit" class="btn btn-a btn-sm smooth" value="Register">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
56
rc4/crypto2/templates/user.html
Normal file
56
rc4/crypto2/templates/user.html
Normal file
@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>HM0x14 - WE(P)B | User</title>
|
||||
|
||||
<link href="static/min.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<style>
|
||||
.hero {
|
||||
background: #eee;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
input,textarea {
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: inset 0 1px 3px #ddd;
|
||||
border-radius: 2px;
|
||||
vertical-align: middle;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
width: 320px;
|
||||
max-width: 100%;
|
||||
padding: 0.5em 0.6em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'navbar.html' %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col c6">
|
||||
<h1>Update description</h1>
|
||||
<hr>
|
||||
<form method="post" action="">
|
||||
<textarea name="description" id="description" rows="12">{{ description }}</textarea>
|
||||
<input type="submit" class="btn btn-a btn-sm smooth" value="Update">
|
||||
</form>
|
||||
</div>
|
||||
<div class="col c6">
|
||||
<h1>Profile description</h1>
|
||||
<hr>
|
||||
<div class="hero">
|
||||
<p>{{ description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
rc4/crypto2/wep.sqlite3
Executable file
BIN
rc4/crypto2/wep.sqlite3
Executable file
Binary file not shown.
BIN
rc4/wep.sqlite3
Normal file
BIN
rc4/wep.sqlite3
Normal file
Binary file not shown.
4
rc4/wsgi.py
Normal file
4
rc4/wsgi.py
Normal file
@ -0,0 +1,4 @@
|
||||
from crypto2 import app
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host='192.168.0.12')
|
Loading…
Reference in New Issue
Block a user