Logo
Overview

Fighting Flare-On 12

November 16, 2025
11 min read
index

Back with another Flare-On challenge! Each year, Flare-On offers a unique set of reverse engineering puzzles that push our skills to the limit. This year was no different. In this blog, I will document my journey through the Flare-On 12 challenges, highlighting the obstacles I faced and the solutions I devised.

Timeline

ChallengeDateTime
Drill Baby Drill!Oct 0210:50:24 AM
project_chimeraOct 022:14:23 PM
pretty_devilish_fileOct 0411:01:32 AM

Drill Baby Drill!

Introduction

This game is written in PyGame. It is about a baby trying to drill to recover its lost teddy bears. The source code is provided, as well as a runnable pyinstaller EXE file.

To launch the game run DrillBabyDrill.exe on a Windows computer. Otherwise, follow these basic python execution instructions:

  1. Install Python 3
  2. Install PyGame (“pip install pygame”)
  3. Run the game: python DrillBabyDrill.py

Solution

We are also provided with the source code along side the executable so we can compile and run from the source

The game starts with a random level out of 6 levels. These levels have a name as such

'California',
'Ohio',
'Death Valley',
'Mexico',
'The Grand Canyon'

We need to drill through the sand in order to get the bear and by avoiding hitting any boulders.

Going through the source, I came across how boulders are set up during the start.

boulder_layout = []
for i in range(0, tiles_width):
if (i != len(LevelNames[current_level])):
boulder_layout.append(random.randint(2, max_drill_level))
else:
boulder_layout.append(-1)

This line of code iterates through each possible x-coordinate (i). It then checks if the x-coordinate is not equal to the length of the level name and places a boulder at a random depth else at -1.

This means that in order to avoid boulders we need to drill at the coordinate equal to the length of the level name. We can calculate the length of each level name:

Level NameLength
California10
Ohio4
Death Valley12
Mexico6
The Grand Canyon16

We can now play the game and drill down the Loc coordinate as per the level name

project_chimera

=================================================================
== 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

This journal entry provides us with some context about the challenge. We have two components to deal with: the Genetic Sequencer and the Catalyst Serum. The Catalyst Serum is keyed to Dr. Khem’s biometric signature, which means we need to find a way to bypass this restriction.

import zlib
import marshal
# These are my encrypted instructions for the Sequencer.
encrypted_sequencer_data = b'x\x9cm\x96K\xcf\..\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))

After fixing and installing all of the dependencies, we get the following output.

After decompiling and analyzing the bytecode, the exploit becomes clear. The code performs the following steps:

  1. Stage 1: Decompresses and deserializes the initial payload
  2. Stage 2: Decodes a base85-encoded, zlib-compressed Python code object that contains the main logic
  3. Stage 3: Implements biometric authentication via username verification
