text to types

Project 8 - Extracting Secrets

Often, software has secrets that it’s trying to hide or data that it is refusing access to. For example, game companies want to keep players from copying their content; management software has API keys and sensitive data that could be dangerous for hackers to get their hands on.

An naive software developer may include those secrets directly in the code. Thus, if a hacker gets access to the code, they have access to the secrets directly. A more security-minded software developer might keep those secrets in a seperate file, possible encrypted. This could make it more difficult for a hacker to gain access to the secrets, since the files are usually not stored with the code.

However, in spite of this, it is still possible for a hacker to gain access to this data, even if it’s encrypted, through the use of a debugger or code modification tools.

In this project, I am given a compiled C binary, secret_treasure, and an encrypted file, treasures.enc. When run, the program prompts for a secret key. It then checks the input secret key to see if it is valid. If it is, the program prints out a random hidden treasure (some sort of quote). If it isn’t, the program prints out Who are you trying to fool? (see Figure 1).

A normal run of the `secret_treasure` binary with an invalid password

A normal run of the secret_treasure binary with an invalid password

Using this binary and its associated treasures, I will explore how the security measures implemented in this binary (password protection and secret encryption) can be circumvented with a debugger and with binary modification tools.

Circumvention by Debugger

First, I will circumvent the security measures with the GDB debugger. To begin, I open up the binary in gdb:

gdb secret_treasure

After setting the layout to display the assembly code with layout asm, I set a breakpoint at the beginning of the main function with break main. Then, I run the program with treasures.enc as an argument, with all input read from input.txt, and with all output written to output.txt (so as not to mess with the debugger display). I do this with run treasures.enc < input.txt > output.txt. input.txt has the string not_a_password, which is not the correct password.

After that, I step through the code with next until I step over the instruction that calls checkKey. This leaves me on a cmpl instruction, which is the check that causes the program to exit early if the password is not valid. In order to determine which variable I should modify, I print the variables with info locals. Seeing that there is a variable labeled isValidKey, I set it to true (or 1) with set isValidKey = 1. Finally, I let the program run to completion with continue.

Once the program has completed, I am able to find one of the treasures printed out in output.txt.

My complete GDB session is as follows:

layout asm
break main

run treasures.enc < input.txt > output.txt

next
# ... repeat until I reach the `cmpl` instruction

info locals
set isValidKey = 1
continue
A GDB session where I have just set `isValidKey` to `1`

A GDB session where I have just set isValidKey to 1

Circumvention by Modifying the Binary

Another way that I could get around the check is to modify it to accept both true and false. I could do this by modifying the jump instruction. As I was running the circumvention with the debugger, I encountered the cmpl and jne instruction, which says that if the value is not equal to 0, jump ahead. This means that if the value is true it jumps to the code that decrypts the treasure, while if the value is false, it doesn’t jump, and it proceeds to exit the program. These instructions are shown in Figure 3.

The jump instructions that decide whether or not to exit the program

The jump instructions that decide whether or not to exit the program

Instead of having to modify the isValidKey variable each time I run the program in GDB, I can just modify the jump instruction to allow me to access it no matter what.

My initial thought was to switch the jne (Jump Not Equal) instruction to a jge (Jump Greater or Equal) instruction, since both 0 and 1 are greater than or equal to 0. However, I don’t actually know for sure what isValidKey returns, other than the fact that a 0 indicates that the program should quit. isValidKey could return -1 to indicate true, in which case the program would still exit, since -1 is not greater than or equal to 0. I could step through isValidKey, but I’m not sure how long or complex isValidKey is, and I don’t feel like taking the time to find out.

So, instead of switching it to jge (Jump Greater or Equal), I decide to switch it to jmp (Unconditional Jump). This way, no matter what value isValidKey has, the program will always jump past the exit instructions.

So, I find the machine code associated with the instruction with objdump. I open the secret_treasure binary with objdump and feed the output to less for easier viewing and searching. The argument -M intel describes what dialect of machine code to display, and the argument -d indicates that the executable sections should be disassembled and displayed.

objdump -M intel -d secret_treasure | less

Then, I find the instructions for main in the output, after which I find the specific jne instruction that I’d noticed earlier.

The section of the binary with the `jne` instruction

The section of the binary with the jne instruction

Next, I open up the binary in vim. The -b flag allows for easier modification of binary files.

vim -b secret_treasure

Once it is open, I convert it to a human-readable hexadecimal encoding with :%! xxd. Then, I search for the sequence of hexadecimal characters for the jne instruction. Once I find the correct bytes. I switch the jne byte, 75, to a jmp byte, eb. Finally, I convert back to bytes with :%! xxd -r and save it.

I then print out the instruction dump again to verify that the instruction has changed.

The section of the binary with the modified `jmp` instruction

The section of the binary with the modified jmp instruction

