「SQL注入入门篇」sql注入初始篇

为什么我是web安全菜鸡啊!!!

Posted by 许大仙 on November 4, 2017

从此记录SQL-labs的学习经历和笔记,主要留下在学习过程中遇到的难点和顿悟点

集成环境:Wampserver(直接安装即可,可能php.ini需要修改)

题目地址:https://github.com/Audi-1/sqli-labs

安装

  • 从https://github.com/Audi-1/sqli-labs下载压缩包
  • 将zip文件复制到mysql中的www文件夹下
  • 打开sql-connections文件夹下的“db-creds.inc”文件
  • 修改mysql用户名和密码为你自己的(没有设置过用户名可以不用管这步)
  • 访问localhost下的文件夹:点击页面第一栏setup/resetDB 链接自动回在你的mysql中创造数据库

参考链接

老学长的学习心得

安全科普:SQLi Labs 指南

一、lesson 1:GET-基于错误-单引号字符型

1.如何理解单引号没有闭合会报错

回顾注入类型:

数字型注入 select * from news where id=$id
字符型注入 select * from news where username='$name'
搜索型注入 select * from news where password like %best%

首先在http://localhost/sqli-labs-master/Less-1/中加入?id=1(用数值型注入得到一个id参数,?表示传参)。这个链接在SQL语言中可以理解为查询语句:$sql=” SELECT * FROM users WHERE id=’1’或者id=1或者like %1%;这是合法的。在hackerbar中输入excute之后是会返回数据库值的。

以字符型为例,如果加入?id=1’。那么查询语句变为$sql=”SELECT * FROM users WHERE id=’1’‘。excute后返回错误信息:

You have an error in your SQL syntax;
 check the manual that corresponds to your MySQL server version for the right syntax 
to use near ''1'' LIMIT 0,1' at line 1

上述错误信息可以说明:

  1. 现在注入的是mysql数据库
  2. 验证了现在真正的查询语句可能是
  3. $sql=” SELECT * FROM users WHERE id=’$id’ LIMIT 0,1 “

在输入?id=某某的时候,也就是给$id传参了($id=1’)。传参之后语句变为

$sql=" SELECT * FROM users WHERE id='1'' LIMIT 0,1 "

三个引号不成对了,mysql_query()不接受了,不知道引号怎么配对了,所以就报错了。 如果改成?id=1’‘,那么查询语句是

$sql="SELECT * FROM users WHERE id='$id''' LIMIT 0,1"

现在引号是双数,成对了,单引号都闭合了,就能返回数据库的值,不报错(说明不是搜索型)。

再者,将查询语句变为:?id=4’ and 1=1 +–+不报错,说明有单引号闭合,不是数字型也不是双引号型。

由此判断less 1是字符型(可以参考less2中的数值型)

2.LIMIT语句:偏移筛选

在语句中的 LIMIT 0,1 这一句是对查询结果的一个偏移,因为$sql=”SELECT * FROM users WHERE id=’$id’ LIMIT 0,1”;语句会显示这个id列中所有的行,但是网站在搭建的时候筛选了能显示的信息量(本题是两个—因为通过前面?id=1,返回两个信息dumb,dumb),因此我们就没法通过加上?id=1来显示id=1列的所有的行。但是又想知道这个列的所有数据,这时候就用limit来遍历(每次显示两个数据,偏移多次,显示多个不同的两个数据)。

这就要通过limit语句。 LIMIT 语句第一个表示起点,第二个表示步长。 因此 LIMIT 0,1这个语句将会显示第一行。同理,如果要显示第 6-15 行,我们需要使用 LIMIT 5,10 来进行限制。

3.注释

  • MySql 的注释符号有 “+–+, – (后面有空格符), #”等
    • #…:从‘#’字符从行尾。
    • – …: 从‘– ’序列到行尾。请注意‘– ’(双破折号)注释风 格要求第2个破折号后面至少跟一个空格符(例如 空格、tab、换行符等等)
    • //:从/序列到后面的/序列。
  • 但是实际在操作的时候 %,#,– 都不能达到效果,需要将其转化为 URL 编码才能被正确的传到后台。
  • 因此在地址栏注入时,可以直接使用+–+但是不可以使用#,而要写成为%23
    • 比如:union select 1,2,table_name from information_schema.tables where table_schema=’数据库名’ +–+或
    • union select 1,2,table_name from information_schema.tables where table_schema=’数据库名’ %23
  • 为什么要注释掉后面?
    • 因为要$sql=”SELECT * FROM users WHERE id=’$id’ LIMIT 0,1”在传参时例如传入1’ union select 1,2,3 +–+
    • 这时候利用输入值中的1’中的’时使得原来的引号sql语句中前引号闭合,加入注释使得后引号被注释掉,不起作用。那么这时候就可以形成自定义的查询语句不受引号闭合影响。
    • 查询语句具体为:SELECT * FROM users WHERE id=’1’ union select 1,2,3 +–+’ LIMIT 0,1
    • 其中的’ LIMIT 0,1被注释掉了

