skip to content
Site header image Minh Ton

Flare-On 12 Write-Up:#2 project_chimera

Reading some “secret” Python bytecode in the 2nd challenge from Flare-On 12!

Last Updated:

Initial Exploration

Challenge two gives us a Python file, with a gigantic hardcoded byte string. That string is then decompressed into a Python code object and the code object is executed.

# ================================================================= #
# ==           PROJECT CHIMERA - Dr. Alistair Khem's Journal     == #
# ==                  -- EYES ONLY --                            == #
# ================================================================= #
#
# Journal Entry 734:
#
# Success is within my grasp! After years of research, I have finally
# synthesized the two key components. The first, my 'Genetic Sequencer,'
# is stable and ready. It's designed to read and execute the final,
# most crucial part of my experiment: the 'Catalyst Serum.'
#
# The Catalyst is the key to creating a true digital lifeform.
# However, it is keyed to my specific biometric signature to prevent
# my research from falling into the wrong hands. Only I, the Lead
# Researcher, can successfully run this final protocol.
#
# If anyone else finds this, I urge you: DO NOT RUN THIS SCRIPT.
# The results could be... unpredictable.
#
# - Dr. A. Khem
#
import zlib
import marshal

# These are my encrypted instructions for the Sequencer.
encrypted_sequencer_data = b'x\x9cm\x96K\xcf\xe2\xe6\x15\xc7\xfd\xcedf\x92\xe6\xd2J\x93\xceTI\x9b\x8c\x05&\x18\xe4\t\x06\x03/\xc2\xdc1w\xcc\x1dl/\x00_\x01\xe3\x1b6\xc6\xe6\xfa\x15\x9a\xae\xd2\xae\xba\xae\xd2/Q\xf5\x0b\xbc\xd1\xa4JJVUV\xdd\xa5\xca\xae\xab\xf2\xceM\x89\x9ag\xe1\xf3\x9cs~\xe7\xfc\x8f\x1f\xc9\xd6\xf3\x1d\xf0\xa3u\xef\xa5\xfd\xe1\xce\x15\x00|\x0e\x08\x80p\xa5\x00\xcc\x0b{\xc5\\=\xb7w\x98;\xcf\xed]\xe6\xaep\x87y\xe3\x0e \xde\x13\xee~q\xf5\xa2\xf0\nx\xee\xbf\xf1\x13\x1f\x90\xdf\x01\xfeo\x89\xaf\x19\xe6\xc1\x85\xb9\x92\x7f\xf53\xcc\x83\xd7\xcc[\x17\xe6\x8e\xfc\xfe\xcf0o\xbdf\xde~\xae}\xef\'\xdaw\xe5\xdf\xfcL\xcd-\xf9\xee\x17/\xbd/\xee\xbc\xac\x7f\xef\x12}\xefU\xf4\n\xd8^\xc1\xf7\xff}\xbb%\xad\xbf\xbe\t\x00\xbc\xf7 \x06[\xe9\xb8\x0f\x89MU\xb0\xbbc\x97\'E!\x0ea<\t\xfa\xc7\x81aG\xf3\xac\x88\xca\xe1\xe0\x12a\xce\x1b\x18\xa5v\xce59:\x85\xd5Y\xb5)G\xac\x92\xbc\xdbB8Y\xeb\\cc\xeff%\xf6\xcb>\xb5\x10\xdc\xce\x15"\x16\x8f\xcb\xc85\\\xc2\xb4b\xfa\x94\xc1\xcb\xabF\x0c\xd3\x95M\xde\xf2r\x0c\xb6_\x11\xc9\xfd!ed\x9bX\x8e\x13\xb9q ]\xd8U\r\xb361\x0bT\x83B\xb3K8\x8ay+\x95AC\xab\x8a\xd16\xa2\xc0\xb9\xb9\x0c\x06b\xce\xbexR \xaa\xe9\x14\xdb\xb6G.\xd2sj\\$\xf7\xabh\xe7\x10EF+\x08\xcd*y\xf7x<lH\xd48\r\xaa\xd7s84\xf0i=4R\x9c\x1d\xdd\xeb\xfa\x98@\xfc+\xaf\x11:b\xa0\xb2E u\x1f\xaa\x08\xe9q0\x12\xc0[\xfb\x80\x15\xaa#\xca\xf2p\xcc7*\xa3z\xcd\x11;&\xb9\x8b\xee\xa1\x12\x92\xcc\x12\x93\xbd\x10\xac\xaa}%\x8e\xe8q\xdf\xb1\xb5\x87l\x8e\x85\x1d\xb4\xdb\x08\x0cr]*\x10O\xac\x83!|\x9c\xcf\xecT\xa5U\xa4\x12\x870\xb73&\xbb\xb5#o\'}\xa1\xce\xc1($\xb61\x01\xa1\xd6\x8b\x10=\x93\x97\x13\xc8\x01\xc7\x10\xea\xdaMr\x831\xd7>\x7f` \xc6\'\xe3\x12\xb7E\xb5H2X\xc6\x87\xc5\x9c\xb4Z\x8c\xe7h:\x94M\x11\xcbE\x14l\x9eL\xd5\x82X\xc9\x9d\x06m\x97\r\x05\x92\xa5\x9d-\x18+R\xd1\xa2M<\x0b\xb6V\x9a\xc0\xc0]|3\xc7l\xdf\xccPU\x8dm\x8a\x0e\xd7\x0fuk\xdc6\xe3\x97\xd885\xf2\x98i\xa6\x83\r\x08\x9f}8)\x8cE\xd0\'D1\xa4QS\nM\x82\xc6\x10\xa9L\xdbTU3\x1cu\xab\x9fTf\xba\x96\x06\xf5\x8c\xdf[\xaf\xb0\x90\xba!\x15}\xc3$i\xb8\x18\x14c\xb6\x13T\xe9X\x83\xcc\x87\xe9\x84\x8f]r#\x83\xc9*\xf3To\x81\x83\xb5\xec\xfaP(_\xc7\x88),\x1b\xa0\x82\xb9\x04\xed\x9f\xc7\xb3^E\xc9a\xc7|B0\x1a\x01\x19\x16\x1b\xfb\xcd\x90\xe7\xb6M7:\xd9sh\x04&\xb3\x0e{\x12\x8d\xde5#\xe9\xbe\xe1\x84\xf6H\xcd\xc0,\x91\xcc\xc6 9\x05-\xa0Q>\x94\xea\xf4"\xa2#gC\xa7<\xb8Xp6\xde\\\x99f\xadZ\xd9\xab\xbe\x92\x9e+\xe7#\x9e\x10)%]\xf0$l:\x87\x84\'\xc2\x1f\xe1j#\xb6$6\xf3\xfc\xb6\xb6\xc9\xed\xf3\th\xb0\xa2B\xfdY\x00\t\xe6\x96\'r\xe4\xbb\x1cK>\xc3\xc6\x1c\x91\xb88\xe6\xae\xbb\x083y0\x86\xc5+#%76\xcb\xd8l#G\xe8\xb5\xa8GB\xbe\xc01\x19M$\xe3Z\xad\x14\x17\xe7\xf1\x8dLP\x8e\xe3\xb6G\xa3]1\x10\xc1\xab\x1b\xa6\xe7Q\xaa\r\xbf\x12\xc8\xd8\xde$Q^Hu\xa9Q4\x86\\\xc0\xa4\x1a[\x07\xcc\xb5OL\x7f\x8c\xf4R\x18\xb5\x8f\xa0\xeb\x95\x88\xb7\xd0\xa5S\xf6\xce\xf2\x8cf_\x8b\x1b6r\x8a%\xb1\x82k\xf2\x15t\xdf\x99\xed\x9b\xc9r?\x9a\xcd\x0b\xab5d\xed\xdde?Y\xdc\xb2\xf9%\xbcI\xf3}\xd3\x93\xa2\x9aY\xbe\x83\x0c\x19\xa6\x86\xb2\xbb\xf9\x1e-J\'\xc9\x91\xfc\xaa@/\'<Q\x98N=;S\xdc\x0cl\tE\xaa\xf1b\xa5\xber\x13|\xbc)f\x02\x0b\xd26\x13\x17-\x1d\xce\xa19\xb5\xc2\xd5\xc1\x98g\x89\x0b\xc1\x8eJ\xc9\xfa@1s|\xaa\x8b\\\x13\x12\xb1\xd1\xbc\xfd6\x94a\xb804E\x92N)\xcc\xc4\xf9Sg\x0ev\x06\x06\x94-\xc5\x05\x7f\'Y]g5%\x82.\x1c~L\x16\xfa}S\x0e\xb4F0GT\xd2yZ\xe9xiu1\xef\r\xc3\x9d\xa2k\x16\xac:\xd9\xd7\t\xd5"\x17\xd2)\x89T\x1b\xe5\xa0\xe2\xcd\x9e\xacf\x91\xd7\x88\n]\xe5d.\xd3@,G\x87\xd2$I\xc7B\x9dZt\x1anP~\x9f\xb7P\x92\x02#?\xaf\xc4\xd7\xd7\xa1D$\x91\xedT\x82\xe9$\xb8\xaccr\xb3\xbfhur\xc7]3+\xf4\x82\x8e\xba\xc42\xdd\xb5\xb5\xaaZ~rm3\xa6\x9fpd|\xe7R\xecP_[`\x0c?\x0e\xda\xd1\xb4F\x1a\xe8LZ\x8a\x16\xd6\x0f\xec\x84=\x1c\x9b#\xe5\x12\x96&{\x9d\xd6\xb1\x1bH\xa0{~\xba\x04SE\xa4x\xe4X\xd2\x8bJ\xf6\x904\x07\xc5MyA\x0f\xa9\x11\x9d\xafb\xd1\xd8^-\x94\xa7\xf6\xd2f$\x83\x84s\xb8\xbb\xe5R\xd6\x91\xdb\x12\xfe\xe2\x86\x91T\xa3\xbb\xdc\xe8X\xa19\x0b\x96\x02\x91\x02$\xc5<\x19u?\xcb\xf61\x1b)\xe3\'5\x7fr\xca\xd4,I\x0e\x9b\xa5\xa2\xec\x93\xa28\xbc*\xa3\x9e\xb8\xab\xd0B\x89\xe8L\xe4J\xd7\x0e\x88\xbe\xd2@\xed\xa05\xbcl\x1c1\xaf\xbb\xcanY\xa5\xe0w\xe1\x1eR\xaa\x12\xb3\x8e\x18\xac\xba\xb9n\xa3\xd6\xee\xaa\xd9"\xe5\xfa\xd6A|\x1em\x84Z\xdd\x1aN\xe0\xbcs\x8c)Z,#\xba\x8d\xca\xf6\x98\x98\x08\x04f\xec\xd0\xb8\xde\xf0\x9f\x88\xe9\x9e\x9d\x12\x88\xa6\xc73\xd3(l\x14\t\x83\xa4\xfdHl\xc8\xd62\x851^K\xf8\xcb$\x98Kj\xd3v\xbf]d\xf2DrD\xa6\xa3\xcb\x14\xabZS{\xbb\xc5]\x95\xa1\x85lkv\x08a{t\xe0\x0f\xa0\xedr\xa3\x9b\x9eGFT\x86eF\x1d\xe9\x14Kdd\xa4d\xa9\x8dqyS\xd5\xcc\xd9B\xd0\x9b\xe1\xa3\x89\xda\xbe#\x95\x0f\xae\x8ezy\x86\x90]\x8f6\xa6\x02\x98\xbd\xcao3\xe8\x8a\xf6b\xb8\xbck\xe6\xe7T\x0eN\xee\xda\x92\x1b\t\xb8\x03p8\xf2z\xa4\x12\xebk\x16ZR\xb72\xd4BPly\xcd\xb2]\'!\xd0\x198\x0e\xdamP+W\x08\xce\xb3\x0c\xd6\\\xfa\x10\x9e\xa7\x97\xd4\x9e\xdcC\xe0\xb4*m\xda\xd4\xa1\x97\x15A-\x17\xa9nO\x1e\xbe>4a\x88/\xb9{\x95\xee\x95\xe5\xc4\x1c\xadL:1QX\xce\xed\xf2\x12\x8e0\x89\xd9\xc8\x98\x9e\xd4\xda\xae\x1c\xc7\xd4\xb8\x1f\xac\x8du?\x18\x16\xc4\xa9\xda\xcaD\xaa\xc5\x1d?Lz\xbb\x9diV\xd2\x17tE\x91\xa1\xfd\xe5\x87\x9c\xf6,\xfa\x87zz\x83L\xe9\n\xdc\xee\xbb\x1e\xa9k\xfb\x0f\xd9\x9cU\xef{\xdac\x98\xd7X\xf0\x90\xb0\x06\xdb\x01\xd2\\\xe7\xdc\xf6\xb1\x99v\x0e\x05\x1e\xb5\xb0I\xbd\x9a\x98+Fx{\x18\xe4\x88\x9a\xb7\x10\xf6b\xady\xec\x94\xb5e\x04\xa4\x91\xe8\x9a\xd8V\xbd4T\'\n$f\xc7\x14<\x90\x91x\xa7;\x91\x8a\xe3CP\x90\x8b\xd5Z\xd4\x06\xd39\x1fJ&\x16ku\x8fGt\xc4\xd6\x92\x08|\x9d\x18{\x8cj[\xd8\x0f\x9d\xed\xae2AG\xad\xed\x8a\xf1V\xe0\xa5\x97\xa2\x8a\x88\xcb\x0fXi&s)\xd2\xb3\x00\x83-MC\xfa2\xc2\x13:\x17\xf4\x83\xfe|k\xc4\xa6K\xebB2\x8c\x16+{h\\\xad\xe8)\x1eJ\x9aI\xd9Z\x93ht\xd5\x9b\x0c\xc6\xa5T\x8e\xf3\xf2\xd1\xd6<:\xcaH4\x08\x8d7\x02%\x11\xe9(-\x81f\xa54\xc6\xd9\xd24\x1f\xe0\xc4@#\xe5/\x94\xfc\x10B\xe0\x19\x18\xe2B\xde|\r>HaF.C\xd5\x9e\x13d\xae)\xbe0\x95\x830g,\xf1x\x82\xa6F\xc4R`\x87q\xd5)O\x96\x8b\xd6\xe5S\xa3\xb7\xaa\xaf\xe0[\xb8~\xc2\xc8\xc5IO\xe6x`\xbbn\xce\xea\xaaI0,B"\xccb\xb9\r\xa3U\x06\xed\x8dS`3\x9c\xaf\xb5\xa8\xe8\xfa\x0eB\x10\xe4I\x81U\x16\x9c\xc9\xae\x17\xda\xecIY\xd4\xc4\xf5\x82\x7f\xd2\x13W\xb6\xa8\xf1\xa2\xf9\xe4B\xec>.\x8a\xbc.\xdc\xe6yv\xcd*[k\xfd\xa4H\xe6\x9eXk\x93\xd5\x84\xa7O\x9f\xee>\xeam\xb5\xf5\\\xb4\x16\xbb[\xa8\xf0\n\xea\x89\xa6\xad^\xf2\xf0/\xcf\xf79\xd6\x12c\xd8\xf9\x8d\xddE\xec\xfc@eMk\xce*\xe7{\xeb\xad!Z\xe7\xc7\x17-]\x10\x85\xc9\xab\xfe\x93\x17\xbd\xcf\xf7\x0cs\xa1\xad\xcfoq\xd7Q\xe1v\x06\xf1\xfc\x90\xd7U\xc3\x14-\xebG\xf4\xf9\x17\xb7\xc9\x17\xe1\xf3\xe3\x97\xbd\x95\x0b0{\xf1:\x93\xe7\x95\xf7\x14\x9d\x15\xac\xf3\xfb\xaf5n\xa3\x13\x9d\x93E~}~\xa7dk\xfcz\xa1k\xfd\xcb@\xe7\x073E\xe7X\xc5:\x7f\xf8\x1a^h\xb7\xdc\x05\x98H/\xc9\xbf\x00?\xdc^\xfb\xfe\xfb\x10\x7f%c\xbd:\xb5\xf4\xf9M\\\xd5\x05[\x11\xd3\xe6\xaf\x9f\xdf\x12\x01\xc0\xfa\xfd\xe5\xf1\xfd\xdd\xab\xab\xab\xef\x80w\xbf\x05\xde\xfe\x16x\xef[\xe0\x9d\xef\xef\x03\x1f\xd6<7\xc0\xe3\x7f\x01\xf7n\xee#_\x01O\xffy\xbb\xf9\xe4+\xc0\xff\xcd#\xdfg\xd2\xd7\x8f|_>\xf2\xdd|\x92~\xf6(s\x03<\xfc\xe6\x03\xf8\x8f\xde?\x7f\xfa\xa7Oo\x02\xa9g\x1f\xa4/u\xdf<\xf6~\xe6|~\xfc\xc3\xf1\x06\xc2\x9f=N\xdd\x00\xef?\xef\xe4\xfb\n\xf8\xe4\xd2\xfbc\xf4\x8f\xe2\xd7\x1f\x85\xbe\xfc(t\x83\x12\x7fs\xfe\xbe}\xf6Q\xe7\x06\xf8\xf0?\xf7\x81\xab\xdf\xfe\x03\xf8\x9d\xf9\xf02\xd3\xff\x00hw\x9dH'

