'; document.writeln(my_chunk); } } // --> --> nb_es009.html: Reversing Warp v1 webpage protection
Reversing Warp version 1
How to take advantage of an added feature of a protection scheme
student
Hyper Javascript
26 dec 1999
by Laurent
(always) Courtesy of Fravia's page of reverse engineering
NOT edited
fra_00xx
98xxxx
handle
1100
NA
PC
There is a crack, a crack in everything That's how the light gets in
Rating
(x)Beginner ( )Intermediate ( )Advanced ( )Expert

In this essay I will try to demonstrate that a protection algorithm is always as weak as the weakest part of it. For this purpose we will take advantage of an 'added' feature that is supposed to turn a protection into something more 'user-friendly'.
Reversing Warp version 1
How to take advantage of an added feature of a protection scheme
Written by Laurent


Introduction

Our target, warp (old version, dated 07/10/98), implement a feature that will prevent user to land on a '404 page not found' in case he enter a wrong password. This feature is implemented using what I'll call a password's signature check.
Without this added feature, it would be impossible (beside pure brute force attack) to break this protection algorithm. Indeed, the actual hidden page name depend entirely on the password you enter.

As you may know, the first version of warp is no more available on its author's website. Hopefully, The Seeker had a copy of the whole package somewhere on his hard disk and moreover he could find us some 'real' target still using that version. Thanks Seeker.

Tools required

A C compiler

Target's URL/FTP

http://www.lombardiaonline.com/Mailmax/index.htm : This is a page (found by The Seeker) actually using warp protection.
http://members.xoom.com/_XMCM/JmV/JavaScript/indexx.htm : This is the warp author's website where you will be able to find the new version of warp.
warp071098.zip : This is The Seekers's copy of warp v1 package.

Program History

Version 1.0 : This is the version we will study
Version 2.0 : That's the new, improved, version we may have a look at in a near future

Essay

1. Warp analysis.

Let's first have a look at that warp thing and try to figure out how it works. Below you'll find the whole script of the protection. You'll probably notice that some code could have been shortened (that's a good exercice :)

function warp()
{
  var password = prompt("Enter PASSWORD:", "Have A Nice Day !");
  if (password == null) { alert("Tired already ? :o)");}
  else
  {
     var a = encode (password, 15)             //convert your password into
     page = a.toUpperCase();                   //destination page name
     var newlocation = "null" + page + ".htm"
     var OutString = verify (password, 15)     //compute signature value.
  }
  if (OutString == "10009804" )                //compare signature value
  {
     window.open ( newlocation, "_new",        //signature is correct, open hidden page
       "resizable=1,scrollbars=1,toolbar=0,location=0,menubar=0,status=0"); //open new window
  }
  else                                          //signature is not correct
  {
     // window.location.href = "null.htm"       //load NO ACCESS page
     alert("ACCESS DENIED");                    //or display alert message
  }
}

//this function will compute the signature of a given password
function verify (password, NumVal)              
{
  Ver="6iAbCDdEeFfGgaHphIkjK7BLM3mN1nOlocPQ9qR2rS5sTtUJuVvWwX8xYyZz40 "
  NumVal = parseInt(NumVal)
  var add = 0
  var code = 0
  for (Count=0; Count < password.length; Count++)
  {
     var HelpLetter = password.substring (Count, Count+1)
     var Convert = getPoss(HelpLetter)
     var Num = Convert^NumVal
     if (Num == "0")
     {
        Num = 64
        add += Num
        add *= Num
     }
     else
     {
        add += Num
        add *= Num
     }
  }

  code = eval(add)
  return (code);
}

function getPoss (Letter) {return (Ver.indexOf(Letter));}

This function will return a encoded page name given a password
function encode (password, NumVal)
{
  Ref="6iAbCDdEeFfGgaHphIkjK7BLM3mN1nOlocPQ9qR2rS5sTtUJuVvWwX8xYyZz40"
  NumVal = parseInt(NumVal)
  var encrypt=""
  for (Count=0; Count < password.length; Count++)
  {
   var HelpLetter = password.substring (Count, Count+1)
   var Convert = getChar(HelpLetter)
   var Num = Convert^NumVal
   Num = getVal(Num)
   encrypt += Num
  }
  return (encrypt)
}

function getChar (Letter) {return (Ref.indexOf(Letter));}
function getVal (Val) {return (Ref.substring(Val, Val+1));}
You see that the password you enter serves 2 purposes.

As you'll see, that signature check is the weakest part of the algorithm, cause it is reversible.
So, we will focus our efforts on it.

In the ideal case, the signature function should meet certain requirements :

Uniqueness :

If 2 different passwords have the same signature, as only 1 of them lead to the 'hidden' page, then the signature fail his goal for the second password, because you will land on a 404 page not found.

Let's prove this with an example :
For simplicity, we will assume the followings : Password is made only of numbers. The signature is the sum of each figures of the password and the pagename is made by converting each figure to his equivalent letter in the alphabet (1->A, 2->B ...).
The secret page name is 'ABCDE'. And the signature value should be 15. We can easily find a big amount of 'valid' password regarding the signature check : "12345", "23451", "34512", "555", "339" ... You can see that's a big amount of password with the same signature value, but only one of them lead to the "ABCDE" page. So, we can say that this signature calculation fails his goal because of a lack of 'uniqueness'.