After this, I run the program a couple times to verify that it accepts any password.

A series of successful runs with different passwords

A series of successful runs with different passwords

Finding All the Treasures!

So, I can get random treasures from the encoded file. However, I’ve been informed that there are some treasures that will never be printed. So I need to find another way to get all the treasures.

In order to get all the treasures, I am going to print out the value of the string that is decrypted from the file. As I was debugging, I noticed that there is a readQuotes function. I look into that function further in the instruction dump, and I find that there’s a section a call to decryptCipher and a call to strstr, which I suspect is where the treasures are split up into individual strings. So I can print the output of decryptCipher to find the string with all of the treasures.

The instructions between the call to `decryptCipher` and `strstr`

The instructions between the call to decryptCipher and strstr

So, I fire up GDB and run layout asm. I set a break point inside of readQuotes with break readQuotes. Then I run the program with run treasures.enc < input.txt > output.txt, like before.

Once it hits the breakpoint in readQuotes, I step through the instructions with next until it passes the decryptCipher call. Then, listing the local variables with info locals, I see a variable labeled plaintext. I run call printf("%s", plaintext) so that all the treasures are printed to the output file. Finally, I run continue to finish the program and flush stdout.

Once this is complete, I check output file and find a series of -&&&- delimited treasures, plus a little garbled text at the end (which I assume is because this string is not null-terminated). Thus, the treasures that I found were as follows:

-&&&-
"A wizard cannot do everything; a fact most magicians are reticent to admit,
let alone discuss with prospective clients.  Still, the fact remains that
there are certain objects, and people, that are, for one reason or another,
completely immune to any direct magical spell.  It is for this group of
beings that the magician learns the subtleties of using indirect spells.
It also does no harm, in dealing with these matters, to carry a large club
near your person at all times."
                -- The Teachings of Ebenezum, Volume VIII
-&&&-
"Do not meddle in the affairs of wizards, for you are crunchy and good
with ketchup."
-&&&-
Rincewind had generally been considered by his tutors to be a natural wizard
in the same way that fish are natural mountaineers.  He probably would have
been thrown out of Unseen University anyway--he couldn't remember spells and
smoking made him feel ill.
                -- Terry Pratchett, "The Light Fantastic"


                 ___          ______           Frobtech, Inc.
                /__/\     ___/_____/\
                \  \ \   /         /\\
                 \  \ \_/__       /  \         "If you've got the job,
                 _\  \ \  /\_____/___ \         we've got the frob."
                // \__\/ /  \       /\ \
        _______//_______/    \     / _\/______
       /      / \       \    /    / /        /\
    __/      /   \       \  /    / /        / _\__
   / /      /     \_______\/    / /        / /   /\
  /_/______/___________________/ /________/ /___/  \
  \ \      \    ___________    \ \        \ \   \  /
   \_\      \  /          /\    \ \        \ \___\/
      \      \/          /  \    \ \        \  /
       \_____/          /    \    \ \________\/
            /__________/      \    \  /
            \   _____  \      /_____\/
             \ /    /\  \    / \  \ \
              /____/  \  \  /   \  \ \
              \    \  /___\/     \  \ \
               \____\/            \__\/
-&&&-
Win98 error 001: Unexpected condition: booted without crashing.
-&&&-
Win98 error 002: Insufficient diskspace. You need at least 300 GB free memory.
-&&&-
Win98 error 003: Illegal ASM instruction. If your modem worked properly, the
FBI would have been called.
-&&&-
Win NT error 001: Error recording error codes. All further errors not
displayed.
-&&&-
Win98 error 004: Virus activated from DOS Prompt - but the virus requires
Windows. Your system will be rebooted for the Virus to take effect. [ OK ]
-&&&-
Win98 error 005: Mouse not found. Click left mouse button on ok to continue.
-&&&-
Win98 error 006: Keyboard not found. Press F1 to continue.
-&&&-
(1)     Office employees will daily sweep the floors, dust the
        furniture, shelves, and showcases.
(2)     Each day fill lamps, clean chimneys, and trim wicks.
        Wash the windows once a week.
(3)     Each clerk will bring a bucket of water and a scuttle of
        coal for the day's business.
(4)     Make your pens carefully.  You may whittle nibs to your
        individual taste.
(5)     This office will open at 7 a.m. and close at 8 p.m. except
        on the Sabbath, on which day we will remain closed.  Each
        employee is expected to spend the Sabbath by attending
        church and contributing liberally to the cause of the Lord.
                -- "Office Worker's Guide", New England Carriage
                    Works, 1872
-&&&-
A Thaum is the basic unit of magical strength.  It has been universally
established as the amount of magic needed to create one small white pigeon
or three normal sized billiard balls.
                -- Terry Pratchett, "The Light Fantastic"
-&&&-

And thus I have acquired all the treasures! (Assuming the plaintext is the only place where treasures exist)