PICKLED
Challenge
A partial Python pickle file (out.pkl.part) is supposed to hold an object with a scrambled self.secret attribute — but the pickle bytecode itself just contains the two raw integers involved (secret and the flag) sitting in plaintext inside the opcode stream, XOR-ed together only at load time by whatever __reduce__/constructor logic the pickle would normally invoke.
Solution
Never actually unpickle the file (it's only a partial stream anyway, and running arbitrary embedded pickle bytecode is exactly what you don't want to do blindly). Instead, walk the opcode stream directly with pickletools.genops and pull out the two labeled integer constants:
gen = pickletools.genops(data)
vals = []
while True:
try: opcode, arg, pos = next(gen)
except (StopIteration, ValueError): break
if opcode.name in ("SHORT_BINUNICODE", "LONG1"):
vals.append((opcode.name, arg))
entries = {}
for i in range(len(vals) - 1):
if vals[i][0] == "SHORT_BINUNICODE" and vals[i + 1][0] == "LONG1":
entries[vals[i][1]] = vals[i + 1][1]
plain = entries["secret"] ^ entries["the flag"]
print(plain.to_bytes(-(plain.bit_length() // -8), "big").decode())
XOR-ing the two recovered integers and decoding the result as bytes gives the flag directly — the "secret" was only ever hidden behind the fact that nobody bothered to read the pickle stream as data instead of code.