print(f"Booting up {f"Project Chimera"} from Dr. Khem's journal...")
# Activate the Genetic Sequencer. From here, the process is automated.
sequencer_code = zlib.decompress(encrypted_sequencer_data)
exec(marshal.loads(sequencer_code))
Click to expand

Unfortunately, this code doesn’t even run for me! At this point, I might just disassemble the code object to figure out how it works.

The Code Object

After a bit of research, I found that we can use Python’s dis module to disassemble the code object into a human-readable form.

import zlib
import marshal
import dis

encrypted_sequencer_data = b'x\x9cm\x96K\xcf\xe2\xe6\x15\xc7\xfd\xcedf\x92\xe6\xd2J\x93\xceTI\x9b\x8c\x05&\x18\xe4\t\x06\x03/\xc2\xdc1w\xcc\x1dl/\x00_\x01\xe3\x1b6\xc6\xe6\xfa\x15\x9a\xae\xd2\xae\xba\xae\xd2/Q\xf5\x0b\xbc\xd1\xa4JJVUV\xdd\xa5\xca\xae\xab\xf2\xceM\x89\x9ag\xe1\xf3\x9cs~\xe7\xfc\x8f\x1f\xc9\xd6\xf3\x1d\xf0\xa3u\xef\xa5\xfd\xe1\xce\x15\x00|\x0e\x08\x80p\xa5\x00\xcc\x0b{\xc5\\=\xb7w\x98;\xcf\xed]\xe6\xaep\x87y\xe3\x0e \xde\x13\xee~q\xf5\xa2\xf0\nx\xee\xbf\xf1\x13\x1f\x90\xdf\x01\xfeo\x89\xaf\x19\xe6\xc1\x85\xb9\x92\x7f\xf53\xcc\x83\xd7\xcc[\x17\xe6\x8e\xfc\xfe\xcf0o\xbdf\xde~\xae}\xef\'\xdaw\xe5\xdf\xfcL\xcd-\xf9\xee\x17/\xbd/\xee\xbc\xac\x7f\xef\x12}\xefU\xf4\n\xd8^\xc1\xf7\xff}\xbb%\xad\xbf\xbe\t\x00\xbc\xf7 \x06[\xe9\xb8\x0f\x89MU\xb0\xbbc\x97\'E!\x0ea<\t\xfa\xc7\x81aG\xf3\xac\x88\xca\xe1\xe0\x12a\xce\x1b\x18\xa5v\xce59:\x85\xd5Y\xb5)G\xac\x92\xbc\xdbB8Y\xeb\\cc\xeff%\xf6\xcb>\xb5\x10\xdc\xce\x15"\x16\x8f\xcb\xc85\\\xc2\xb4b\xfa\x94\xc1\xcb\xabF\x0c\xd3\x95M\xde\xf2r\x0c\xb6_\x11\xc9\xfd!ed\x9bX\x8e\x13\xb9q ]\xd8U\r\xb361\x0bT\x83B\xb3K8\x8ay+\x95AC\xab\x8a\xd16\xa2\xc0\xb9\xb9\x0c\x06b\xce\xbexR \xaa\xe9\x14\xdb\xb6G.\xd2sj\\$\xf7\xabh\xe7\x10EF+\x08\xcd*y\xf7x<lH\xd48\r\xaa\xd7s84\xf0i=4R\x9c\x1d\xdd\xeb\xfa\x98@\xfc+\xaf\x11:b\xa0\xb2E u\x1f\xaa\x08\xe9q0\x12\xc0[\xfb\x80\x15\xaa#\xca\xf2p\xcc7*\xa3z\xcd\x11;&\xb9\x8b\xee\xa1\x12\x92\xcc\x12\x93\xbd\x10\xac\xaa}%\x8e\xe8q\xdf\xb1\xb5\x87l\x8e\x85\x1d\xb4\xdb\x08\x0cr]*\x10O\xac\x83!|\x9c\xcf\xecT\xa5U\xa4\x12\x870\xb73&\xbb\xb5#o\'}\xa1\xce\xc1($\xb61\x01\xa1\xd6\x8b\x10=\x93\x97\x13\xc8\x01\xc7\x10\xea\xdaMr\x831\xd7>\x7f` \xc6\'\xe3\x12\xb7E\xb5H2X\xc6\x87\xc5\x9c\xb4Z\x8c\xe7h:\x94M\x11\xcbE\x14l\x9eL\xd5\x82X\xc9\x9d\x06m\x97\r\x05\x92\xa5\x9d-\x18+R\xd1\xa2M<\x0b\xb6V\x9a\xc0\xc0]|3\xc7l\xdf\xccPU\x8dm\x8a\x0e\xd7\x0fuk\xdc6\xe3\x97\xd885\xf2\x98i\xa6\x83\r\x08\x9f}8)\x8cE\xd0\'D1\xa4QS\nM\x82\xc6\x10\xa9L\xdbTU3\x1cu\xab\x9fTf\xba\x96\x06\xf5\x8c\xdf[\xaf\xb0\x90\xba!\x15}\xc3$i\xb8\x18\x14c\xb6\x13T\xe9X\x83\xcc\x87\xe9\x84\x8f]r#\x83\xc9*\xf3To\x81\x83\xb5\xec\xfaP(_\xc7\x88),\x1b\xa0\x82\xb9\x04\xed\x9f\xc7\xb3^E\xc9a\xc7|B0\x1a\x01\x19\x16\x1b\xfb\xcd\x90\xe7\xb6M7:\xd9sh\x04&\xb3\x0e{\x12\x8d\xde5#\xe9\xbe\xe1\x84\xf6H\xcd\xc0,\x91\xcc\xc6 9\x05-\xa0Q>\x94\xea\xf4"\xa2#gC\xa7<\xb8Xp6\xde\\\x99f\xadZ\xd9\xab\xbe\x92\x9e+\xe7#\x9e\x10)%]\xf0$l:\x87\x84\'\xc2\x1f\xe1j#\xb6$6\xf3\xfc\xb6\xb6\xc9\xed\xf3\th\xb0\xa2B\xfdY\x00\t\xe6\x96\'r\xe4\xbb\x1cK>\xc3\xc6\x1c\x91\xb88\xe6\xae\xbb\x083y0\x86\xc5+#%76\xcb\xd8l#G\xe8\xb5\xa8GB\xbe\xc01\x19M$\xe3Z\xad\x14\x17\xe7\xf1\x8dLP\x8e\xe3\xb6G\xa3]1\x10\xc1\xab\x1b\xa6\xe7Q\xaa\r\xbf\x12\xc8\xd8\xde$Q^Hu\xa9Q4\x86\\\xc0\xa4\x1a[\x07\xcc\xb5OL\x7f\x8c\xf4R\x18\xb5\x8f\xa0\xeb\x95\x88\xb7\xd0\xa5S\xf6\xce\xf2\x8cf_\x8b\x1b6r\x8a%\xb1\x82k\xf2\x15t\xdf\x99\xed\x9b\xc9r?\x9a\xcd\x0b\xab5d\xed\xdde?Y\xdc\xb2\xf9%\xbcI\xf3}\xd3\x93\xa2\x9aY\xbe\x83\x0c\x19\xa6\x86\xb2\xbb\xf9\x1e-J\'\xc9\x91\xfc\xaa@/\'<Q\x98N=;S\xdc\x0cl\tE\xaa\xf1b\xa5\xber\x13|\xbc)f\x02\x0b\xd26\x13\x17-\x1d\xce\xa19\xb5\xc2\xd5\xc1\x98g\x89\x0b\xc1\x8eJ\xc9\xfa@1s|\xaa\x8b\\\x13\x12\xb1\xd1\xbc\xfd6\x94a\xb804E\x92N)\xcc\xc4\xf9Sg\x0ev\x06\x06\x94-\xc5\x05\x7f\'Y]g5%\x82.\x1c~L\x16\xfa}S\x0e\xb4F0GT\xd2yZ\xe9xiu1\xef\r\xc3\x9d\xa2k\x16\xac:\xd9\xd7\t\xd5"\x17\xd2)\x89T\x1b\xe5\xa0\xe2\xcd\x9e\xacf\x91\xd7\x88\n]\xe5d.\xd3@,G\x87\xd2$I\xc7B\x9dZt\x1anP~\x9f\xb7P\x92\x02#?\xaf\xc4\xd7\xd7\xa1D$\x91\xedT\x82\xe9$\xb8\xaccr\xb3\xbfhur\xc7]3+\xf4\x82\x8e\xba\xc42\xdd\xb5\xb5\xaaZ~rm3\xa6\x9fpd|\xe7R\xecP_[`\x0c?\x0e\xda\xd1\xb4F\x1a\xe8LZ\x8a\x16\xd6\x0f\xec\x84=\x1c\x9b#\xe5\x12\x96&{\x9d\xd6\xb1\x1bH\xa0{~\xba\x04SE\xa4x\xe4X\xd2\x8bJ\xf6\x904\x07\xc5MyA\x0f\xa9\x11\x9d\xafb\xd1\xd8^-\x94\xa7\xf6\xd2f$\x83\x84s\xb8\xbb\xe5R\xd6\x91\xdb\x12\xfe\xe2\x86\x91T\xa3\xbb\xdc\xe8X\xa19\x0b\x96\x02\x91\x02$\xc5<\x19u?\xcb\xf61\x1b)\xe3\'5\x7fr\xca\xd4,I\x0e\x9b\xa5\xa2\xec\x93\xa28\xbc*\xa3\x9e\xb8\xab\xd0B\x89\xe8L\xe4J\xd7\x0e\x88\xbe\xd2@\xed\xa05\xbcl\x1c1\xaf\xbb\xcanY\xa5\xe0w\xe1\x1eR\xaa\x12\xb3\x8e\x18\xac\xba\xb9n\xa3\xd6\xee\xaa\xd9"\xe5\xfa\xd6A|\x1em\x84Z\xdd\x1aN\xe0\xbcs\x8c)Z,#\xba\x8d\xca\xf6\x98\x98\x08\x04f\xec\xd0\xb8\xde\xf0\x9f\x88\xe9\x9e\x9d\x12\x88\xa6\xc73\xd3(l\x14\t\x83\xa4\xfdHl\xc8\xd62\x851^K\xf8\xcb$\x98Kj\xd3v\xbf]d\xf2DrD\xa6\xa3\xcb\x14\xabZS{\xbb\xc5]\x95\xa1\x85lkv\x08a{t\xe0\x0f\xa0\xedr\xa3\x9b\x9eGFT\x86eF\x1d\xe9\x14Kdd\xa4d\xa9\x8dqyS\xd5\xcc\xd9B\xd0\x9b\xe1\xa3\x89\xda\xbe#\x95\x0f\xae\x8ezy\x86\x90]\x8f6\xa6\x02\x98\xbd\xcao3\xe8\x8a\xf6b\xb8\xbck\xe6\xe7T\x0eN\xee\xda\x92\x1b\t\xb8\x03p8\xf2z\xa4\x12\xebk\x16ZR\xb72\xd4BPly\xcd\xb2]\'!\xd0\x198\x0e\xdamP+W\x08\xce\xb3\x0c\xd6\\\xfa\x10\x9e\xa7\x97\xd4\x9e\xdcC\xe0\xb4*m\xda\xd4\xa1\x97\x15A-\x17\xa9nO\x1e\xbe>4a\x88/\xb9{\x95\xee\x95\xe5\xc4\x1c\xadL:1QX\xce\xed\xf2\x12\x8e0\x89\xd9\xc8\x98\x9e\xd4\xda\xae\x1c\xc7\xd4\xb8\x1f\xac\x8du?\x18\x16\xc4\xa9\xda\xcaD\xaa\xc5\x1d?Lz\xbb\x9diV\xd2\x17tE\x91\xa1\xfd\xe5\x87\x9c\xf6,\xfa\x87zz\x83L\xe9\n\xdc\xee\xbb\x1e\xa9k\xfb\x0f\xd9\x9cU\xef{\xdac\x98\xd7X\xf0\x90\xb0\x06\xdb\x01\xd2\\\xe7\xdc\xf6\xb1\x99v\x0e\x05\x1e\xb5\xb0I\xbd\x9a\x98+Fx{\x18\xe4\x88\x9a\xb7\x10\xf6b\xady\xec\x94\xb5e\x04\xa4\x91\xe8\x9a\xd8V\xbd4T\'\n$f\xc7\x14<\x90\x91x\xa7;\x91\x8a\xe3CP\x90\x8b\xd5Z\xd4\x06\xd39\x1fJ&\x16ku\x8fGt\xc4\xd6\x92\x08|\x9d\x18{\x8cj[\xd8\x0f\x9d\xed\xae2AG\xad\xed\x8a\xf1V\xe0\xa5\x97\xa2\x8a\x88\xcb\x0fXi&s)\xd2\xb3\x00\x83-MC\xfa2\xc2\x13:\x17\xf4\x83\xfe|k\xc4\xa6K\xebB2\x8c\x16+{h\\\xad\xe8)\x1eJ\x9aI\xd9Z\x93ht\xd5\x9b\x0c\xc6\xa5T\x8e\xf3\xf2\xd1\xd6<:\xcaH4\x08\x8d7\x02%\x11\xe9(-\x81f\xa54\xc6\xd9\xd24\x1f\xe0\xc4@#\xe5/\x94\xfc\x10B\xe0\x19\x18\xe2B\xde|\r>HaF.C\xd5\x9e\x13d\xae)\xbe0\x95\x830g,\xf1x\x82\xa6F\xc4R`\x87q\xd5)O\x96\x8b\xd6\xe5S\xa3\xb7\xaa\xaf\xe0[\xb8~\xc2\xc8\xc5IO\xe6x`\xbbn\xce\xea\xaaI0,B"\xccb\xb9\r\xa3U\x06\xed\x8dS`3\x9c\xaf\xb5\xa8\xe8\xfa\x0eB\x10\xe4I\x81U\x16\x9c\xc9\xae\x17\xda\xecIY\xd4\xc4\xf5\x82\x7f\xd2\x13W\xb6\xa8\xf1\xa2\xf9\xe4B\xec>.\x8a\xbc.\xdc\xe6yv\xcd*[k\xfd\xa4H\xe6\x9eXk\x93\xd5\x84\xa7O\x9f\xee>\xeam\xb5\xf5\\\xb4\x16\xbb[\xa8\xf0\n\xea\x89\xa6\xad^\xf2\xf0/\xcf\xf79\xd6\x12c\xd8\xf9\x8d\xddE\xec\xfc@eMk\xce*\xe7{\xeb\xad!Z\xe7\xc7\x17-]\x10\x85\xc9\xab\xfe\x93\x17\xbd\xcf\xf7\x0cs\xa1\xad\xcfoq\xd7Q\xe1v\x06\xf1\xfc\x90\xd7U\xc3\x14-\xebG\xf4\xf9\x17\xb7\xc9\x17\xe1\xf3\xe3\x97\xbd\x95\x0b0{\xf1:\x93\xe7\x95\xf7\x14\x9d\x15\xac\xf3\xfb\xaf5n\xa3\x13\x9d\x93E~}~\xa7dk\xfcz\xa1k\xfd\xcb@\xe7\x073E\xe7X\xc5:\x7f\xf8\x1a^h\xb7\xdc\x05\x98H/\xc9\xbf\x00?\xdc^\xfb\xfe\xfb\x10\x7f%c\xbd:\xb5\xf4\xf9M\\\xd5\x05[\x11\xd3\xe6\xaf\x9f\xdf\x12\x01\xc0\xfa\xfd\xe5\xf1\xfd\xdd\xab\xab\xab\xef\x80w\xbf\x05\xde\xfe\x16x\xef[\xe0\x9d\xef\xef\x03\x1f\xd6<7\xc0\xe3\x7f\x01\xf7n\xee#_\x01O\xffy\xbb\xf9\xe4+\xc0\xff\xcd#\xdfg\xd2\xd7\x8f|_>\xf2\xdd|\x92~\xf6(s\x03<\xfc\xe6\x03\xf8\x8f\xde?\x7f\xfa\xa7Oo\x02\xa9g\x1f\xa4/u\xdf<\xf6~\xe6|~\xfc\xc3\xf1\x06\xc2\x9f=N\xdd\x00\xef?\xef\xe4\xfb\n\xf8\xe4\xd2\xfbc\xf4\x8f\xe2\xd7\x1f\x85\xbe\xfc(t\x83\x12\x7fs\xfe\xbe}\xf6Q\xe7\x06\xf8\xf0?\xf7\x81\xab\xdf\xfe\x03\xf8\x9d\xf9\xf02\xd3\xff\x00hw\x9dH'