One-way :

As the signature's goal is purely to prevent 404 page and have nothing to do with the actual pagename encryption process, it should not be possible to retrieve valid password(s) from a given signature. Imagine the signature was just the password written in reverse order (what is perfect regarding uniqueness). In that case, no need to break the encryption algorithm, just reverse the order of the valid signature character and you get the right password.


To summarize, a good signature algorithm will have a high uniqueness and a high one-way properties. Note that, from the security point of view, only the one-way property is important. Uniqueness will only prevent to land on 404 page not found.
A last word on this : the actual name of that kind of signature function is one-way hash function. But allow me to use the word signature, as I find it to be more 'meaningfull'.

2. Reversing the signature calculation.

Of course, to break this protection, a first idea could be to just brute force every possible password and keep only those that pass the 'signature' check.
Let's say the password maybe be up to 8 characters, that will make about 648 password to check. That's about 300.1012 passwords. Let's say we can check 1.000.000 password every second that would still request 30.106 seconds = 10 YEARS ... Of course you may be lucky and find the right password quickly. But I don't believe in luck too much, so let's try to figure out another way.

Is it possible, given the know signature, to retrieve the correct password (or passwords, if the uniqueness is not perfect) ?
To answer this question we have to study the way the signature is calculated. This is done in the function Verify.

we can write some pseudo code for it :

 signature = 0;
 Loop for each character of the password
 {
	index = char position in alphabet;
	newindex = index xor constant_value;
	signature = (signature+newindex)*newindex;
 }
We can notice that the xor step can be taken out of the loop and done in its own separate loop. Indeed, we can rewrite the process as follow :
1.  xor every character of the password with a constant value (NumVal);
2.  signature = 0;
    Loop for each character of the new password
    {
	index = char position in alphabet;
	signature = (signature+newindex)*newindex;
    }
As xor is a reversible operation (xor twice = do nothing), we can just forget about it as long as we remember that we are working on a new password. The actual password will just be a matter of xoring.

Note that in the above pseudo-code I did not took into account the special case where the index is 0. In this case, the index is changed to 64. So, in place of having index value ranging from 0 to 63, they will range from 1 to 64 with 64 beeing actually 0. Just remember, for now, that we will have to map 64 to 0 (a simple modulo operation will suffice. Indeed, 64 modulo 64 = 0).

So, how can we reverse that ?
Well, it's clear that if the serie of index when added/muliplied together give the signature, it's true saying that dividing/substracting the serie of index (in reverse order) from the signature will give 0 at the end.

Example :
Let's say the new password's characters indexes are 2,3,4.
The signature will be equal to : (((((0+2)*2)+3)*3)+4)*4 = 100.
And so we can write : 100/4 = 25; 25-4 = 21; 21/3=7; 7-3=4; 4/2=2; 2-2=0.

So, if we can figure out a way to retrieve a serie of value which when substracted/divided from the signature value give 0, then we got a password.

How to do that ?

Given the know signature, we start to find possible indexes for the last password character. Once we found a working index, we divide/substract the signature accordingly and repeat the same process for the next to last character. If we end up with a signature = 0, then we found a valid password.
To see if a given value fit as a possible index value, we can use the following rules.

If the result = 0 then the serie of indexes gathered are the indexes of the characters forming a valid password.

We should also add a third rule to the above stated ones to take into account a maximum password length (thus a maximum number of indexes in our serie). Otherwise we may end up with very long passwords.
The best example is with the index beeing 1 for every characters. Whatever the signature value is, there will always be a password made of x times the character whose index is 1 (with x beeing the signature value).

Let's say the signature is 10.
We can write 0=(((((10-1)/1)-1)/1)-1)/1 .......
As dividing by 1 do nothing, this is the same as writing 0=10-1-1-1-1...
what is still the same as writing 0 = 10-(1*10)

So, we add the rule :

3. recursivity

Now, how can we implement those rules in a program ?
Well, IMHO, this is a perfect case for some recursive function. A recursive function is a function which call itself. Indeed, our program will have to start from the known signature value. Then it will have to try to divide/substract with each value from 1 to 64. If the result respects the rules (ie, not <0 and integer) then it actually update the signature value and try again for the next index. If we broke any rules, then we get back to the previous step and try with the next index value. If we reach a signature value of 0 then we found a valid serie. We convert it to character (remember that xor thing and the 64=0) and print the found password on the screen. We keep trying until all the possibilities have been explored.

You can see this algorithm as a tree. The root node represent the initial signature value. You start a branch for each value that perfectly divide the node signature value and you start a new node with the updated signature value.
If you reach a node where the signature equal 0, then the path leading to that node correspond to a valid password.

Here you have the whole source code. I won't say much about it, cause it should be easy to understand. I tried to comment it as much as I could.

#include "stdio.h"
#include "math.h"
#include "string.h"

