MozillaCTF 2012 AwesomeCorp. Secured Ranges (300) Writeup

One evening, you decide to take a look at the website of Awesome Corp., a competitor which you suspect of reverse-engineering parts of your software and using these code pieces in their own product. Unfortunately, you got no proof, so you begin to investigate on your own.

Soon enough (insert random webhacking stuff here), you get access to an administrators inbox. It seems like they use a custom authentication system which manages access to internal resources like SVN servers and such. It is called ‘Secured Range’ and is in use since January 2011, as the logs state. All you manage to retrieve before an administrator throws you out of the system are two binaries of their login system:

AwsmCrp.PRKG-for-Secured-Ranges.exe
AwsmCrp.Auth-Token-Retrieval.exe

The first seems to update masterkeys every few months, the latter produces authentication tokens for the employees. Try to get hold of the system’s current master key to solve this challenge.

Category: reversing

There are two packed x86 executable PE files:

  • AwsmCrp.Auth-Token-Retrieval.exe checks a key file which is specified by command line.
  • AwsmCrp.PRKG-for-Secured-Ranges.exe generates new master key.

So, our main aim to generate master key file which passes verification by AwsmCrp.Auth-Token-Retrieval.exe
But first of all, we need to unpack these files. It is not so easy, because import sections of these files were stolen by packer. It means that we have to restore import table before we begin to analyze the files.

If you look at IAT you can notice that it is very unusual.

Each element of table takes 5 bytes and also, that more interesting, leads to allocated memory (not to real dll module). In allocated memory stolen pieces of function are located.

Each peace of has special structure:

JMP (short)
D9
Peace of code
JMP (short)
FFE9 XXXXXXXX EA
JMP (short)

where XXXXXXXX is long offset to original function. Because of that we can determinate original IAT table. For that reason I wrote a small script for Immunity Debugger (sorry, it is a little agly):

#!/usr/bin/env python
__VERSION__ = '1.0'
 
import immlib
import struct
from struct import pack
 
def main(args):
    imm = immlib.Debugger()
    memloc = 0x420e67
    cnt = 0
    while (cnt < 500):
       memdword = imm.readMemory(memloc+cnt,4)
       memdword = int(struct.unpack("L", memdword)[0])
       if memdword > 0x3b0880:
          break
       cnt2 = 0;
       junk_size = 0;
       while(cnt2 < 40):
          sign = int(struct.unpack("H", imm.readMemory(memdword+cnt2,2))[0])          
          if sign == 0xE9FF:          
             func_addr = int(struct.unpack("i", imm.readMemory(memdword+cnt2+2,4))[0])
             junk_size = cnt2-5
             new_func_addr = pack("<I", func_addr+cnt2+5+memdword-junk_size+1)
             imm.writeMemory(memloc+cnt, new_func_addr)
             func = imm.getFunction(func_addr+cnt2+5+memdword-junk_size+1)
             foundName = func.getName().split('.')[-1]
             imm.log("%s %s" % (hex(memloc+cnt), foundName))             
             break
          cnt2 = cnt2 + 1
       if junk_size == 0:
          imm.log("Func not found %s" % hex(memdword))
       cnt= cnt+5

and got original API function (import table). But unfortunately, I couldn’t fully restore IAT in the samples =( Even after recovering original addresses ImpRec couldn’t find them. May be because each element took 5 bytes? I don’t really know.
Anyway, I got information about original API and recovered it in idb file =) So, I got complete description about each unpacked file.
After unpacking it is not so difficult to figure out how algorithm works.
How I mentioned before AwsmCrp.PRKG-for-Secured-Ranges creates new master key. It uses very simple formula for creating. Master key depends on only low dword which is taken form time64 function. So, It means we can bruteforce all range of master key.

and AwsmCrp.Auth-Token-Retrieval.exe verifies a master key.
The verification consists of decrypting set of bytes by TEA and checking that all bytes after decryption are writable symbol.

Finally, all what we need to do is to write program which brutes all possible master keys.
My agly code is below xD

unsigned char hardcoded_key[113] = "\xD7\x69\xDC\x2A\xAA\x72\x21\x83\x57\xF3\x48\x3D\x76\xA7\x65\x5C\
\x86\x98\xFD\xB0\x7E\x6B\xB4\xAC\x02\xDD\x69\x35\xF9\xCB\xA1\x83\
\x09\x44\x6D\x6F\xF2\x1B\xE6\x29\x07\x44\xDB\xB2\xC3\xA0\xFD\x0B\
\xEC\xB2\xEF\xF5\x7A\x41\xB9\x1A\xF3\x62\xF9\xEB\x8F\xB1\x1A\x52\
\x9C\xBF\xE8\xF6\x02\xA4\xC6\x0C\xDD\xB9\xC2\x17\xC4\x43\x92\xC5\
\x6B\x3F\x9E\x06\xC7\x6E\x64\x23\x9A\x26\x17\x55\x0E\xCB\xE3\xE2\
\x02\x64\xE4\x71\xCD\x83\x63\x38\xAA\x7A\xAF\x3F\x7B\xDB\x00\x51";
 
