About three weeks ago, we published our second CrackMe. It triggered a lot of interest, and we got many high-quality write-ups. Choosing the winner was really difficult!

In this post, I am going to summarize the contest and comment on the received submissions.

CrackMe 2 challenge

The topic of the challenge was Python, and its goal was to teach how the Python scripts can be packaged and integrated with native executables. The involved Python script was not obfuscated, and the user was supposed to adapt it for the purpose of finding the solution.

The CrackMe was made of three components, cooperating with each other:

  • a Python script (converted to EXE with the help of PyInstaller)
  • a native DLL, loaded with the help of the above script
  • a Python script unpacked by the DLL and injected into Actxproxy.dll

In the first level, the user was supposed to find a valid PIN to decode a URL, from which the next level was downloaded. The next level was a native DLL that was injected in the main, Pyinstaller-based EXE by the initial script.

After the second level was passed, the DLL was unpacking another piece of obfuscated Python script and injecting it into the header of Actxproxy.dll. When the DLL finished and the execution was passed back to the main script, the chunk of data was read from Actxproxy.dll, decoded, and executed. So, the Actxproxy.dll was used not as a DLL, but rather as a named memory area where the data was covertly passed from one component to another.

There were a number of other techniques used, such as:

  • A covert way to host and download a PE file: a PE was encoded as an image (with file2png.py) and hosted on a image-hosting site
  • A Reflective loader (based on the Steven Fewer’s template)
  • A modified PE header, thanks to which the PE file can be injected like a piece of shellcode
  • Changing the execution flow with the help of exception handlers
  • Using a pseudo-random sequence as an AES key (taking advantage of the fact that the standard function rand() is not really random—a sequence generated with the same seed will be the same)


The CrackMe has so far been downloaded 956 times (653 of those were in the first weekend after the release).

We had people from all around the world participating. The most downloads were from the US (243), Israel (59), and the UK (52).

Hall of fame

The submission was counted as valid if it contained the final flag:

flag{“Things are not always what they seem; the first appearance deceives many; the intelligence of a few perceives what has been carefully hidden.” – Phaedrus}

In total, I got flags from 25 people. Congratulations to all of you, you did great!

12 people solved it during the first weekend:

    1. [write-up]
    2. [write-up]
    3. [write-up]
    4. [write-up]
    5. [write-up]

The remaining 13 solved it in the following days:

  1. May 1:
  2. May 1:
  3. May 2: [write-up] [video]
  4. May 2:
  5. May 2: [write-up]
  6. May 2:
  7. May 3:
  8. May 3: [write-up]
  9. May 3: [write-up]
  10. May 6:
  11. May 10: [write-up]
  12. May 15: [write-up]
  13. May 18: [write-up]

Write-up scoring

Due to the high quality of the received write-ups, it was hard to select the winner. In order to introduce some objective measures, several categories were used to assign points.

The first category involved an in-depth explanation of the inner workings of the CrackMe, guiding through the process of solving. This means writers should have:

  1. Explained how to identify that the executable is in reality a wrapped Python script
  2. Provided tools and an explanation of how to unpack the PyInstaller executable back to Python script
  3. Demonstrated how to find the password having an MD5
  4. Explained why the key generated is using rand() and is not really random
  5. Provided and explained a sourcecode of brutforcer for the PIN
  6. Explained how the PE file is decoded from the image
  7. Noticed and understood the PE header modification (shellcodified PE)
  8. Noticed and understood the Reflective Loader in the DLL
  9. Explained how the VEH was used to modify the execution flow
  10. Noticed what checks were made in order to prevent the DLL from running outside the Python
  11. Explained the mechanism behind the “secret console”
  12. Explained how the Actprxy.dll is used as a communication channel between the two independent modules
  13. Analyzed the injected data and minimized the set of the characters that has to be brutforced
  14. Explained what (and why) to expect in the output (in level 3 decoding)
  15. Provided and explained the script used to solve the final Python chunk

The second category was style. Writers should have:

  1. Introduced tools before they were used, showing the environment setup
  2. Provided detailed explanation of the used techniques, reaching beyond the CrackMe itself. For example, providing links where the reader can learn more.
  3. Provided graphical illustration of the taken steps. Diagrams, GIFs, videos, etc.
  4. Been especially clear and had a pleasant writing style

You could also get some bonus points for OSINT if you found:

  1. My tool to do exactly the same conversion as used in the CrackMe (file2png,py)
  2. My post explaining in detail how to unpack PyInstaller-based EXE
  3. A malware that previously used any of the descried techniques (i.e. shellcodified PE was used by Shakti Trojan, PyInstaller package was used by Telegram RAT, Reflective Loader is commonly used by multiple malware, for example the dropper of the original Petya)

Points per write-up (plus, their extra features that gave a bonus point)

  1. @Hexacorn’s write-up: 8
  2. @_qaz_qaz’s writeup: 13.5
  3. @pieceofsummer’s write-up: 8
  4. @KernelM0de‘s write-up: 8.5
  5. @Eleemosynator’s write-up: 16
  6. @LadislavZezula‘s write-up and video: 13.5
    • adding the video to the write-up was really cool!
  7. @tqkve’s write-up: 7.5
    • interesting explanation how to manually extract the pyc module from PyInstaller-based EXE
  8. @voidm4p‘s write-up: 12.5
  9. @ravitiwari1989’s write-up: 13.5
  10. @Jacob_Pimental’s write-up: 8
  11. @th3m4ks’s write-up: 12.5
  12.  @nictln’s write-up: 12
    • the best way to solve the last stage: plaintext attack instead of brutforce

The write-ups showed a diversity of ideas, each of them providing a fresh perspective and a valuable source of learning for the people who want to enter into our field. Thank you so much for your participation! As a small token of our appreciation, we decided to give Malwarebytes swag to each writeup author! You can contact me via twitter for details.


I decided to reward four authors, who’s solutions stood out:

  • @Eleemosynator
  • @ravitiwari1989
  • @_qaz_qaz
  • @LadislavZezula

Each winner can choose a book of their liking (in a printed or electronic form). Contact me on Twitter for receiving the reward. Congratulations again, and thank you for all the work that you put in writing the solution!