题目一
1 | select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1; |
题目解析
简单的 SQL 注入,Payload 如下:
1 | ' or 1=1 ; -- |
没有任何过滤就拼接 SQL 语句,导致漏洞产生。下面分析 Payload 的效果:
1 | select username,password from user where username !='flag' and id = '"' or 1=1 ; -- "' limit 1; |
- 单引号
'
与前面的单引号闭合,使后面的or 1=1 ; --
被视为 SQL 语句而非字符串。 or 1=1
与前面的表达式组合得到username !='flag' and id = '"' or 1=1
:where
一般情况下检查表中的每一行,并针对每行计算where
后的整个表达式的结果,若真则返回该行,反之则跳过。- 由于
and
的优先级比or
高,所以最终计算结果恒为1
。where 1
会匹配并返回所有行。 - 分号
;
结束该句,防止语法错误。 - 注释符
--
注释掉后面的双引号、单引号与limit 1
。防止语法错误,以及绕过原语句中返回结果只有一行的限制。(最后面一定要有空格,关于注释,参考另一篇简单讲解 SQL 注释符的笔记)
题目二
SQL 部分
1 | select username,password from ctfshow_user2 where username !='flag' and id = '".$_GET['id']."' limit 1; |
PHP 片段
1 | //检查结果是否有flag |
题目解析
先探测列数:
1 | ' order by 1 -- |
order by
:在 SQL 里,ORDER BY
是用于对查询结果进行排序的子句。语法如:
1 | ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...; |
column1
为列,可以是列名,也可以是索引(1,2,3…); [ASC|DESC]
为升降排序的可选项。
order by 1
则是测试列数,若无报错(这里应该是空结果,因为select username,password from ctfshow_user2 where username !='flag' and id = '"' order by 1 --
,不太可能有某个字段的值为"
)则递增数字,直到报错为止,那么列数即为未报错的最大数字。
也可以使用联合查询来探测:
1 | ' union select 1,2,3,4-- --递增列数 |
union select
:在 SQL 中,SELECT
之间用UNION
连接起来,结果集合并成一个结果集。
合并要求:- 列数必须相同:所有
SELECT
语句的列数必须一致。 - 数据类型兼容:对应列的数据类型应该兼容(或可隐式转换)。
ORDER BY
只能放在最后:合并结果只有一张表,所以ORDER BY
要放在最后给整张表排序。列名通常继承自第一张表,如果要用列名排序,需要用第一张表的列名。也可以用索引和第一张表的别名排序。
基本语法:
1
2
3SELECT column1, column2 FROM table1
UNION [ALL]
SELECT column1, column2 FROM table2;UNION
默认去重,使用UNION ALL
合并结果时不去重。- 列数必须相同:所有
union select 1,2,3,4
测试列数。可以把select 1,2,3,4
看作联合查询时自定义添加一行内容1,2,3,4
。由于联合查询时列数必须相同,所以当无报错时,说明列数正确;如果报错,则修改列数再次测试,直到不报错。
确定列数后,利用联合查询,按照下列步骤获得 flag :
1 | ' union select 1,database()-- --返回数据库名 |
database()
:MySQL内置函数,返回当前连接的数据库名。information_schema
:虚拟的数据库,并不实际存储数据,由数据库系统根据元数据动态生成。包含数据库中所有数据库的信息、数据库中所有表的详细信息、数据库中所有列的信息与关于索引的元数据以及其他一些信息等等。information_schema.tables
:包含了数据库中所有表的信息,如表名、表类型、存储引擎、行数、创建时间、更新时间等。此处用于获取当前数据库中有哪些表。where table_schema='<库名>'
:table_schema
是表中的一个字段,表明该表位于哪个数据库。information_schema.columns
:大致同上,此处用于获取目标表中有哪些列。where table_name='<表名>'
:大致同上,表明列在哪张表中。group_concat()
:将多行结果合并成逗号分隔的字符串,全部表示在一行中。
整个过程,就是利用联合查询与数据库的特性得到各种信息,最后得到 flag 。