sequencer_code = zlib.decompress(encrypted_sequencer_data)
code_obj = marshal.loads(sequencer_code)
print(dis.dis(code_obj))
Click to expand

And here’s what we get:

  0           0 RESUME                   0

  2           2 LOAD_CONST               0 (0)
              4 LOAD_CONST               1 (None)
              6 IMPORT_NAME              0 (base64)
              8 STORE_NAME               0 (base64)

  3          10 LOAD_CONST               0 (0)
             12 LOAD_CONST               1 (None)
             14 IMPORT_NAME              1 (zlib)
             16 STORE_NAME               1 (zlib)

  4          18 LOAD_CONST               0 (0)
             20 LOAD_CONST               1 (None)
             22 IMPORT_NAME              2 (marshal)
             24 STORE_NAME               2 (marshal)

  5          26 LOAD_CONST               0 (0)
             28 LOAD_CONST               1 (None)
             30 IMPORT_NAME              3 (types)
             32 STORE_NAME               3 (types)

  8          34 LOAD_CONST               2 (b'c$|e+O>7&-6`m!Rzak~llE|2<;!(^*VQn#qEH||xE2b$*W=zw8NW~2mgIMj3sFjzy%<NJQ84^$vqeTG&mC+yhlE677j-8)F4nD>~?<GqL64olvBs$bZ4{qE;{|=p@M4Abeb^*>CzIprJ_rCXLX1@k)54$HHULnIe5P-l)Ahj!*6w{D~l%XMwDPu#jDYhX^DN{q5Q|5-Wq%1@lBx}}|vN1p~UI8h)0U&nS13Dg}x8K^E-(q$p0}4!ly-%m{0Hd>^+3*<O{*s0K-lk|}BLHWKJweQrNz5{%F-;@E_{d+ImTl7-o7&}O{%uba)w1RL*UARX*79t+0<^B?zmlODX9|2bzp_ztwjy_TdKb)1%eP4d-Xti0Ygjk_%w!^%1xuMNv4Z8&(*Ue7_^Fby1n3;+G<VDAfqi^h1>0@=Eki5!M~rms%afx`+uxa0*;FzudpqNln5M<@!OqndZ)R<vh4u&gpmmnaMewbT0RJby?(fa7XW#r>ZQ4UE&u|~lZsEY~-lpfWMf0_+pV-H`PXInpwmyo~mZ`tfUK?($KHa%mvNlovZ;Y)D+e6uw+mY6LNB2Y9&akbWpZ@lh=Si<!J@t|CG86E`)jp!l4xEY(h7@$llA4}B9dpL*j)eL{vVcbyMx5_{b13)N@wa~epS8Zfo&V_Y#fM*g9;@6%j=%i%WB0=QS3ewj@0~B!iibu<MqrrJIH{m&FoAGB3#0Nf;x!~dvQ|9#3c})IL6kEvhByJvA{B9%UqX0Tg*-+Ak~NW&RJbB?a6weENW&rzRi2ZB!647HWlA^rG4gvj3Yteo30&*};59;7nJF7eh7vjEXwwxPWWzD*3<IvZS#lIL(l*?u$;EGifKfLDpVb*rXLyw!AP~ZT^-S=4X{31tqe<O1kwG$gBZnu8eva3~6;4CxrcH1{Qg{M;GT5@Bdqt%s{xkT;DyaBk)v>cTr#=XM@cQ-VZZJ1azh{1Df~fwf(mdYk_cEC``#zrevUuf1-I7DHKqx9c7Me?*iNur9a3~o)A1AmHbK!6#k<d+QmXjoUlrAc=R-8EfEvn$TP%?Zb2%`-;wF2Z7c~Qh!QUp%@F7d(Q;It@nl31iwc^NCTTrj*OW)bEH>BYlQ$YmihSV2QDxrCsKNToEmsNif~;-ILG+l$@~sMDcnEHYIbjb?L-swo%>NNY60QJ5`2LX(&$CFf*W(cl7t80939@QH+>;!kK4jMTiOQA}zM@dS+wmk4?RtsqIs(NtuZr(Ewj<zxXaVots!6<}UP5>nNp1gfkes4T*zd{)6h-GF4>NSQO}R*91{c`k!=D-D}baN$1fuVNrUDvGiYVXWYBI456{mCG`ukuZfpN)A<xyb=s}byE(DvZfmpRkvo4CMg+F*3C%f6#?m{g@T4u-G<~mB~wGXg;NVMFDj&f5<)qG1#7xlYdFEQ_jHRu*e&FUmQ1J<Gp}4$xq@yalC(x)S-FIEgQe+IxARLJPRm@DXx&t+<h5L0ORJ<E<cw}6ln6?exLHy}9_dE4pz17oL(~E`{a`E-no7?`5)pDEpNY(-6VaJ?C^<J9(GN!A;n`PTPDZBE;WN>5k=ams`uyy<xmZYd@Og|04{1U(*1PGLR>h3WX?aZWQf~69?j-FsmL^GvInrgidoM2}r1u&}XB+q}oGg-NR#n^X*4uqBy?1qY$4<jzMBhXA);zPfx3*xU!VW$#fFa&MCOfRHVn0%6k8aaRw9dY?)7!uP!nGHEb#k+JxY|2h>kX{N{%!`IfvPX|S@e!nA3Iy~#cKVr)%cFx{mYSGj9h1H_Q6edkhuGk)3Z9gWp`~mJzG74m7(!J^o(!2de`mO?3IDzcV;$RQ`@foiYHlj%{3;+>#iT|K>v-`YH)PTx#fRu(|@AsKT#P^)cna!|9sUyU-MtAxP}M>w|Cc1s4_KI9hlp2y|UAEJ$C2$4Oh6~@uj-!Y-5tEyI$Y%KECN4u6l<*?fcwR_fD^|+djDIJ5u!>A&1N9itm{<3o-un;-)89^#pIPd{VwyzH_1WOyqZ$H)k$XXD-xcUafgjb=N#i!+Onn-Tj-cEob+(!(BOWa>FtC;21DH{%^IHo=c%;r;jstN15qS_U^F=Ab$c5Oh5W?fY!%^vdXfE>5Yf!rHF^<aF`B*be*L=(CF(%-E<?)%b0$BJ)|f2ZjG%ISw+Z8XcC`j+)bpk<79YXWEkdaV7mwG_kiObaNYym&C&ix(EpA7N#?}|aRxAsRm;!2e%e)a4AvZnHUPvwCa?b&OiHoo')
             36 STORE_NAME               4 (encoded_catalyst_strand)

 10          38 PUSH_NULL
             40 LOAD_NAME                5 (print)
             42 LOAD_CONST               3 ('--- Calibrating Genetic Sequencer ---')
             44 CALL                     1
             52 POP_TOP

 11          54 PUSH_NULL
             56 LOAD_NAME                5 (print)
             58 LOAD_CONST               4 ('Decoding catalyst DNA strand...')
             60 CALL                     1
             68 POP_TOP

 12          70 PUSH_NULL
             72 LOAD_NAME                0 (base64)
             74 LOAD_ATTR               12 (b85decode)
             94 LOAD_NAME                4 (encoded_catalyst_strand)
             96 CALL                     1
            104 STORE_NAME               7 (compressed_catalyst)

 13         106 PUSH_NULL
            108 LOAD_NAME                1 (zlib)
            110 LOAD_ATTR               16 (decompress)
            130 LOAD_NAME                7 (compressed_catalyst)
            132 CALL                     1
            140 STORE_NAME               9 (marshalled_genetic_code)

 14         142 PUSH_NULL
            144 LOAD_NAME                2 (marshal)
            146 LOAD_ATTR               20 (loads)
            166 LOAD_NAME                9 (marshalled_genetic_code)
            168 CALL                     1
            176 STORE_NAME              11 (catalyst_code_object)

 16         178 PUSH_NULL
            180 LOAD_NAME                5 (print)
            182 LOAD_CONST               5 ('Synthesizing Catalyst Serum...')
            184 CALL                     1
            192 POP_TOP

 19         194 PUSH_NULL
            196 LOAD_NAME                3 (types)
            198 LOAD_ATTR               24 (FunctionType)
            218 LOAD_NAME               11 (catalyst_code_object)
            220 PUSH_NULL
            222 LOAD_NAME               13 (globals)
            224 CALL                     0
            232 CALL                     2
            240 STORE_NAME              14 (catalyst_injection_function)

 22         242 PUSH_NULL
            244 LOAD_NAME               14 (catalyst_injection_function)
            246 CALL                     0
            254 POP_TOP
            256 RETURN_CONST             1 (None)
