29 Jan

Earlier this month, my friend Washi invited me to take a part in a small CTF competition. It was a first time his team was making something like that, so I did not know what to expect. I must say that RTN CTF was a great success and I really really enjoyed it.

Challenges and official solutions have already been published on RTN Github and few people have already made great and detailed writeups:
https://github.com/CodeOfDark/CTF-Writeups/tree/master/RTN/2021
https://holly-hacker.github.io/ctf-writeups/2021-01_RTNCTF/Index.html
https://zsr2531.github.io/writeups/RTN_2021/

I have no intention of repeating what's already been said and done, so I'll just add a few personal notes about how I solved the most interesting challenges. smile

01 Aug 2016

In part 1 of the tutorial, I explained how badly initialized PRNG causes a serious problems and allows us to find the private key. In this part of tutorial, I'll show how to use another weakness in crackme to find private key without using bruteforce.

### Looking deeper in the keygenme

If you analyze serial check in more details, you'll notice part of code that processes blacklisted names and keys:

It looks like we have 2 blacklisted names & corresponding serials. We can quickly patch crackme and verify that these names and serials really work.

But did you also notice that those serials look a lot alike? That's weird.. Let's take a closer look at them:

First part of the serial is exactly the same! Let's go back to ElGamal basics and think a bit.

### Revisiting ElGamal signing algorithm

I'm sure you already remember the algorithm from my previous blog post. But here it is again.. smile

To sign a message M, one would:

• Make a hash of message, H(M). In this crackme, it's SHA1 of the username
• Generate a random number K where K<P-1
• Calculate R=G^K (mod P)
• Calculate S=(H(M)-RX)*K^(-1) (mod P-1)
• The signature will be the pair C(R,S).

So, in our case, R is always the same. G and P are hardcoded in the crackme. That means.. K is always the same. And that doesn't sound right! smile

Quick look in Wikipedia confirms that it's a really bad idea:

The signer must be careful to choose a different k uniformly at random for each signature and to be certain that k, or even partial information about k, is not leaked. Otherwise, an attacker may be able to deduce the secret key x with reduced difficulty, perhaps enough to allow a practical attack. In particular, if two messages are sent using the same value of k and the same key, then an attacker can compute x directly.

The problem of reusing k and the attack itself is explained in Stinson's "Cryptography Theory And Practice", pages 290-291. Unfortunately, normal person has no chance to understand that "explanation".

Few Google searches later I found 2 writeups from Boston Key Party CTF 2015 which were slightly better:

So, let's try to get something useful out of them.

### Reused K and recovering private key

As I said earlier, cryptography is a dark magic - if you don't spend years studying it, you can't understand it. So, I just took the code from those CTF writeups and added few more comments to it. And no, I still don't know why it works. smile

### Conclusion

Wise man once said:

Knowledge is of two kinds. We know a subject ourselves, or we know where we can find information upon it.

You don't need to be a crypto wizard to solve crackmes - you just need to know where to find the necessary information. But if you're implementing cryptography in your software, better ask someone who understands those things.

Have fun!

22 Jul 2016

Some weeks ago I found a nice keygenme on URET forum. The description looked interesting enough:

Yet another company is making wild claims! Your mission: prove that people shouldn't trust companies promoting "revolutionary" crypto algos. Keygen this son of a crypto nightmare and write a DETAILED tutorial!

Rules:
1) The only acceptable solution is a keygen
2) No patching of course

It was not solved for few weeks, so I decided to take a look at it. smile

### Crash-course in ElGamal signature scheme

I hate cryptography. It's complex, it's confusing and unless you're prepared to study this field for years, you can't really understand why stuff works this or that way.

So, here's a short version, just enough to solve this keygenme. It's based on the explanation in InfoSec Institute's tutorial.

#### Key generation

• Generate a random prime number P with chosen length.
• Generate two random numbers, G and X, with G<P and X<P.
• Calculate Y=G^X mod P.
• Public key consists of 3 numbers: (P, G and Y), Private key is X.

All 3 numbers of public key are hardcoded in keygenme and are 256 bits long. We can find them in disassembly:

Private key X is.. well, private. smile Without it we can't generate correct keys for a name of our choice. So, the challenge would be to recover the private key somehow.

#### Signing

To sign a message M, one would:

• Make a hash of message, H(M). In this crackme, it's SHA1 of the username
• Generate a random number K where K<P-1
• Calculate R=G^K (mod P)
• Calculate S=(H(M)-RX)*K^(-1) (mod P-1)
• The signature will be the pair C(R,S).

#### Verification

To verify a given pair C(R,S), one would:

• Compute V1=G^M (mod P)
• Compute V2=Y^R * R^S (mod P)
• If V1==V2, the signature is valid

### Breaking ElGamal

Strength of ElGamal algorithm lies in the Discrete Logarithm Problem (DLP). What it means is that easy to calculate Y=G^X mod P, but it's bloody hard to find out X, if you know P, G and Y.

In the InfoSec Institute's tutorial the author is using figugegl's DLPTool to solve the problem for 128-bit integers. However, it's not even possible to enter 256-bit integers in that tool. And the rest of the suggested tools can't handle such large integers either.

So, we must find another way to break the keygenme. After all, it's called "B0rken ElGamal", so there must be a weakness somewhere!

### Inside the keygenme

The keygenme itself is an application that generates ElGamal keys. It would be logical to assume that SmilingWolf used this same application to generate keys for the keygenme. So, let's examine the application and see how ElGamal is implemented in it.

I'll skip the boring "unpack modified UPX part", as it's been explained dozens of times already.

So, here's the relevant code for key generation:

Looks legit, right? smile Well, not so fast.

Random numbers don't just magically appear out of thin air. They are generated using random number generators. If the generators are flawed, the numbers are not really random. You can read a lot about such attacks on Wikipedia.

So, let's examine random number generator used in this keygenme.

If you search for those constants, you'll see that it's a very standard PRNG which is not great but supposed to be reliable enough. You could bruteforce all 2^32 possibilities but it will take a long time. Another dead end?

Well, not really. Any PRNG must be initialized somehow, see random_seed variable above. So, let's see how SmilingWolf is initializing his PRNG..

Hmm, that's weird. Normally PRNG is initialized using rdtsc instruction or something even less predictable than that. And what exactly is arg_0? It's a handle of the DialogBox - not random at all!

Finally, we've found a reason why this ElGamal implementation is broken! smile

### Bruteforcing the private key

Now that we know the weakness, we can write a bruteforcer that will go through all possibilities of random seeds and generate all possible ElGamal keys. Once we generate a key that has the same P, G and Y as in the keygenme, we will also know the correct private key X. But generating all these numbers is a slow process!

Let's look back at the code and see what we can optimize.

1) We don't need to generate all the numbers. It's enough if we find correct P - the rest of numbers will match automatically. So, let's remove the rest of the code.

2) First 32 bits of 256 bit integer will almost certainly not change when searching for nearest prime. Therefore, we could get rid of bigint_find_nearest_prime call which is the slowest piece of code. This means we won't be looking for number FE6D5B4400B30374A403F88CFBA3642435FB269AEC2BE5C8C2F331545EF37AB3, but any number starting with FE6D5B44...

3) Multiplication by 2. Another unnecessary step. Let's just divide P by 2 and look for that number. Instead of looking for FE6D5B44..., we'll look for 7F36ADA2...

4) Copying bigints. Unnecessary.

There's not much left, I'm satisfied.

Now, how to bruteforce random seed? It's a window handle. Window handles are always even. Also, they consist of 2 parts, high word and low word. Low word is the actual handle. It is almost never higher than 0x2000. High word is the "uniquifier" - just a counter. It's usually quite small - on my PC it's never larger than 0x800.

Taking all these assumptions into account, my pseudo-code for bruteforcer would look like this:

I implemented the bruteforcer in MASM32 with 95% of code ripped from keygenme and let it run. In less than 2 hours I had one (of the several possible) seed:

Once we know X, implementing keygen is a child's play. Again, lots of ripped code and small UI around it. Problem solved!

InfoSec Institute's tutorial about ElGamal: http://resources.infosecinstitute.com/breaking-software-protection-elgamal-signature-scheme/
Interactive webpage showing how ElGamal works with small numbers: https://asecuritysite.com/encryption/elgamal

### Final thoughts

As SmilingWolf told me after solving his keygenme - there is another way how to break this keygenme. There's one more thing broken in this implementation that makes generating keys really easy. smile So, if you're interested, try to figure out what is it and how to abuse it.

02 Nov 2015

Yesterday Extreme Coders posted a small crackme on Tuts4You. It's quite an easy one but solving it would require either lots of typing or some clever automation. Of course, being lazy I went for the automation route! smile

### Initial analysis

My preferred way is doing static analysis in IDA and - when necessary do dynamic analysis using OllyDbg. So, here is how it looks like in IDA: As you can see, parts of code have been encrypted. 102 parts of code, to be exact. smile

### Decrypt the code

Since there is a lot of code that's encrypted, I need to automate decryption somehow. IDA scripting to the rescue!

There's not much to comment. There's a big loop that's looking for the pattern of the decryption code. Then it extracts information about encrypted code address, size and used encryption key. Finally it decrypts the code.

Note - when you're patching binary data in IDA, it's always better to force IDA to reanalyze the affected fragment. I didn't do that here because changing end of _main() will force analysis automatically.

After decryption the code looks much better: Well, it's better, but it still kinda sucks. We have 100 checks like this:

So, we're solving system of 100 linear equations with 32 variables. Great! Who wants to write down these equations based on disassembly and then solve them manually? Not me!

### Decompile the code

Let's see if we can somehow make the problem easier for us. Hexrays decompiler provides nice output but it still needs a lot of cleanup: Basically, the code responsible for encryption/decryption of checks is getting into our way.

Another IDA script to the rescue:

I took the previous script and modified it a bit. Now it finds both encryption and decryption loops and just nops them out. It also forces IDA to reanalyze the patched region - it's very important because otherwise IDA lost track of correct stack pointer and decompiled code was wrong.

Quick changes in Hexrays plugin options to use decimal radix and the decompiled code looks great! ### Text editor magic

Beginning reversers commonly underestimate power of text editors. Sure, the Hexrays output we got is readable, but it's not really suitable for any sort of automated processing.

First, let's get rid of all extra spaces. Replace " " (2 spaces) with " " (one space). Repeat until no more matches. Now it looks like this:

Put each equation on one line. Replace "\r\n +" (new line,space,plus) with " +" (space,plus). Replace "\r\n *" (new line,space,star) with " *" (space,star).

Get rid of those "if". Get rid of "++v6;". Replace "==" with "=".

Finally, rename "enteredString" to "z" and get rid of those "[" and "]"

Congratulations, within one minute you got from ugly decompiled code to well-written system of equations!

### And solve the problem

Nicely written system of equations is pointless, if you can't solve it. Luckily, there's an online solver for that right there! wink Copy-pasting our cleaned system of equations into their webform gives us result:

This system has a unique solution, which is

{ z0 = 102, z1 = 108, z10 = 48, z11 = 108, z12 = 118, z13 = 101, z14 = 100, z15 = 95, z16 = 116, z17 = 104, z18 = 97, z19 = 116, z2 = 97, z20 = 95, z21 = 114, z22 = 49, z23 = 103, z24 = 104, z25 = 116, z26 = 33, z27 = 33, z28 = 33, z29 = 125, z3 = 103, z4 = 123, z5 = 89, z6 = 48, z7 = 117, z8 = 95, z9 = 115 }.

Converting character codes to ANSI string is an equally simple exercice, I'm not gonna bore you with that.

And that's how you solve a crackme with nothing but a few scripts in IDA and a text editor.. wink