On to the task...
The second task was similar to the first one - break into the admin panel of some website. The page was constructed like the one from Day 1, but with the difference that there was no LFI, and the section names were in ASCII, not base64 as before (for example - /zad2/?page=ofirmie). One of the things that was easy to spot (well, if u have Live HTTP headers and Firebug with Firecookie turned on that is) was that the site sets a cookie - id=12345 (where 12345 was some random number). Playing with the cookie a little made the server show a PHP warning message that was related to /zad2/incl/setid.inc script. The directory incl was listable, and there were some other files there too, including admin.inc that had the following script inside:
/* TopSecurity - Unhackable technologies! */
/* M.Niedoceniany - firstname.lastname@example.org */
/* (c) by TopSecurity 2oo8 */
/* debug? */
if ( $_GET['debug'] )
$del = abs((abs($_GET['del'])%0x7ffffff0)) + 1;
$ins = abs((abs($_GET['inc'])%0x7ffffff0)) + 1;
$del = 1;
$ins = 1;
/* auth stuff */
if ( isset($_POST['login']) && isset($_POST['password']) )
if ( $_GET['debug'] )
echo '<!-- ';
echo levenshtein($adminPass, $_POST['login'] . $_POST['password'], $ins, 1, $del);
if ( levenshtein($adminPass, $_POST['login'] . $_POST['password'], $ins, 1, $del) == 0 )
echo 'Nieprawidłowy login i/lub hasło!';
This is were the solution splits into to paths. The method showed on the official forum focused on the analysis of the internals of the levenshtein functions. The most important part of the internal code looks like this:
static int reference_levdist(const char *s1, int l1, const char *s2, int l2, int cost_ins, int cost_rep, int cost_del )
int *p1, *p2, *tmp;
int i1, i2, c0, c1, c2;
if(l1==0) return l2*cost_ins;
if(l2==0) return l1*cost_del;
So, if one of the given strings is empty, then the result is calculated by the length of the other multiplied by $ins or $del value. The target was to get 0 returned, and it was possible to maneuvered the function into returning 0 using an empty password, and a specific $ins/$del value (even though some abs() and % were used for sanitation). The solution was to solve the equation 32 * (x + 1) = 0 (where 32 is the length of the password) in the 32 bit signed space. One of such values is 134217727. So one could insert this number into $ins, and thats it.
Of course I had to choose the long path ;p
The second, long, path was focused on using the value returned by levenshtein function to guess each letter of the password, letter by letter. The Levenshteins distance (in Polish it's also called Editors distance, don't know 'bout English though) is the total cost of adding, replacing and removing letters from one word, to form another (the cost of each operation may differ). Knowing it the rest is simple - one can just replace each letter in the password by each letter of the alphabet, and note for which letters the returned (displayed in the source) distance is minimum. One of the traps was that the password uses some non alpha characters, but it's just a matter of using a proper alphabet. Of course it's required to create some script to make this task automagic (python is a good choice).
And thats it ;>
I have to confess that I liked this task very much, because there were two ways to solve it. Just for statistics I'll add that it took me about 40 minutes to do this task, including writing a python script that searched for the password.
OK, that would be it for today ;>
P.S. I've added some note below the copyright message in the menu ;>