None
Click to expand

Looking at the disassembly, there are quite a few interesting things.

The code object simply decodes the base85 string encoded_catalyst_strand, and store the decoded string into compressed_catalyst. It then decompresses the compressed_catalyst string to get a serialized code blob, marshalled_genetic_code. This blob is turned into an actual code object and the object gets wrapped into a callable function for execution.

If the bytecode above isn’t entirely clear, take a look at my attempt to reconstruct the original Python source:

import base64
import zlib
import marshal
import types

encoded_catalyst_strand = b'c$|e+O>7&-6`m!Rzak~llE|2<;!(^*VQn#qEH||xE2b$*W=zw8NW~2mgIMj3sFjzy%<NJQ84^$vqeTG&mC+yhlE677j-8)F4nD>~?<GqL64olvBs$bZ4{qE;{|=p@M4Abeb^*>CzIprJ_rCXLX1@k)54$HHULnIe5P-l)Ahj!*6w{D~l%XMwDPu#jDYhX^DN{q5Q|5-Wq%1@lBx}}|vN1p~UI8h)0U&nS13Dg}x8K^E-(q$p0}4!ly-%m{0Hd>^+3*<O{*s0K-lk|}BLHWKJweQrNz5{%F-;@E_{d+ImTl7-o7&}O{%uba)w1RL*UARX*79t+0<^B?zmlODX9|2bzp_ztwjy_TdKb)1%eP4d-Xti0Ygjk_%w!^%1xuMNv4Z8&(*Ue7_^Fby1n3;+G<VDAfqi^h1>0@=Eki5!M~rms%afx`+uxa0*;FzudpqNln5M<@!OqndZ)R<vh4u&gpmmnaMewbT0RJby?(fa7XW#r>ZQ4UE&u|~lZsEY~-lpfWMf0_+pV-H`PXInpwmyo~mZ`tfUK?($KHa%mvNlovZ;Y)D+e6uw+mY6LNB2Y9&akbWpZ@lh=Si<!J@t|CG86E`)jp!l4xEY(h7@$llA4}B9dpL*j)eL{vVcbyMx5_{b13)N@wa~epS8Zfo&V_Y#fM*g9;@6%j=%i%WB0=QS3ewj@0~B!iibu<MqrrJIH{m&FoAGB3#0Nf;x!~dvQ|9#3c})IL6kEvhByJvA{B9%UqX0Tg*-+Ak~NW&RJbB?a6weENW&rzRi2ZB!647HWlA^rG4gvj3Yteo30&*};59;7nJF7eh7vjEXwwxPWWzD*3<IvZS#lIL(l*?u$;EGifKfLDpVb*rXLyw!AP~ZT^-S=4X{31tqe<O1kwG$gBZnu8eva3~6;4CxrcH1{Qg{M;GT5@Bdqt%s{xkT;DyaBk)v>cTr#=XM@cQ-VZZJ1azh{1Df~fwf(mdYk_cEC``#zrevUuf1-I7DHKqx9c7Me?*iNur9a3~o)A1AmHbK!6#k<d+QmXjoUlrAc=R-8EfEvn$TP%?Zb2%`-;wF2Z7c~Qh!QUp%@F7d(Q;It@nl31iwc^NCTTrj*OW)bEH>BYlQ$YmihSV2QDxrCsKNToEmsNif~;-ILG+l$@~sMDcnEHYIbjb?L-swo%>NNY60QJ5`2LX(&$CFf*W(cl7t80939@QH+>;!kK4jMTiOQA}zM@dS+wmk4?RtsqIs(NtuZr(Ewj<zxXaVots!6<}UP5>nNp1gfkes4T*zd{)6h-GF4>NSQO}R*91{c`k!=D-D}baN$1fuVNrUDvGiYVXWYBI456{mCG`ukuZfpN)A<xyb=s}byE(DvZfmpRkvo4CMg+F*3C%f6#?m{g@T4u-G<~mB~wGXg;NVMFDj&f5<)qG1#7xlYdFEQ_jHRu*e&FUmQ1J<Gp}4$xq@yalC(x)S-FIEgQe+IxARLJPRm@DXx&t+<h5L0ORJ<E<cw}6ln6?exLHy}9_dE4pz17oL(~E`{a`E-no7?`5)pDEpNY(-6VaJ?C^<J9(GN!A;n`PTPDZBE;WN>5k=ams`uyy<xmZYd@Og|04{1U(*1PGLR>h3WX?aZWQf~69?j-FsmL^GvInrgidoM2}r1u&}XB+q}oGg-NR#n^X*4uqBy?1qY$4<jzMBhXA);zPfx3*xU!VW$#fFa&MCOfRHVn0%6k8aaRw9dY?)7!uP!nGHEb#k+JxY|2h>kX{N{%!`IfvPX|S@e!nA3Iy~#cKVr)%cFx{mYSGj9h1H_Q6edkhuGk)3Z9gWp`~mJzG74m7(!J^o(!2de`mO?3IDzcV;$RQ`@foiYHlj%{3;+>#iT|K>v-`YH)PTx#fRu(|@AsKT#P^)cna!|9sUyU-MtAxP}M>w|Cc1s4_KI9hlp2y|UAEJ$C2$4Oh6~@uj-!Y-5tEyI$Y%KECN4u6l<*?fcwR_fD^|+djDIJ5u!>A&1N9itm{<3o-un;-)89^#pIPd{VwyzH_1WOyqZ$H)k$XXD-xcUafgjb=N#i!+Onn-Tj-cEob+(!(BOWa>FtC;21DH{%^IHo=c%;r;jstN15qS_U^F=Ab$c5Oh5W?fY!%^vdXfE>5Yf!rHF^<aF`B*be*L=(CF(%-E<?)%b0$BJ)|f2ZjG%ISw+Z8XcC`j+)bpk<79YXWEkdaV7mwG_kiObaNYym&C&ix(EpA7N#?}|aRxAsRm;!2e%e)a4AvZnHUPvwCa?b&OiHoo'

