Link to archived challenge

Category Difficulty Solves Author
reverse medium 8 tetratarius

Description

We’ve found what we suspect to be one of the hacker’s programs for storing secrets, we believe that a secret key is hidden within the program.

Players are given a file to download: secretman.pyc.

There are also two hints available:

  1. Look up what a .pyc file is.
  2. Look up how to reverse engineer a .pyc file.

Analysis

First, let’s run file secretman.pyc for some basic information:

secretman.pyc: Byte-compiled Python module for CPython 3.10, timestamp-based, .py timestamp: Sat Nov 19 00:00:40 2022 UTC, .py size: 3308 bytes

.pyc files like this contain compiled Python bytecode: a machine-optimized form of Python code.
This means we cannot simply open the file in a text editor to read it.

Usually, we can use a decompiler like decompyle3 or pycdc to try to recover something resembling the original source code.

However, my solution uses a different approach: we’re going to use dynamic analysis instead.

Solution

Note: I’m using IPython for a better interactive shell, but the default Python shell should work as well.

.pyc files can be imported just like any old .py file:

In [1]: import secretman
m1n074ur's secrets

1. view all secrets
2. view specific secret
3. enter new secret
5. exit
Enter your selection (#): 5

Exiting...

The program’s main menu appears when we try to import it, so I entered 5 to exit.
(This happens when you don’t use if __name__ == "__main__" blocks.)

Now, we can use tab-completion to explore the module’s top-level objects:

In [2]: secretman.<TAB>
          display_all_sc()  main()            translate_key()  
          display_menu()    md5()             write_sc_to_db() 
          exists()          new_sc()          xor()            
          get_scs_from_db() specific_sc()     secretman.pyc    
          index             text

At this point, we’re free to try executing various functions, inspecting global variables, etc.

One function sticks out to me: translate_key()

Let’s try running it:

In [2]: secretman.translate_key()
Out[2]: 'clubeh{tH3s3us_sUcK50rz}'

Huh. That was easy.

Fully-automated Solve Script

Because why not — and it’s even a one-liner!

python -c '__builtins__.input = lambda x: "5"; import secretman; print(secretman.translate_key())' | tail -n1

__builtins__.input = lambda x: "5"; overrides the input() function so it always returns “5” (to auto-exit the menu).