[Translation] New Benchmark alternative or effective blind SQL-injection by Elekt

Discussion in 'Forum for discussion of ANTICHAT' started by NeMiNeM, 11 Apr 2007.

  1. NeMiNeM

    NeMiNeM Elder - Старейшина

    22 Aug 2005
    Likes Received:

    New Benchmark alternative or effective blind SQL-injection

    In this article we describe a new and universal way to work without benchmark in blind sql-inj in so-called "unusable" bugged queries like UPDATE, DELETE, REPLACE, UPDATE etc.

    Using this new alternative method we can exactly get the required information from database, not just simply modify entries.

    Here we describe all features of well-known methods of sql-inj attacks, which are used towards above-mentioned operators.

    We'll also study the aspects of practical usage of BENCHMARK during exploits writing more comprehensively.

    Note: we provide examples for mysql in this article, however the description will practically work on all databases (considering the queries syntax of course).

    [1] INTRO

    Programmers become more clever, there are less and less bugs in select-queries, but the problem of injections in INSERT,UPDATE,REPLACE,DELETE and others remains important.

    I often see such questions in the forums: "...please, help what to do if I see - 'mysql error INSERT INTO table_name VALUES(...'" When a newbie sees such an error he'll probably close his browser and the amateur will post the same 100th question in the forums. He won't probably receive an answer or the answers will be too difficult for him, because the promotion of blind sql, considering the amount of queries, demands the implementation of automatization, i.e. the ability to write exploits.

    The methods were known long ago and described in details by 1dt.w0lf. on November 14, 2004 and only lazy-bones didn't read it - http://www.securitylab.ru/contest/212099.php
    Our article can be a continuation to it.

    [2] INSERT and others
    (known attacks on instert/update etc are described, it can be omitted if you know this)

    What's the benefit?

    1) (general case)Data modification without a possibility to watch results
    Ideal case - sql-inj in table name. Here is possible everything:
    INSERT INTO [COLOR=Red][SQL][/COLOR] VALUES ('stat','stat2');
    New user adding:
    INSERT INTO [COLOR=Red]mysql.user (user, host, password) VALUES ('newadmin', 'localhost', PASSWORD('passwd'))/*[/COLOR] VALUES ('stat');
    2) DDoS-attack
    But similar sql-injections exist in most cases in column name and it doesn't allow us to change the upgradable table to the one we need.

    INSERT INTO table VALUES ('stat','[COLOR=Red][SQL][/COLOR]');
    So besides database breaking\littering\ddos we won't get anything with data modifying.
    INSERT INTO table VALUES ('stat','[COLOR=Red]bla'),('test', 'demo[/COLOR]');
    INSERT INTO table VALUES ('stat','[COLOR=Red]bla' and BENCHMARK(10000000,BENCHMARK(10000000,md5(now()))) ) /*[/COLOR]');
    3) Queries separating in MSSQL, PostgreSql, Oracle
    But what does combine these three popular DBMS?
    Such wonderful thing as queries separating support, using semicolon, carriage return etc.
    Thus we can add absolutely ANY sql-query, using ";" to separate it from the main.
    What useful can we add? - Search in google for concrete db.

    4) Data modification with a possibility to watch results.
    It's not important here where the injection is - in table or in column name.
    We can see the result of query, though marginally. So it's not a problem to make an attack - purely technical details of result clarification.
    Example: let web-application OPENLY conduct the attendance statistics and there is a vulnerability in a column with User-Agent.
    Then, the result of sql-inj realization will be the attendance statistics output on a page.

    5) BENCHMARK use
    We use BENCHMARK, which helps us to determine the result of query execution depending on the server response time.
    This query will be positive for if and server response will come at once.

    INSERT INTO table VALUES ('stat','[COLOR=Red]1' and 1=if(ascii(lower(substring((select users from mysql.user limit 1),1,1)))>=1,1,benchmark(999999,md5(now()))) )/*[/COLOR] ,'stat2');
    And this one is negative and a lot of time will pass for BENCHMARK execution, thus we'll not get the server's response at once:
    INSERT INTO table VALUES ('stat','[color=red]1' and 1=if(ascii(lower(substring((select users from mysql.user limit 1),1,1)))>=254,1,benchmark(999999,md5(now()))) )/*[/color] ,'stat2');
    It's obvious that instead of INSERT we can put any other operator, including SELECT, because there are also some complicated cases of injections in it.

    [3] Bencmark for hackers
    (benchmark operation features, it can be omitted if you know this)

    The use of BENCHMARK is like a moving to another dimension.
    From received information difference measuring to the measuring of query execution time difference.

    If anyone has any doubts whether the exploits for BENCHMARK are written, please, look through the bugtraqs and make sure in the opposite - there are really some exploits.

    BENCHMARK exploits realization is more complicated than simple character-oriented bruteforce.

    Benchmark features:

    -perhaps the most important detail is that benchmark creates a serious load on server's processor.
    And this load lasts during the whole time of exploit working.
    The administator may not look through the access\error logs, but he could be quite interested why the server is lagging and in top->P "mysqld" takes the first place...

    - action period of exploit is bigger depending on the record length we want to get. (more length = more action period)
    In my practice I needed more than an hour for 32-symbols hash searching. Sometimes more hours - it depends, the conditions are listed below.

    - A hacker and a server will need a wide and reliable channel. These things are important for bruteforcing quality and stability. I don't say that everything will fail if you use dial-up connection, but there will be much more lags, especially if you decide to save time and choose a too small parameter.

    - performance measurement parameter, i.e. the ammount of iterations, in our example - 999999
    From my first-hand experience, since 1dt.w0lf''s article this number has changed together with the growth of performance of modern servers.
    It's recommended to make self-tuning of this parameter for universality, trying to get the acceptable response time.

    - and accordingly, having found the number in benchmark we must set the response timeout.
    This will be arithmetic mean value from the time of true\false execution query.

    - Note: while making an exploit take into account that according to the statistics the number of false queries is bigger than a number of true,
    that means you should set benchmark delay in the way it works when the response is correct. This way:
    if( ?,  true, false )
    1 = if( 1=1 , benchmark(999999,md5(user()), 1 )
    Thus we save our time and reduce the load on the server.

    - Don't forget that server needs rest after each benchmark, to recover so to say.
    Otherwise the next query may have an unexpectable execution time and will give us wrong data. Many people forget about this small detail, because they either test locally or don't think at all, releasing their code without former testing.
    I was really surprised and didn't know why during the bruteforce the first symbol was correct, but then I got garbage - the server was just overloaded with benchmark.
    It's recommended to set the delay time 1-1,5 times more than benchmark execution time. Yes, it will essentially slow down bruteforce but the effect will be better.

    If we look at the code we can easily modify 1dt.w0lf's character-oriented bruteforcer, taking into account benchmark.
    We need
    - change sql according to benchmark
    $http_query = $path."?Cat=&page=1&like=".$username."' AND 1=if(ascii(substring(CONCAT(U_LoginName,CHAR(58),U_Password),".$s_num.",1))".$ccheck.",benchmark(999999,md5(user()),1)/*";

    - timeout
    But sub check($) should return the result depending on whether the server has given the response in time or not.
    $mcb_reguest = LWP::UserAgent->new(timeout=>$timeout) or die;
    - sleep()
    After each false query let's give N seconds rest to the server. Let it recover.

    That's it.

    [4] Benchmark alternative
    Here is, in fact, the most delicious thing!

    Dealing with benchmark I wished to find a substitution to this troublemaking, but the only method.
    And I've found it!

    Actually the difference of output data and time aren't the only factors.

    Let's look at the beginning again and remember how each sql-inj starts.
    Certainly from the quotation mark in request! ... and error message of course.
    But let's think logically - we put quotation mark, mysql checks our query, finds a mistake in it and tells us.

    Follow my thought: mysql first checks whether it's correct, then either makes a query and shows a result or an error message.

    And now let's think if there is another way. Certainly, maybe that's just the point. There may be such case when the query will successfully pass verification, execute,.. but in result we'll get an error message.

    So our task is simply to PROVOKE such query + we should join parameteres in our request in the way that we are be able to manipulate the result of query and get a useful data.

    Here is a task:
    Query failure frustration and as a result - intentional error call which will help to distinguish true\false query.

    I realized it and began to remember different db errors.

    First, I tried to wreck the request with the help of IF, trying to put false table\column\datatype names:

    select if(1=1,null,blaaaah);
    But damn mysql is clever and checks datatype before execution. So it has failed.
    You have an error in your SQL syntax

    An attempt to change not column but table didn't work out as well.
    select null from if(1=1,users,blaaaah);
    By the way, the algorithm of query parsing in mysql is probably expecting the name of a table to be a constant and here it's obviously not a constant.
    It checks the existence of a column in already known table and if the table is not defined, e.g. I specify it through if, mysql will just send me away.

    I didn't check verification principle in other databases, so maybe such simple but universal tricks will work out there.

    What other mistakes can occur during the query execution?
    The most popular after "You have an error in your SQL syntax" is perhaps "The used SELECT statements have a different number of columns". But here verification also passes before executing.

    "Operand should contain 1 column(s)" - fails because of the same reason.
    "warning: mysql_fetch_array..." - it may be, but php-error is specific according to the engine.

    .. the next thing in my errors' chart is "Subquery returns more than 1 row"... Yes yes, that ubiquitous limit.

    Here is what we need.
    MySQL checks data correctness, but can't check the number of returned responses, haven't made a response itself.

    The question is how to formulate a query to be sure to get such error?

    You'll need to provoke two conditions:
    when you are 100% sure that you'll get an error and 100% sure that there won't be any errors.

    Let's say we know beforehand table and column names in a vulnerable application.
    Let the password hash with md5. Then the length of any password = 32 symbols.
    ID and login length will probably be shorter, won't it?
    length(id)=(1-5..) and length(password)=(32)
    Then our exploit will be like this:

    ...123' and 1=(select null from users where length(if(ascii(substring((select password from users where uid=1),1,1))>254,password,uid))>5)/* 
    First password symbol is probably smaller than ascii(254)..
    It means if will return length(id)>5 and so there are not so many users (though we can set a bigger number), SELECT will return NULL.
    In the result we won't see anything as if there is no injection at all.

    ...123' and 1=(select null from users where length(if(ascii(substring((select password from users where uid=1),1,1))>1,password,uid))>5)/*
    As a first password symbol is probably bigger than ascii(1), the condition is true.
    So if will return length(password)>5 which will cause the N results output in SELECT query.
    But in condition only one result 1=(*) can be compared... which will provoke "Subquery returns more than 1 row" !!!

    It's so simple, isn't it?

    I'm sure that more similar mistakes, shown right after query execution, can be found.

    "more than 1 row" features:

    1) the main: the standard speed of characters bruteforce with a full benchmark substitution, time saved and no server load.

    2) At the cost of this performance - a lot of errors in mysql logs, which can attract administrator's attention.

    3)The only thing you should know is some data, like table and column names in order to provoke "more than 1 row" error.

    Maybe you'll find more perfect and universal way than mine! Good luck!

    [5] Links

    Links, public exploits which use benchmark blind SQL-inj :

    March 8, 2007 PHP-Nuke <= 8.0 Final (INSERT) Blind SQL Injection Exploit (mysql)

    March 8, 2007 PHP-Nuke <= 8.0 Final (HTTP Referers) Remote SQL Injection Exploit

    March 8, 2007 PHP-Nuke <= 8.0 Final (INSERT) Remote SQL Injection Exploit

    February 21, 2007 Blind sql injection attack in INSERT syntax on PHP-nuke <=8.0 Final

    December 1, 2006 Invision Gallery 2.0.7 SQL Injection Vulnerability

    September 8, 2006 PHPFusion <= 6.01.4 extract()/_SERVER[REMOTE_ADDR] sql injection exploit

    July 25, 2006 SQL-Injection in Shop-Script PRO & Shop-Script Premium all version

    May 3, 2006 sBlog SQL Injection and Path Disclosure Vulnerability

    March 24, 2004 MS Analysis v2.0 module for PhpNuke MS Analysis

    February 20, 2004 SQL injection in Php-Nuke 7.1.0

    Original article by Elekt - http://forum.antichat.ru/thread35207.html

    Translation by NeMiNeM.
    (c) for www.antichat.ru


    Please, feel free to show and correct my mistakes in the translation. No flame please. Thank you.
    #1 NeMiNeM, 11 Apr 2007
    Last edited: 16 Apr 2007
    13 people like this.
  2. Elekt

    Elekt Banned

    5 Dec 2005
    Likes Received:
    come on email
  3. zl0y

    zl0y Banned

    13 Sep 2006
    Likes Received:
    Good work man :)