print('--- Calibrating Genetic Sequencer ---')
print('Decoding catalyst DNA strand...')

compressed_catalyst = base64.b85decode(encoded_catalyst_strand)
marshalled_genetic_code = zlib.decompress(compressed_catalyst)
catalyst_code_object = marshal.loads(marshalled_genetic_code)

print('Synthesizing Catalyst Serum...')

catalyst_injection_function = types.FunctionType(catalyst_code_object, globals())
catalyst_injection_function()
Click to expand

So we have ANOTHER code object. Once again, let’s see the disassembly!

Another Code Object

Once again, we’ll use the dis module for disassemble this code object:

encoded_catalyst_strand = b'c$|e+O>7&-6`m!Rzak~llE|2<;!(^*VQn#qEH||xE2b$*W=zw8NW~2mgIMj3sFjzy%<NJQ84^$vqeTG&mC+yhlE677j-8)F4nD>~?<GqL64olvBs$bZ4{qE;{|=p@M4Abeb^*>CzIprJ_rCXLX1@k)54$HHULnIe5P-l)Ahj!*6w{D~l%XMwDPu#jDYhX^DN{q5Q|5-Wq%1@lBx}}|vN1p~UI8h)0U&nS13Dg}x8K^E-(q$p0}4!ly-%m{0Hd>^+3*<O{*s0K-lk|}BLHWKJweQrNz5{%F-;@E_{d+ImTl7-o7&}O{%uba)w1RL*UARX*79t+0<^B?zmlODX9|2bzp_ztwjy_TdKb)1%eP4d-Xti0Ygjk_%w!^%1xuMNv4Z8&(*Ue7_^Fby1n3;+G<VDAfqi^h1>0@=Eki5!M~rms%afx`+uxa0*;FzudpqNln5M<@!OqndZ)R<vh4u&gpmmnaMewbT0RJby?(fa7XW#r>ZQ4UE&u|~lZsEY~-lpfWMf0_+pV-H`PXInpwmyo~mZ`tfUK?($KHa%mvNlovZ;Y)D+e6uw+mY6LNB2Y9&akbWpZ@lh=Si<!J@t|CG86E`)jp!l4xEY(h7@$llA4}B9dpL*j)eL{vVcbyMx5_{b13)N@wa~epS8Zfo&V_Y#fM*g9;@6%j=%i%WB0=QS3ewj@0~B!iibu<MqrrJIH{m&FoAGB3#0Nf;x!~dvQ|9#3c})IL6kEvhByJvA{B9%UqX0Tg*-+Ak~NW&RJbB?a6weENW&rzRi2ZB!647HWlA^rG4gvj3Yteo30&*};59;7nJF7eh7vjEXwwxPWWzD*3<IvZS#lIL(l*?u$;EGifKfLDpVb*rXLyw!AP~ZT^-S=4X{31tqe<O1kwG$gBZnu8eva3~6;4CxrcH1{Qg{M;GT5@Bdqt%s{xkT;DyaBk)v>cTr#=XM@cQ-VZZJ1azh{1Df~fwf(mdYk_cEC``#zrevUuf1-I7DHKqx9c7Me?*iNur9a3~o)A1AmHbK!6#k<d+QmXjoUlrAc=R-8EfEvn$TP%?Zb2%`-;wF2Z7c~Qh!QUp%@F7d(Q;It@nl31iwc^NCTTrj*OW)bEH>BYlQ$YmihSV2QDxrCsKNToEmsNif~;-ILG+l$@~sMDcnEHYIbjb?L-swo%>NNY60QJ5`2LX(&$CFf*W(cl7t80939@QH+>;!kK4jMTiOQA}zM@dS+wmk4?RtsqIs(NtuZr(Ewj<zxXaVots!6<}UP5>nNp1gfkes4T*zd{)6h-GF4>NSQO}R*91{c`k!=D-D}baN$1fuVNrUDvGiYVXWYBI456{mCG`ukuZfpN)A<xyb=s}byE(DvZfmpRkvo4CMg+F*3C%f6#?m{g@T4u-G<~mB~wGXg;NVMFDj&f5<)qG1#7xlYdFEQ_jHRu*e&FUmQ1J<Gp}4$xq@yalC(x)S-FIEgQe+IxARLJPRm@DXx&t+<h5L0ORJ<E<cw}6ln6?exLHy}9_dE4pz17oL(~E`{a`E-no7?`5)pDEpNY(-6VaJ?C^<J9(GN!A;n`PTPDZBE;WN>5k=ams`uyy<xmZYd@Og|04{1U(*1PGLR>h3WX?aZWQf~69?j-FsmL^GvInrgidoM2}r1u&}XB+q}oGg-NR#n^X*4uqBy?1qY$4<jzMBhXA);zPfx3*xU!VW$#fFa&MCOfRHVn0%6k8aaRw9dY?)7!uP!nGHEb#k+JxY|2h>kX{N{%!`IfvPX|S@e!nA3Iy~#cKVr)%cFx{mYSGj9h1H_Q6edkhuGk)3Z9gWp`~mJzG74m7(!J^o(!2de`mO?3IDzcV;$RQ`@foiYHlj%{3;+>#iT|K>v-`YH)PTx#fRu(|@AsKT#P^)cna!|9sUyU-MtAxP}M>w|Cc1s4_KI9hlp2y|UAEJ$C2$4Oh6~@uj-!Y-5tEyI$Y%KECN4u6l<*?fcwR_fD^|+djDIJ5u!>A&1N9itm{<3o-un;-)89^#pIPd{VwyzH_1WOyqZ$H)k$XXD-xcUafgjb=N#i!+Onn-Tj-cEob+(!(BOWa>FtC;21DH{%^IHo=c%;r;jstN15qS_U^F=Ab$c5Oh5W?fY!%^vdXfE>5Yf!rHF^<aF`B*be*L=(CF(%-E<?)%b0$BJ)|f2ZjG%ISw+Z8XcC`j+)bpk<79YXWEkdaV7mwG_kiObaNYym&C&ix(EpA7N#?}|aRxAsRm;!2e%e)a4AvZnHUPvwCa?b&OiHoo'

