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
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
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
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
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
After this, I run the program a couple times to verify that it accepts any password.

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