爆字段

使用 order by 函数加上多行来爆字段,当数字为 3 时没有报错;当数字加到 4 的时候提示没有该列,因此字段数为 3。

order by 1,2,3,4 表示按照 1,2,3,4, 列去排列查找到的数据,由于只有 3 列,当我们试图用第 4 列去排序的时候自然会报错。

或者用order by 加上数字。例如order by 3或者order by 4

union查询数据库,版本,数据库用户

由爆字段order by可知 union select中要求的:

  1. 两个查询返回的列数必须相同
  2. 两个查询语句对于列返回的数据类型必须相同

可知前一个select的列数为3,所以现在select 后面只能跟3个待查询值。

此外:union select 和前面的查找是并集的关系(两个select语句查询的结果合并成一个表统一显示,列名按照第一个select的列)。由于显示的位置只有两个,我们必须让前面查找不出结果,这样合并后,就会显示出我们需要查找的内容。比如把 id 设置为 - 1,注意在查找时仍然要闭合好前面的单引号。

相关union指令的使用参见: 菜鸟教程:SQL UNION指令

之后,使用 union select 1,2,3 来判断下网页上会显示哪一列,然后再把相应的位置来替换成需要的信息。此时显示出:Your Login Name:2,Your Password:3。说明应该把2,3位置替换成需要的信息。

查找以下项目来收集所需信息:

  • 版本信息:version()
  • 数据库信息:database()
  • 用户信息:user()
  • 当前用户:current_user()
  • 数据库路径:@@datadir
  • MySQL 安装路径:@@basedir
  • 操作系统:version_compile_os

查询表、查询列、查询数据

枚举数据库并提取数据的步骤: 数据库名称->表->列->数据

若要想获取远程数据库的表、列,就要访问专门保存描述各种数据库结构的表。(通常将这些结构描述的信息成为元数据)。

在Mysql中,这些表都保存在information_schema数据库中。

提取数据库:

在MySQL中,数据库名存放在information_schema数据库下schemata表schema_name字段中

`?id=-1' union select null,schema_name,null from information_schema.schemata +--+`

如果要遍历所有数据库名可以加上limit k,1

例如:

?id=-1' union select 1,schema_name,null from information_schema.schemata limit 2,1 +--+

可以爆出表名:information_schema,mysql,security……

提取表名:

在MySQL中,表名存放在information_schema数据库下tables表table_name字段中。

union select 1,2,table_name from information_schema.tables where table_schema='数据库名' +--+

此时可以使用where子句来筛选了,只返回上述以提取过的数据库名中某个数据库下的表名。若想返回所有表名,去掉where子句就行了(但是由于本题只可以显示两个信息位,因此还是需要limit k,1来进行遍历)。 例如:

?id=-1' union select 1,schema_table,null from information_schema.tables 
where table_schema='security' +--+

提取字段名(列名):

在MySQL中,字段名存放在information_schema数据库下columns表column_name字段中。用where子句限制哪个数据库的哪个表。

union select 1,2,column_name from information_schema.columns where table_schema='数据库名' 
and table_name='表名' limit 0,1 +--+

例如:id=-1’ union select null,column_name,null from information_schema.columns where table_schema=’security’and table_name=’emails’ +–+

提取数据:

按照需要select某个列的数据信息。

union select 1,2,username from users limit 3,1 +--+ 来获取具体的数据信息。

总结上述,是提取security数据库中的users表的id/username/password列的数据的过程。

concat_ws 函数

使用 concat_ws 函数可以获取多条数据并用所要求的符号分割开。

详见:concat、concat_ws、group_concat函数用法

例如:concat_ws(char(32,58,32),username,password)就可以同时显示出用户名和密码,用 “空格、冒号、空格” 来分割 PS:char()将ascii码转化为字符

  • 32ascii表示’ ‘
  • 58ascii表示:

例子中显示出Dumb:Dumb