Analysis of TimeLock and Vulnerability Writeup

I have something exciting to share today, my first bug bounty! Hopefully the start of something great. It even came with a generous reward, in my favourite crypto currency, Bitcoin.

I was browsing Reddit as you do, and came across this post on r/bitcoin.

post

u/cryptocomicon has developed some encryption software which can be used to safely store secrets and release them to the world sometime in the future. The software can encrypt and store arbitrary files, and release them to trusted third parties when you are no longer around. Sounds good for passing on crypto currency wallets and passwords.

u/cryptocomicon issued a challenge:

“I’m so confident in this technology that I’ve created a challenge LockBox file which holds the private key to an address with 0.02 BTC.

Please give it a try.”

Challenge accepted.

Reconnaissance

If we have a look at the website at algomachines.com

website

Under technical information, some things become apparent:

Project files are encrypted using a hash of the project password and the contents of a random file generated on install.

LockBox files contain encrypted:

  • Questions
  • Delay and TimeLock information
  • Data file

Already we can speculate that the encryption:

  • Is based on the password
  • Is possibly based on the hash of answers to questions
  • Is based on a random file generated on install
  • Is probably not based around what time it actually is!

Another interesting thing is this:

CPU clock is continuously checked against BTC Network time.

Interesting. I think it’s a great idea. You can’t trust the system clock, as that is easy to change, and if you aren’t careful, an attacker will be able to spoof or redirect any attempts to use unauthenticated NTP within your application. Bitcoin will be around for a long time yet, and each server is capable of returning a UNIX timestamp.

We will just have to see how its implemented, that’s all.

Head over to the download page, where you can get a copy of the software and the challenge lockbox.

download

Here are the files:

TimeLock_v1_0.msi SHA256 a0d0d2391194eb921472cd6dd26ddcbfb9f5d2219448cf6b59db0983d178d568 Challenge1.x SHA256 513278e94bf7b6ebd948ddca798abf2fdd7db823f3d91292bb5b4fd04792821f

Boot up a Windows 10 VM, install the software, and lets have a peek under the hood. Note, your VM needs more than 2048mb of memory or the installer fails. Adjust to 2100mb or more. Caught me out, since I only have 4gb ram on my laptop.

When you first open TimeLock, you will see something like this:

TimeLock Control Panel

At the bottom of the window, the program will display how many connections it has to nodes on the BTC network. The “Open a LockBox” prompt becomes available only when 8 nodes have been connected to. Then, the status updates to “Connected to BTC Network”.

I actually spent a lot of time messing around with network disconnected, and tried to patch the program to remove the requirement. It was actually quite confusing, and I gave up, and connected to a network.

If you click “Create or Edit a LockBox”, we see a window similar to this:

Creation

We can see the fields which we can specify:

  • A name
  • A file to be encrypted
  • A password
  • Questions and answers
  • A time delay after asking questions
  • The most important part: defining the time window where the encrypted file may be accessed, and disallowed otherwise.

From what we read on the website, this time data is stored alongside the questions and the secret file. It is highly likely that a set of if statements are enforcing this behaviour. Only one way to find out.

Analysis

Open up IDA Pro, and since I am a poor student, I am using 7.0 free edition. Cutter will also work just as well, and maybe I should have used that instead.

Load up TimeLock.exe and wait for the analysis to complete.

IDA

Just look at those functions. LOOK AT THEM! 11,995 functions!

functions

Man, high level languages these days. Why must the path of being a reverse engineer be so hard. That means we will not be examining the contents of functions willy nilly, we need to be smarter than that.

Lets look at some strings.

strings

“Incorrect answers”, “This data is no longer available”, “Reveal file %s in Windows Explorer?”, and “%ld days %ld hours %ld sec until data is available” all look interesting. Let’s try and see if we can trigger any of these when trying to decrypt the challenge.

Head back to TimeLock. Click “Open a LockBox”.

Open the file Challenge1.x. You will get a password window:

password window