//those are the 'parameters'
const int mcode = 412032; //for the 'real' target or
//const int mcode = 10009804; // for the warp sample.
const int NumVal = 15;
//our 'alphabet'
const char alphabet[64] = "6iAbCDdEeFfGgaHphIkjK7BLM3mN1nOlocPQ9qR2rS5sTtUJuVvWwX8xYyZz40 ";

//max password length ... be carefull as the number of solution increase quickly
const int passlen = 4;

//some 'stat' counters
long nb_sol=0;                  //total number of valid password found

//this is a recursive function which will try to recover a valid password
//string from a given signature value.
void newnode(int sign_val, char* password, int level)
{
        int i;                        //we start from 64 downard 1 because 
        for (i=64;i>0; i--)           //there is less chance for a high value to
        {                             //perfectly divide the signature.
                if ((sign_val%i)==0)  //if the value perfectly divide the signature
		{			
                        int new_sign_val;               //update new node value
                        new_sign_val=sign_val/i; new_sign_val-=i;
                        //keep trace of password
                        password[level]=alphabet[(i%64)^NumVal];
                        //start a new node only if the signature is > 0 
                        if ((new_sign_val>0) && ((level+1)<passlen))     
                                    newnode(new_sign_val, password, level+1); 
                        if (new_sign_val==0)            //got a signature of 0 
			{
                                nb_sol++;
				password[level+1]=0;
				char temp[20];
                                int j;
                                //we will reverse the characters
                                //(as we build the password from end to begin)
                                for (j=0; j<strlen(password); j++)
                                       temp[j]=password[strlen(password)-j-1]; 
                                temp[j]=0;
                                //display the password
                                printf("-->%s<--\n",temp);
			}
		}
	}
}

void main()

{
        char password[12];        // we assume password won't be longer than 11 char
        newnode(mcode,  password, 0); // start recursive function.
	printf("total number of possible password found : %ld\n", nb_sol);
}
To confirm the usefullness of this way of breaking the protection, I made some comparaison with a simple brute force through all possible passwords. The following table show the time needed by both program to find all the passwords which have a correct signature given a maximum password length :

max pw
length
# of pwTime ATime B
30<1s<1s
44<1s18s
534<1s19m30s
6218<1s21hour*
715402s56 days*
872188s10 years*
99717629s6 centuries*

Some little comments I'd like to do. First the times marked with a * are extrapolated, but are probably close to reality. Second, even if that algorithm speed up the sorting of possible password, in no way it return ONLY the correct password (because of the non perfect uniqueness of the signature function). For example, if the password length is 8, you'll still have to find a way to try about 7.000 password. Although that's a lot less than the total number of password made up of 8 characters (300.1012), that's still a lot too much. Some more sorting techniques would be needed.

4. Some practical use.

Let's try to see if this approach really work.
As a starter, we will retrieve the correct password for the sample application given along with warp package. The advantage is that we know the password and it's length. So, let's run the program with a max password length of 4 and a mcode of 10009804. The found passwords are : 0grt, test, Tett, 7c9e. You can see that the correct one is well listed :) Our program is working.
We can also see that for the given signature there are 4 possible password of 4 characters. Try to enter any one of the 3 others and you'll see that you won't land neither on the secret page, neither will the 'Access denied' box appear, but you will well land on a '404 page not found'. This confirm what we said about the uniqueness of the signature function.

Now, let's see if we can break that secret page The Seeker found for us.
First we have to find the mcode used by the webmaster. A quick look at the source of that page will show you that the script is located in a external file named warp.js. So, point your browser to http://www.lombardiaonline.com/Mailmax/warp.js and save that page. Now you got it on your hard disk and can easily view it. The mcode is 412032. Don't forget to check also if the NumVal value is still 15 and if the alphabet is the same as in the original warp scrit.
Ok, everything is correct, only the mcode value changed.
We could start the program right now and check for all possible password up to a max length of let's say 8 character. In a few second, we would get a list of about 4500 possible password which we would have to browse through to try to locate the correct one. Let's do it another way and hope that the webmaster was a bit lazy (or kind:) and used a short password. So we will first check out for possible password with a max length of 4. quickly, we come up with 3 possible password :'G ap', 'MAX', 'Ed1J'.
hehe .. MAX, that sound nice ... could it be it ? ... Let's try ... Yep, it is :)
Easy, wasn't it ?



Final Notes

Even if the main protection scheme of this script is unbreakable (beside brute force), the fact that the author wanted to make his script 'user-friendly' by showing a box in place of landing on a '404 page not found' when you enter a wrong password created a big hole in the security of the protection. Of course, long password would always be hard to find (especially if they aren't meaningfull), but the method shown above will in every case dramatically lower the amount of password you'll have to check in a very short time.
In the new warp version, the author implement a different signature algorithm which he claims to be more secure. Why not trying to reverse that one too ? :)

As always, comments, critics, improvements are welcome. Mail me at laurent30(at)hotmail(dot)com

Ob Duh


"Ob Duh" section doesn't apply since we aren't cracking anyones _program_, are we ;)


choose your way out:

redFravia's (frozen) homepage redThe Seeker's homepage redThe javascript workshop redWhat's new