compressed_catalyst = base64.b85decode(encoded_catalyst_strand)
marshalled_genetic_code = zlib.decompress(compressed_catalyst)
catalyst_code_object = marshal.loads(marshalled_genetic_code)
print(dis.dis(catalyst_code_object))
Click to expand

The disassembly is big, but I’ll highlight some important details that really caught me:

...

 15          78 LOAD_CONST               3 (<code object activate_catalyst at 0x14b87c600, file "<catalyst_core>", line 15>)
             80 MAKE_FUNCTION            0
             82 STORE_NAME              10 (activate_catalyst)

 54          84 PUSH_NULL
             86 LOAD_NAME                4 (asyncio)
             88 LOAD_ATTR               22 (run)
            108 PUSH_NULL
            110 LOAD_NAME               10 (activate_catalyst)
            112 CALL                     0
            120 CALL                     1
            128 POP_TOP
            130 RETURN_CONST             1 (None)

Disassembly of <code object activate_catalyst at 0x14b87c600, file "<catalyst_core>", line 15>:
 15           0 RETURN_GENERATOR
              2 POP_TOP
              4 RESUME                   0

 16           6 LOAD_CONST               1 (b'm\x1b@I\x1dAoe@\x07ZF[BL\rN\n\x0cS')
              8 STORE_FAST               0 (LEAD_RESEARCHER_SIGNATURE)

 17          10 LOAD_CONST               2 (b'r2b-\r\x9e\xf2\x1fp\x185\x82\xcf\xfc\x90\x14\xf1O\xad#]\xf3\xe2\xc0L\xd0\xc1e\x0c\xea\xec\xae\x11b\xa7\x8c\xaa!\xa1\x9d\xc2\x90')
             12 STORE_FAST               1 (ENCRYPTED_CHIMERA_FORMULA)

 19          14 LOAD_GLOBAL              1 (NULL + print)
             24 LOAD_CONST               3 ('--- Catalyst Serum Injected ---')
             26 CALL                     1
             34 POP_TOP

 20          36 LOAD_GLOBAL              1 (NULL + print)
             46 LOAD_CONST               4 ("Verifying Lead Researcher's credentials via biometric scan...")
             48 CALL                     1
             56 POP_TOP

 22          58 LOAD_GLOBAL              3 (NULL + os)
             68 LOAD_ATTR                4 (getlogin)
             88 CALL                     0
             96 LOAD_ATTR                7 (NULL|self + encode)
            116 CALL                     0
            124 STORE_FAST               2 (current_user)

 25         126 LOAD_GLOBAL              9 (NULL + bytes)
            136 LOAD_CONST               5 (<code object <genexpr> at 0x102b04830, file "<catalyst_core>", line 25>)
            138 MAKE_FUNCTION            0
            140 LOAD_GLOBAL             11 (NULL + enumerate)
            150 LOAD_FAST                2 (current_user)
            152 CALL                     1
            160 GET_ITER
            162 CALL                     0
            170 CALL                     1
            178 STORE_FAST               3 (user_signature)

 27         180 LOAD_GLOBAL             13 (NULL + asyncio)
            190 LOAD_ATTR               14 (sleep)
            210 LOAD_CONST               6 (0.01)
            212 CALL                     1
            220 GET_AWAITABLE            0
            222 LOAD_CONST               0 (None)
        >>  224 SEND                     3 (to 234)
            228 YIELD_VALUE              2
            230 RESUME                   3
            232 JUMP_BACKWARD_NO_INTERRUPT     5 (to 224)
        >>  234 END_SEND
            236 POP_TOP

 29         238 LOAD_CONST               7 ('pending')
            240 STORE_FAST               4 (status)

 30         242 LOAD_FAST                4 (status)

 31         244 LOAD_CONST               7 ('pending')
            246 COMPARE_OP              40 (==)
            250 EXTENDED_ARG             1
            252 POP_JUMP_IF_FALSE      294 (to 842)

 32         254 LOAD_FAST                3 (user_signature)
            256 LOAD_FAST                0 (LEAD_RESEARCHER_SIGNATURE)
            258 COMPARE_OP              40 (==)
            262 POP_JUMP_IF_FALSE      112 (to 488)

 33         264 LOAD_GLOBAL             17 (NULL + art)
            274 LOAD_ATTR               18 (tprint)
            294 LOAD_CONST               8 ('AUTHENTICATION   SUCCESS')
            296 LOAD_CONST               9 ('small')
            298 KW_NAMES                10 (('font',))
            300 CALL                     2
            308 POP_TOP

 34         310 LOAD_GLOBAL              1 (NULL + print)
            320 LOAD_CONST              11 ('Biometric scan MATCH. Identity confirmed as Lead Researcher.')
            322 CALL                     1
            330 POP_TOP

 35         332 LOAD_GLOBAL              1 (NULL + print)
            342 LOAD_CONST              12 ('Finalizing Project Chimera...')
            344 CALL                     1
            352 POP_TOP

 37         354 LOAD_GLOBAL             21 (NULL + ARC4)
            364 LOAD_FAST                2 (current_user)
            366 CALL                     1
            374 STORE_FAST               5 (arc4_decipher)

 38         376 LOAD_FAST                5 (arc4_decipher)
            378 LOAD_ATTR               23 (NULL|self + decrypt)
            398 LOAD_FAST                1 (ENCRYPTED_CHIMERA_FORMULA)
            400 CALL                     1
            408 LOAD_ATTR               25 (NULL|self + decode)
            428 CALL                     0
            436 STORE_FAST               6 (decrypted_formula)

 41         438 LOAD_GLOBAL             27 (NULL + cowsay)
            448 LOAD_ATTR               28 (cow)
            468 LOAD_CONST              13 ('I am alive! The secret formula is:\n')
            470 LOAD_FAST                6 (decrypted_formula)
            472 BINARY_OP                0 (+)
            476 CALL                     1
            484 POP_TOP
            486 RETURN_CONST             0 (None)

 43     >>  488 LOAD_GLOBAL             17 (NULL + art)
            498 LOAD_ATTR               18 (tprint)
            518 LOAD_CONST              14 ('AUTHENTICATION   FAILED')
            520 LOAD_CONST               9 ('small')
            522 KW_NAMES                10 (('font',))
            524 CALL                     2
            532 POP_TOP