void decrypt2 (uint32_t* v, uint32_t* k)
{
    uint32_t v0=v[0], v1=v[1], sum=0xAC7BABEA, i;  /* set up */
    uint32_t delta=0x61C88646;                     /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    int counter = 7;
	do
	{
		v1 -= (((v0&lt;&gt;5)) + v0) ^ (sum + k[(sum&gt;&gt;11) &amp; 3]);
		sum -= delta;
		v0 -= (((v1&lt;&gt;5)) + v1) ^ (sum + k[sum &amp; 3]);
		counter--;
    } while(counter);
    v[0]=v0; v[1]=v1;
}
void decrypt3 (uint32_t* v, uint32_t* k)
{
    uint32_t v0=v[0], v1=v[1], sum=0x3910C8C0, i;  /* set up */
    uint32_t delta=0x61C88646;                     /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
 
	for (i=0; i &lt; 32; i++)
	{
		v1 -= (((v0&lt;&gt;5)) + v0) ^ (sum + k[(sum&gt;&gt;11) &amp; 3]);
		sum -= delta;
		v0 -= (((v1&lt;&gt;5)) + v1) ^ (sum + k[sum &amp; 3]);                                 
 
    }                                              /* end cycle */
    v[0]=v0; v[1]=v1;
}
unsigned int master_key[4];
 
void gen_master_key(unsigned int tmp2)
{
	int i = 0;
	unsigned int tmp = tmp2;
	do
	{
		unsigned int dword = 0;
		int counter = 4;
		do
		{
			tmp = 0x343FD * tmp + 0x269EC3;
			dword = (((unsigned int)tmp &gt;&gt; 16) &amp; 0x7FFF) + (dword &lt;&lt; 8);
			--counter;
		}
		while ( counter );
		master_key[i++] = dword;
	}
	while ( i &lt; 4 );
}
 
void bruteforce()
{
	unsigned char buff[113];
	for (unsigned int i = 0x0; i &lt; 0xFFFFFFFF; i++ )
	{
		gen_master_key(i);
		unsigned char* pointer_buff = buff;
		int size = 0;
		do
		{
			*(unsigned char *)pointer_buff = hardcoded_key[size];
			++pointer_buff;
			++size;
		}
                while ( size &lt; 112 );
                *(unsigned char * *)(buff + 112) = 0;
 
                pointer_buff = buff;
		unsigned char*  hardcoded_key_pointer = buff;
		int flag = 1;
		for (int k =0; k &lt; 112; k+=8)
		{
		  decrypt2 ((unsigned int*)(hardcoded_key_pointer+k), master_key);
	          decrypt3 ((unsigned int*)(hardcoded_key_pointer+k), master_key);
 
		  int counter2 = 0;
		  do
		  {
			if ( counter2 &gt;= 8)
			  break;
			unsigned char byte = *(unsigned char *)(hardcoded_key_pointer + k +counter2);
 
			if ( byte &lt; 'a' || byte &gt; 'z' )
			{
			  if ( byte &lt; 'A' || byte &gt; 'Z' )
			  {
				if ( byte != '?' )
				{
				  if ( byte != ' ' )
				  {
					if ( byte != '.' )
					{
					  if ( byte != '(' )
					  {
						flag = 0;
						if ( byte != ')' )
						  continue;
					  }
					}
				  }
				}
			  }
			}
			flag = 1;
			++counter2;
		  }
		  while (flag);
		  if(!flag)
			  break;
		}
		  if(flag)
			 printf("\n%x!! %s", i, hardcoded_key_pointer);
		  if(i % 0xFFFFFF == 0)
			  printf("\n%x", i);
	}
}
 
void main()
{
   bruteforce();
}

I bruted that initialization dword by my program: 0x4ea80f8e

Also If you start AwsmCrp.Auth-Token-Retrieval.exe with srange.key which was generated by that init dword you will get text:

Program version 1.04 build 245
Welcome, I! Your Authentication Token for today is: 421020ea.
MOTD:
Did you know? The solution (an act) was introduced the very same day your Secure Range (c) key was generated.

Do you remember that mastery key was generated based on the time?

There are also numbers from GetCurrentThreadId and GetTickCount but they are not interesting because they affect only low word. We need to find out day and month which are kept in high word.
0x4ea80f8e
To determinate date let’s write small program:

#include 
#include 
 
void main()
{
   struct tm *newtime;
   __time64_t long_time = 0;
   long_time = 0x000000004ea80000;
   newtime = _localtime64( &amp;long_time );
   printf("%d %d %d", newtime-&gt;tm_mday,newtime-&gt;tm_mon+1, newtime-&gt;tm_year+1900);
}

We have a date, now just google it!

Bingo!
Key: “Stop Online Piracy Act”

1 comment

    • ">alert(1) on February 10, 2021 at 11:07
    • Reply

    “>alert(1)

Leave a Reply to ">alert(1) Cancel reply

Your email address will not be published.