Skip to content

Instantly share code, notes, and snippets.

@pich4ya
Created April 15, 2018 18:26
Show Gist options
  • Save pich4ya/c559cb91235931d231251bae595817b5 to your computer and use it in GitHub Desktop.
Save pich4ya/c559cb91235931d231251bae595817b5 to your computer and use it in GitHub Desktop.
HITB XCTF 2018 - Python's revenge (Web)
# Python's revenge
# This is a easy python sandbox, can you bypass it and get the flag?
# https://hitbxctf2018.xctf.org.cn/contest_challenge/
from __future__ import unicode_literals
from flask import Flask, request, make_response, redirect, url_for, session
from flask import render_template, flash, redirect, url_for, request
from werkzeug.security import safe_str_cmp
from base64 import b64decode as b64d
from base64 import b64encode as b64e
from hashlib import sha256
from cStringIO import StringIO
import random
import string
import os
import sys
import subprocess
import commands
import pickle
import cPickle
import marshal
import os.path
import filecmp
import glob
import linecache
import shutil
import dircache
import io
import timeit
import popen2
import code
import codeop
import pty
import posixfile
SECRET_KEY = 'you will never guess'
if not os.path.exists('.secret'):
with open(".secret", "w") as f:
secret = ''.join(random.choice(string.ascii_letters + string.digits)
for x in range(4))
f.write(secret)
with open(".secret", "r") as f:
cookie_secret = f.read().strip()
app = Flask(__name__)
app.config.from_object(__name__)
black_type_list = [eval, execfile, compile, open, file, os.system, os.popen, os.popen2, os.popen3, os.popen4, os.fdopen, os.tmpfile, os.fchmod, os.fchown, os.open, os.openpty, os.read, os.pipe, os.chdir, os.fchdir, os.chroot, os.chmod, os.chown, os.link, os.lchown, os.listdir, os.lstat, os.mkfifo, os.mknod, os.access, os.mkdir, os.makedirs, os.readlink, os.remove, os.removedirs, os.rename, os.renames, os.rmdir, os.tempnam, os.tmpnam, os.unlink, os.walk, os.execl, os.execle, os.execlp, os.execv, os.execve, os.dup, os.dup2, os.execvp, os.execvpe, os.fork, os.forkpty, os.kill, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, os.spawnvp, os.spawnvpe, pickle.load, pickle.loads, cPickle.load, cPickle.loads, subprocess.call, subprocess.check_call, subprocess.check_output, subprocess.Popen, commands.getstatusoutput, commands.getoutput, commands.getstatus, glob.glob, linecache.getline, shutil.copyfileobj, shutil.copyfile, shutil.copy, shutil.copy2, shutil.move, shutil.make_archive, dircache.listdir, dircache.opendir, io.open, popen2.popen2, popen2.popen3, popen2.popen4, timeit.timeit, timeit.repeat, sys.call_tracing, code.interact, code.compile_command, codeop.compile_command, pty.spawn, posixfile.open, posixfile.fileopen]
@app.before_request
def count():
session['cnt'] = 0
@app.route('/')
def home():
remembered_str = 'Hello, here\'s what we remember for you. And you can change, delete or extend it.'
new_str = 'Hello fellow zombie, have you found a tasty brain and want to remember where? Go right here and enter it:'
location = getlocation()
if location == False:
return redirect(url_for("clear"))
return render_template('index.html', txt=remembered_str, location=location)
@app.route('/clear')
def clear():
flash("Reminder cleared!")
response = redirect(url_for('home'))
response.set_cookie('location', max_age=0)
return response
@app.route('/reminder', methods=['POST', 'GET'])
def reminder():
if request.method == 'POST':
location = request.form["reminder"]
if location == '':
flash("Message cleared, tell us when you have found more brains.")
else:
flash("We will remember where you find your brains.")
location = b64e(pickle.dumps(location))
cookie = make_cookie(location, cookie_secret)
response = redirect(url_for('home'))
response.set_cookie('location', cookie)
return response
location = getlocation()
if location == False:
return redirect(url_for("clear"))
return render_template('reminder.html')
class FilterException(Exception):
def __init__(self, value):
super(FilterException, self).__init__(
'The callable object {value} is not allowed'.format(value=str(value)))
class TimesException(Exception):
def __init__(self):
super(TimesException, self).__init__(
'Call func too many times!')
def _hook_call(func):
def wrapper(*args, **kwargs):
session['cnt'] += 1
print session['cnt']
print args[0].stack
for i in args[0].stack:
if i in black_type_list:
raise FilterException(args[0].stack[-2])
if session['cnt'] > 4:
raise TimesException()
return func(*args, **kwargs)
return wrapper
def loads(strs):
reload(pickle)
files = StringIO(strs)
unpkler = pickle.Unpickler(files)
unpkler.dispatch[pickle.REDUCE] = _hook_call(
unpkler.dispatch[pickle.REDUCE])
return unpkler.load()
def getlocation():
cookie = request.cookies.get('location')
if not cookie:
return ''
(digest, location) = cookie.split("!")
if not safe_str_cmp(calc_digest(location, cookie_secret), digest):
flash("Hey! This is not a valid cookie! Leave me alone.")
return False
location = loads(b64d(location))
return location
def make_cookie(location, secret):
return "%s!%s" % (calc_digest(location, secret), location)
def calc_digest(location, secret):
return sha256("%s%s" % (location, secret)).hexdigest()
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5051)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment