0xEBFE

Blog about life.

Ghost in the Shellcode 2015: Pirate’s Treasure 500 write-up

This task is a standard keygenme. Solving it would get you 500 points, which is a bit high for the difficulity.

The task is located inside the game Pwn Adventure 3. At some point, you’ll get to the ship with a chest. To open this chest you have to enter DLC key:

All game logic is located in GameLogic.dll and conveniently there is also the .pdb file with symbols. It’s easy to locate the DLC key checking routine:

This routine takes entered DLC key as argument and return 1 if it passes all checks. Here is the list of the first checks:

  • The length of DLC key should be more than or equals to 25 chars
  • The key should use characters from this alphabet “0123456789ABCDEFHJKLMNPQRTUVWXYZ”
  • The last character of the DLC key is a error control character (check sum)

The lenght of the alphabet gives us a hint that it might be BASE32 with non-standard characters set. However, further debugging revealed that in this case a custom BASE32 encoding was used.

Here’s how a standard BASE32 algorithm looks like:

standard BASE32 scheme
1
2
3
4
5
+--first octet--+-second octet--+--third octet--+--fourth octet-+--fifth octet--+
|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
+---------+-----+---+---------+-+-------+-------+-+-------------+-----+---------+
|4 3 2 1 0|4 3 2 1 0|4 3 2 1 0|4 3 2 1 0|4 3 2 1 0|4 3 2 1 0|4 3 2 1 0|4 3 2 1 0|
+-1.index-+-2.index-+-3.index-+-4.index-+-5.index-+-6.index-+-7.index-+-8.index-+

The VerifyKey-routine uses the following scheme:

non-standard BASE32 scheme
1
2
3
4
5
+--first octet--+-second octet--+--third octet--+--fourth octet-+--fifth octet--+
|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
+-----+---------+-+---------+---+-------+-------+-------------+-+---------+-----+
|2 1 0|4 3 2 1 0|0|4 3 2 1 0|4 3|3 2 1 0|4 3 2 1|1 0|4 3 2 1 0|4|4 3 2 1 0|4 3 2|
+2indx+-1.index-+4+-3.index-+-2-+5.index+4.index+-7-+-6.index-+5+-8.index-+7indx+

This was the hardest part in this task :)

Afterwards a function with obfuscated symbol name decrypts the custom BASE32-decoded bytes:

The function takes two big numbers 0x10001 and 0x3C9921AC0185B3AAAE37E1B as arguments and decrypts the buffer. It is obviously a RSA decryption routine. N module is 90 bits only, so it can be factored in seconds.

After RSA decryption, the routine checks if the decrypted array contains the string “PWNADV3”. Also, the routine contains a check for the first DWORD of the decrypted array. It takes the last 4 bytes from the BASE32-decoded bytearray and performs an XOR-operation with a value of 0xAEB7037B and shifts it 2 bits to the right. The result of this expression should be equal to the first DWORD after RSA decryption.

If you’re interested, you can consult the source code of the keygen here

Now, let’s enter the generated DLC number from the keygen, for example: 6R87D-Y0AVZ-NA3X5-ME2DK-NUA0W

Then we will get the solution to this challenge: