solve (2765B)
1#!/usr/bin/env python3 2 3from Crypto.Cipher import AES 4from crcmod.predefined import mkPredefinedCrcFun 5 6import itertools 7import string 8import struct 9import requests 10import time 11import ast 12import sys 13 14crc16 = mkPredefinedCrcFun("x-25") 15 16def xor(al, bl): 17 return bytes([a^b for a,b in zip(al, bl)]) 18 19def upload(session, firmware): 20 baseurl = f"https://{session}-8000-bulb.challenge.cscg.live:1337" 21 open("exploit.bin", "wb+").write(firmware) 22 files = { "file": ("update.bin", firmware, "application/octet-stream") } 23 r = requests.post(f"{baseurl}/update", files=files) 24 assert(r.status_code == 200) 25 if "Update running" in r.text: 26 print("Updating..") 27 time.sleep(10) 28 logs = requests.get(f"{baseurl}/logs").content 29 open("logs.txt", "wb+").write(logs) 30 return logs 31 32def parse(logs): 33 part = logs.decode().split("Decrypted content: ", 1)[1] 34 return ast.literal_eval(part.split("\n")[0].strip()) 35 36def fixup(source, target_crc): 37 allowed = set(string.printable) - set(string.whitespace) - {'#', '"', "'"} 38 key = b"{PERMUTE}" 39 addr = bytes(source).index(key) 40 for test in itertools.permutations(allowed, len(key)): 41 source[addr:addr+len(key)] = "".join(test).encode() 42 source[-2:] = struct.pack("<H", crc16(source[:-2])) 43 if source[-2:] == target_crc: 44 break 45 return source 46 47def patch_next(firmware, expectation, reality): 48 for addr in range(0, len(expectation), 16)[::-1]: 49 if expectation[addr:addr+16] != reality[addr:addr+16]: 50 faddr = 64 + addr - 16 51 patch = xor(expectation[addr:addr+16], reality[addr:addr+16]) 52 firmware[faddr:faddr+16] = xor(patch, firmware[faddr:faddr+16]) 53 print("Mismatch", addr) 54 return firmware, addr == 0 55 return firmware, True 56 57def patch(session, firmware, version, new_source, old_source): 58 prev_source = old_source 59 while True: 60 firmware[8:12] = struct.pack("<I", version) 61 firmware, final = patch_next(firmware, new_source, prev_source) 62 logs = upload(session, firmware) 63 if final: break 64 prev_source = parse(logs) 65 66def main(): 67 session = sys.argv[1] 68 69 firmware = bytearray(open("update.bin", "rb").read()) 70 71 old_source = bytearray(open("source", "rb").read()) 72 #assert(old_source == parse(upload(session, firmware))) 73 74 new_source = bytearray(open("source.new", "rb").read()) 75 new_source = new_source[:len(old_source)] 76 assert(new_source[-3] == 0) 77 new_source = fixup(new_source, old_source[-2:]) 78 79 patch(session, firmware, 152, new_source, old_source) 80 81 baseurl = f"https://{session}-8000-bulb.challenge.cscg.live:1337" 82 print(requests.get(baseurl).text) 83 84 85 86if __name__ == "__main__": 87 main()