import zlib
import marshal
# These are my encrypted instructions for the Sequencer.
encrypted_sequencer_data = b'x\x9cm\x96K\xcf\..\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))
obj = marshal.loads(sequencer_code)
dis.dis(obj)
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)
# SEQUENCE 2
en_seq = b'c$|e+O>7&-6`m!Rzak~llE|2<;....a4AvZnHUPvwCa?b&OiHoo'
base_decode = base64.b85decode(en_seq)
decompress = zlib.decompress(base_decode)
obj = marshal.loads(decompress)
dis.dis(obj)
0 0 RESUME 0
2 2 LOAD_CONST 0 (0)
4 LOAD_CONST 1 (None)
6 IMPORT_NAME 0 (os)
8 STORE_NAME 0 (os)
3 10 LOAD_CONST 0 (0)
12 LOAD_CONST 1 (None)
14 IMPORT_NAME 1 (sys)
16 STORE_NAME 1 (sys)
4 18 LOAD_CONST 0 (0)
20 LOAD_CONST 1 (None)
22 IMPORT_NAME 2 (emoji)
24 STORE_NAME 2 (emoji)
5 26 LOAD_CONST 0 (0)
28 LOAD_CONST 1 (None)
30 IMPORT_NAME 3 (random)
32 STORE_NAME 3 (random)
6 34 LOAD_CONST 0 (0)
36 LOAD_CONST 1 (None)
38 IMPORT_NAME 4 (asyncio)
40 STORE_NAME 4 (asyncio)
7 42 LOAD_CONST 0 (0)
44 LOAD_CONST 1 (None)
46 IMPORT_NAME 5 (cowsay)
48 STORE_NAME 5 (cowsay)
8 50 LOAD_CONST 0 (0)
52 LOAD_CONST 1 (None)
54 IMPORT_NAME 6 (pyjokes)
56 STORE_NAME 6 (pyjokes)
9 58 LOAD_CONST 0 (0)
60 LOAD_CONST 1 (None)
62 IMPORT_NAME 7 (art)
64 STORE_NAME 7 (art)
10 66 LOAD_CONST 0 (0)
68 LOAD_CONST 2 (('ARC4',))
70 IMPORT_NAME 8 (arc4)
72 IMPORT_FROM 9 (ARC4)
74 STORE_NAME 9 (ARC4)
76 POP_TOP
15 78 LOAD_CONST 3 (<code object activate_catalyst at 0x000001ED4EF77760, 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 0x000001ED4EF77760, 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 0x000001ED4F040C30, 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
44 534 LOAD_GLOBAL 1 (NULL + print)
544 LOAD_CONST 15 ('Impostor detected, my genius cannot be replicated!')
546 CALL 1
554 POP_TOP
45 556 LOAD_GLOBAL 1 (NULL + print)
566 LOAD_CONST 16 ('The resulting specimen has developed an unexpected, and frankly useless, sense of humor.')
568 CALL 1
576 POP_TOP
47 578 LOAD_GLOBAL 31 (NULL + pyjokes)
588 LOAD_ATTR 32 (get_joke)
608 LOAD_CONST 17 ('en')
610 LOAD_CONST 18 ('all')
612 KW_NAMES 19 (('language', 'category'))
614 CALL 2
622 STORE_FAST 7 (joke)
48 624 LOAD_GLOBAL 26 (cowsay)
634 LOAD_ATTR 34 (char_names)
654 LOAD_CONST 20 (1)
656 LOAD_CONST 0 (None)
658 BINARY_SLICE
660 STORE_FAST 8 (animals)
49 662 LOAD_GLOBAL 1 (NULL + print)
672 LOAD_GLOBAL 27 (NULL + cowsay)
682 LOAD_ATTR 36 (get_output_string)
702 LOAD_GLOBAL 39 (NULL + random)
712 LOAD_ATTR 40 (choice)
732 LOAD_FAST 8 (animals)
734 CALL 1
742 LOAD_GLOBAL 31 (NULL + pyjokes)
752 LOAD_ATTR 32 (get_joke)
772 CALL 0
780 CALL 2
788 CALL 1
796 POP_TOP
50 798 LOAD_GLOBAL 43 (NULL + sys)
808 LOAD_ATTR 44 (exit)
828 LOAD_CONST 20 (1)
830 CALL 1
838 POP_TOP
840 RETURN_CONST 0 (None)
51 >> 842 NOP
52 844 LOAD_GLOBAL 1 (NULL + print)
854 LOAD_CONST 21 ('System error: Unknown experimental state.')
856 CALL 1
864 POP_TOP
866 RETURN_CONST 0 (None)
27 >> 868 CLEANUP_THROW
870 EXTENDED_ARG 1
872 JUMP_BACKWARD 320 (to 234)
>> 874 CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
876 RERAISE 1
ExceptionTable:
4 to 226 -> 874 [0] lasti
228 to 228 -> 868 [2]
230 to 868 -> 874 [0] lasti
Disassembly of <code object <genexpr> at 0x000001ED4F040C30, 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

The key part of the exploit is bypassing the biometric authentication by mimicking Dr. Khem’s signature. The code generates a user signature based on the current username and compares it to the lead researcher’s signature. If they match, it decrypts and reveals the secret formula.

Here is my final code to run the sequencer and reveal the secret formula:

SOLVE.PY
import dis
import zlib
import base64
import marshal
from arc4 import ARC4
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("\n--- STAGE 1 ---")
seq_1 = zlib.decompress(encrypted_sequencer_data)
obj = marshal.loads(seq_1)
dis.dis(obj)
print("\n--- STAGE 2 ---")
en_seq = 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'
base_decode = base64.b85decode(en_seq)
decompress = zlib.decompress(base_decode)
obj = marshal.loads(decompress)
dis.dis(obj)
print("\n--- STAGE 3 ---")
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'
# Getting the Username from LEAD_RESEARCHER_SIGNATURE
required_username_bytes = bytearray()
for i, c in enumerate(LEAD_RESEARCHER_SIGNATURE):
original_char_code = c ^ (i + 42)
required_username_bytes.append(original_char_code)
required_username = required_username_bytes.decode('utf-8')
print(f"Required username: {required_username}")
# Using the Username to decrypt ENCRYPTED_CHIMERA_FORMULA
key = bytes(required_username_bytes)
cipher = ARC4(key)
decrypted_formula = cipher.decrypt(ENCRYPTED_CHIMERA_FORMULA)
flag = decrypted_formula.decode('utf-8')
print(f"Decrypted flag: {flag}")

pretty_devilish_file

We are provided with a corrupted PDF file. After analyzing and repairing the file structure, we can successfully open and view its contents.

Which I think is not the intended solution as we didn’t get any flags

https://github.com/DidierStevens/DidierStevensSuite/blob/master/pdf-parser.py

Terminal window
python pdf-parser.py pretty_devilish_file.pdf

object 4 0 R contains a stream with /Filter /FlateDecode

Dumping the encrypted stream with pdf-parser to extract the FlateDecode stream data We can use pdf-parser with the -o option to dump a specific object and its stream:

The stream is broken and needs to be repaired. Using the pdf-parser tool, we can extract the raw stream data and attempt to decompress it manually.

The FlateDecode compressed stream contains the actual PDF content.

Fixing the broken stream

We will be using qpdf to decrypt the AES first

https://github.com/qpdf/qpdf

Terminal window
qpdf --decrypt pretty_devilish_file.pdf d_pretty_devilish_file.pdf

Now we can deflate the stream using pdf-parser I then wrote a script to extract the flag stored inside the JPEG image data within the PDF.

/W 37/H 1 gives us an idea that the image is of 37 pixel width and 1 pixel height

/F[/AHx /DCT] the filter indicates that the data is first ASCII hex decoded and then JPEG decoded

To get the flag, you need to decode the hex data to get the raw JPEG bytestream, load it as an image, and then read the value of each of the 37 pixels. These values will correspond to the ASCII codes for the flag’s characters.

solve.py
import io
import binascii
from PIL import Image
"""
This is a 37x1 pixel JPEG image encoded as a hex string extracted from the pdf
We will decode the hex into raw JPEG bytes, then use Pillow to open the image and extract pixel values
Finally we convert pixel values to ASCII characters to form the flag
"""
hex_data = "ffd8ffe000104a46494600010100000100010000ffdb00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101ffc0000b080001002501011100ffc40017000100030000000000000000000000000006040708ffc400241000000209050100000000000000000000000702050608353776b6b7030436747577ffda0008010100003f00c54d3401dcbbfb9c38db8a7dd265a2159e9d945a086407383aabd52e5034c274e57179ef3bcdfca50f0af80aff00e986c64568c7ffd9"
try:
jpeg_bytes = binascii.unhexlify(hex_data)
except binascii.Error as e:
print(f"Error decoding hex data: {e}")
exit()
image_stream = io.BytesIO(jpeg_bytes)
img = Image.open(image_stream)
pixels = list(img.getdata())
flag = "".join(chr(p) for p in pixels)
print(f"flag: {flag}")