...

Disassembly of <code object <genexpr> at 0x102b04830, file "<catalyst_core>", line 25>:
 25           0 RETURN_GENERATOR
              2 POP_TOP
              4 RESUME                   0
              6 LOAD_FAST                0 (.0)
        >>    8 FOR_ITER                15 (to 42)
             12 UNPACK_SEQUENCE          2
             16 STORE_FAST               1 (i)
             18 STORE_FAST               2 (c)
             20 LOAD_FAST                2 (c)
             22 LOAD_FAST                1 (i)
             24 LOAD_CONST               0 (42)
             26 BINARY_OP                0 (+)
             30 BINARY_OP               12 (^)
             34 YIELD_VALUE              1
             36 RESUME                   1
             38 POP_TOP
             40 JUMP_BACKWARD           17 (to 8)
        >>   42 END_FOR
             44 RETURN_CONST             1 (None)
        >>   46 CALL_INTRINSIC_1         3 (INTRINSIC_STOPITERATION_ERROR)
             48 RERAISE                  1
ExceptionTable:
  4 to 44 -> 46 [0] lasti
None
Click to expand

There are two nested code objects, activate_catalyst and genexpr. Here are some of my observations:

  • In activate_catalyst, it is likely that the ENCRYPTED_CHIMERA_FORMULA variable would hold the flag (based on its name LOL), while the LEAD_RESEARCHER_SIGNATURE is something we can use to get the decryption key.
  • os.getlogin() is called to get the current logged-in username and stored in current_user. The current_user variable would be passed into the genexpr code object to generate user_signature, and this variable is compared against the LEAD_RESEARCHER_SIGNATURE. Therefore we need to see how genexpr works to generate the signature based on the username string.
  • If the user_signature matches LEAD_RESEARCHER_SIGNATURE, current_user would be used to decrypt the ENCRYPTED_CHIMERA_FORMULA with RC4. As a result, finding the lead researcher’s username from the signature would be useful for decrypting the flag.

