Home > Writeups > DEADROP Rev 2 - Clearance Check

DEADROP Rev 2 - Clearance Check

A multi-layer obfuscated Python script hiding its payload behind base64, marshal bytecode, and a runtime exec chain. Peel back each layer to recover the deobfuscated comparison and the flag.

Clearance Check

Overview

An obfuscated Python clearance verification tool. The script has a comment // TODO: obfuscate better after audit - KOVACS. It uses layered base64 encoding and Python's marshal module to hide a bytecode payload. Peel back the layers to find a direct flag comparison.

Layer 1: The outer script

Open clearance_check.py. The structure is:

_payload = "aW1wb3J0IGJh..."   # long base64 string
def _init():
    import base64 as _b64
    exec(_b64.b64decode(_payload))
if __name__ == "__main__":
    _init()

Decode the outer layer:

import base64
print(base64.b64decode(_payload).decode())

This reveals:

import base64, marshal
exec(marshal.loads(base64.b64decode("YwAAAA...")))

Layer 2: The marshal bytecode

The inner string is base64-encoded Python bytecode in marshal format. Decode and disassemble:

import base64, marshal, dis

inner_b64 = "YwAAAA..."  # extracted from layer 1
code_obj = marshal.loads(base64.b64decode(inner_b64))
dis.dis(code_obj)

Key section of the disassembly:

LOAD_NAME    sys
LOAD_ATTR    argv
LOAD_CONST   1
BINARY_SUBSCR
LOAD_CONST   'DEADROP{python_deobfuscation_and_a_headache}'
COMPARE_OP   ==
POP_JUMP_IF_FALSE  ...

The flag string is a literal constant in the bytecode, LOAD_CONST loads it directly before the comparison.

Shortcut: scan constants

import base64, marshal

# Layer 1
outer = base64.b64decode(_payload)
# Extract the inner b64 (the string inside b64decode("..."))
import re
inner_b64 = re.search(rb'b64decode\("([^"]+)"\)', outer).group(1)
code_obj = marshal.loads(base64.b64decode(inner_b64))

# Dump all constants recursively
def dump_consts(code):
    for c in code.co_consts:
        if isinstance(c, str) and c.startswith('DEADROP'):
            print(c)
        if hasattr(c, 'co_consts'):
            dump_consts(c)

dump_consts(code_obj)

You could also put the first base64 layer into CyberChef, then grab the second layer and do the same, scanning the final output for the flag or saving it and greping for DEADROP{.

Verify

python3 clearance_check.py 'DEADROP{python_deobfuscation_and_a_headache}'
# CLEARANCE VERIFIED. Access level: TOP SECRET.

Flag: DEADROP{python_deobfuscation_and_a_headache}

Key Takeaway

Python exec + marshal + base64 is a common obfuscation pattern but provides no real security. The bytecode is just another representation of the source, dis.dis() and constant extraction defeat it completely. The // TODO: obfuscate better comment was not a joke.

< Back to All Writeups