On the Download page of the website, the password was said to be “TimeLock”. Enter “TimeLock”.

It seems the answer is correct, and we have the next prompt:

question

It is asking for the reward value. Again, the website says 0.02 BTC, so enter “0.02”.

You will then be forced to wait two minutes. We could probably patch this out, but two minutes is not worth the effort.

Finally, we see this:

wait

It wants us to wait another month before we can decrypt the secret. Well now. That just won’t do.

Lets examine the use of the strings “%ld days %ld hours %ld sec until data is available” and “Reveal file %s in Windows Explorer?”. We need to determine if the time is used for decryption or not. If it is not, we will attempt to bypass waiting, either by hard coding the time or overwriting conditional jumps to continue with decryption.

Exploitation

Back to IDA.

Double click the string “%ld days %ld hours %ld sec until data is available” and right click the variable. Click “Jump xref to operand…”

xref

Click OK to head to sub_14000BCD0 at pos +FA5

messages

We seem to have arrived in some error handling code for incorrect times. Lets follow the red arrow upwards and try and find what logic is controlling this. Keep following the logic upward until we see the call to __time64 at sub_14000BCD0 at pos +EFE

time call

The call to __time64 is stored in rax, and for the comparison, the current time must be stored in var_78, since var_78 is being moved into rbx, and then compared to rax with the cmp instruction. The conditional jump, jbe, then decides what path we take. Since I arrived here from the left path, which is red, this must be the false path.

Looking at the true path, or the green path, we see another comparison, this time, var_70 with rbx.

On the false path, we call __time64 again and compare with var_70, with failure leading to an error message below, and true heading to the right. Interesting. The code probably looks something like this:

if (__time64 > earliest_time) {
  if (time < latest_time) {
    continue_right_side();
  } else {
    printf("The data is no longer available);
  }
} else {
  printf("Wait days / hours / seconds");
}

Having a look down the right side, we see a message box with the text “select folder where %s will be created”:

created

and further down a call to fopen() with file mode wb and a call to fwrite().

fwrite

We are in business. It appears that the plaintext file is being written to disk here, which means that we are past all of the decryption logic, and don’t have to worry about those algorithms or hashing routines. Excellent. Nearly there, I promise.

Next, we will patch the time decision logic to always take the true paths, ignoring the result of the cmp instructions.

jbe

Currently, all cmp instructions are examined by jbe instructions, which branches on success, or basically, whatever the result of the cmp is. Branching is to the true path, or the right hand side, where we want to go. What we will do is remove the conditional part of this jump, to always take this path, no matter what.

Click the first jbe at loc_14000CBCC and Edit menu > Patch program > Assemble…

assemble

Change the jbe to jmp and press Ok. The control flow graph has now changed:

graph

Do the same with the jbe at loc_14000CD03 and change that to a jmp too. The resulting control flow graph should look like this:

graph

Now that the time checks have been removed, we need to apply the patches to the input binary.

Edit menu > Patch program > Apply patches to input file…

patching

Make a backup, but you can probably see I am already working on a copy of the binary. You learn this pretty quick when dealing with malware, since they like deleting themselves and its annoying to have to restore the file all the time.

Click OK.

Time to run TimeLock and see if it works. Let it sync up to the BTC network.

Click “Open a LockBox”, select Challenge1.x and follow through with the password of “TimeLock” and answer the question with “0.02”.

Now. This was the longest 120 seconds of my life. Man, I was sitting on the edge of my seat.

success

Yes! Oh man, I’m getting excited. Lets see where this goes…

success

Yes, yes we do want to reveal the file in windows explorer…

success

Here we go, we are right here… come on… lets see a private key…

success

YES YES YES! YES! YES! Oh thank you generous author for the private key! YES! Oh man. You know the book underground? I link it in the books section. They talk about that high you get when you successfully get into a system. Man. Its true. Its pure reward for hard work.

Well, the above probably took me about 30 mins to do. The thing is, reverse engineering isn’t all that glamorous most of the time. You spend a lot of time going down the wrong paths. I spent an hour in x64dbg trying to see if I could modify the time there. That was a no go. I spent a few more hours looking at the encryption and hashing implementations.

Loot

All up, it took me about 4.5 hours to get your private key. I’m still learning, as we all are. Maybe I might get faster in the future.

Anyway. The private key for the challenge reward, 3FJb6fUi6jVnAQtjxTkskT35FRMeh9saW9 is: p2wpkh-p2sh:L1VhgihJF21ovrW2D5VXYuwYaXDjs6rQrNXigdrQ9MkB6mzMW8y2

I quickly stuck this in electrum and transferred the funds into a wallet I control. Thank you very much u/cryptocomicon for allowing me to do this bug bounty on your software.

The secret code is: 90FF12E5-8F68-432F-8518-BEC252CA8076

I hope this writeup is sufficient instructions on how I did this, and is worth 0.02 BTC further.

This can be payable to the address which I swept the initial 0.02 BTC into: 19KuusbvKQrGLDTCGChysGe1fYTkUznXYf

Notification of Vulnerability

I notified the author of TimeLock by posting to the reddit thread.

notification

And the reply:

reply

The secret phrase for the sha256 hash is:

echo -n "Cowsay bitcoin is cool" |  openssl sha256
6bd77c5ccd4103fecdf13b5249743f06e393cd2833480eeab2db103179ed9bf8

This verifies I am in control of u/security_researcher

How To Fix The Vulnerability

The most important part. I’m here to help and offer suggestions. After all, you are paying for this writeup.

Okay. Firstly, I just want to say you have a great application. The encryption is implemented okay, hashing is implemented okay, and the formats that you store your lockboxes in are okay. You have an application that pretty much works, and you can be proud of it.

So the root cause of your problem, is that you are trusting the executable. Attackers can modify and change executables willy nilly, just like I did in this post. Because of this, you cannot trust your application to handle decisions such as what the time actually is.

As you said on Reddit, when someone mentioned going open source, and you expressed concern that someone could simply remove the time checking code and release a new version. That’s exactly what I did, but I didn’t even have access to the source code. You know what the vulnerability is.

So. How do we fix this? Well, as you say, it is a fundamentally hard problem.

The naive ways are pretty much what malware authors do to stop reverse engineers nosing around your code. That is obfuscating strings, messing around with the layout of the program so that nothing makes sense, and maybe even deploying the use of a packer.

Now, I don’t recommend doing any of that. If you do those things, the community will just think that your code is malware and people won’t touch it. That’s bad. You must have put a lot of time and effort into your program.

You can embed certificates inside your program, and when started, it verifies that the program hasn’t been modified by computing a hash of itself, and comparing the result to the signed hash stored in the program through use of a certificate. This will deter beginner reverse engineers such as myself, but if you use your program to protect a wallet with 1 million dollars in it, you bet I will learn to get around it.

The next thing to think about integrating the time into the encryption itself, and I think this is the best way to go about things. Now, this is actually impossible. Encryption has three things you can use. Something you know, like a password. Something you have, like a hardware token. Or something that you are, like a fingerprint.

What you want is still possible. It’s just that you need a trusted third party. I know, you don’t want one. The whole point of your program is to be standalone, and only require a internet connection. But hear me out.

I want you to go and read this answer on stackoverflow.

Read it twice.

Do you see what you need to do now? You need to set up a server, and this server needs to accept encrypted “passwords” and know the time to publish them, or give the password back. Embed a public key into your program.

When the program decrypts, ask the server for the password. If the time is wrong, the server will say no. If the time is right, give the password back.

This will then be pretty much “un-crackable” like you wished. It might take you a while to implement it though.

If this sounds hard, consider re-releasing the app with the time functionality removed, or leave as it as. Simply asking questions is probably enough to deter most people, and murmurhash3 is mostly collision-resistant. Ish.

If you wish to contact me, email me. See my about page.

Good luck.

Matthew Ruffell.