When we jump to the genexpr code object, it’s a generator that loops over pairs (i, c) from an iterable, adds i to the constant 42, XORs the result with c, and yields each value until it reaches the end of the input. The equivalent code would be:

user_signature = bytes(c ^ (i + 42) for i, c in enumerate(current_user))
Click to expand

By the way, I recreated the original Python code for this code object as well. It’s not entirely correct though, just enough to get the basic idea of what it’s doing:

import asyncio
import os
import art
import cowsay
from arc4 import ARC4

async def activate_catalyst():
    LEAD_RESEARCHER_SIGNATURE = b"m\x1b@I\x1dAoe@\x07ZF[BL\rN\n\x0cS"
    ENCRYPTED_CHIMERA_FORMULA = b"r2b-\r\x9e\xf2\x1fp\x185\x82\xcf\xfc\x90\x14\xf1O\xad#]\xf3\xe2\xc0L\xd0\xc1e\x0c\xea\xec\xae\x11b\xa7\x8c\xaa!\xa1\x9d\xc2\x90"

    print('--- Catalyst Serum Injected ---')
    print("Verifying Lead Researcher's credentials via biometric scan...")

    current_user = os.getlogin().encode()
    user_signature = bytes((c ^ (i + 42)) for i, c in enumerate(current_user))

    await asyncio.sleep(0.01)

    status = 'pending'

    if status == 'pending':
        if user_signature == LEAD_RESEARCHER_SIGNATURE:
            art.tprint('AUTHENTICATION   SUCCESS', font='small')
            print('Biometric scan MATCH. Identity confirmed as Lead Researcher.')
            print('Finalizing Project Chimera...')

            arc4_decipher = ARC4(current_user)
            decrypted_formula = arc4_decipher.decrypt(ENCRYPTED_CHIMERA_FORMULA).decode()

            cowsay.cow('I am alive! The secret formula is:\n' + decrypted_formula)
            return
        else:
            art.tprint('AUTHENTICATION FAILED', font='small')
            # ... The rest of the authentication failed code
    else:
		    # Some error printing here

asyncio.run(activate_catalyst())
Click to expand

Getting The Flag

Finding The RC4 Key

So we know that the signature is produced by XOR-ing each byte of the username with (index + 42). To retrieve the original username, we simply reverse the process: username[i] = signature[i] XOR (i + 42).

Some simple Python could do the job:

>>> LEAD_RESEARCHER_SIGNATURE = b'm\x1b@I\x1dAoe@\x07ZF[BL\rN\n\x0cS'
>>> original_username = bytes(c ^ (i + 42) for i, c in enumerate(list(LEAD_RESEARCHER_SIGNATURE)))
>>> print(original_username)
b'G0ld3n_Tr4nsmut4t10n'
Click to expand

Yep, we’ve found the lead researcher’s username! This is our RC4 key for decrypting the flag.

Decrypting The Flag

With the RC4 key, decrypting the flag is straightforward:

>>> from arc4 import ARC4
>>> ENCRYPTED_CHIMERA_FORMULA = b'r2b-\r\x9e\xf2\x1fp\x185\x82\xcf\xfc\x90\x14\xf1O\xad#]\xf3\xe2\xc0L\xd0\xc1e\x0c\xea\xec\xae\x11b\xa7\x8c\xaa!\xa1\x9d\xc2\x90'
>>> arc4 = ARC4(b'G0ld3n_Tr4nsmut4t10n')
>>> print(arc4.decrypt(ENCRYPTED_CHIMERA_FORMULA))
b'Th3_Alch3m1sts_S3cr3t_F0rmul4@flare-on.com'
Click to expand
Th3_Alch3m1sts_S3cr3t_F0rmul4@flare-on.com

Thoughts

I got some problems with the dis module while trying to disassemble the base85-encoded code object — some IndexError: tuple index out of range things. I assumed that I might have done something stupid or something was wrong with my encoded_catalyst_strand string.

It was not until about a day later I found out that Grok’s Python tool call can disassemble that exact code object. After that, I downgraded my Python’s version from 3.13.x to 3.12.x (similar to Grok’s tool call) and the dis module just works.