SQL注入

Posted Kiopler

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL注入相关的知识,希望对你有一定的参考价值。

0. phpStorm代码调试方法


phpstorm启动调试方法:

加上&XDEBUG_SESSION_START=PHPSTORM即可开始调试

phpstorm调试时动态修改变量方法:

调试时对特定变量按下F2即可修改变量内容或者右击按Set Value...选项

1. 联合查询注入


利用前提:

页面上有显示位

优点:

方便快捷, 易于利用

缺点:

需要显示位

细分:

  • 字符型注入

实践一下: 

成功注入:

可以构造如下来判定是否存在字符型注入:

  • 整型注入

 实践一下:

 成功

 联合注入利用order by和group by判断表的列数

order by后面是可以接列号的,比如以第一列排序就是order by 1 以此类推

如果说order by选择的列号存在,就会正常显示

如果选择的列号过大,就会报出如下错误

用这种方法二分法慢慢缩小范围很快能确定列总数,以此方便联合注入

如果不能使用order by, 则可以使用group by来进行判断列数 

 可以构造如下, 即后面接算数表达式, 如果1+2查询与直接写3结果一样则代表存在整型注入:

2. 利用注入列出所有数据库的方法


最重要的数据库为information_schema, 其中有3个非常重要的表:

  1. schemata表的schema_name包含数据库名
  2. tables表中table_schema对应数据库名, table_name对应这存在数据库内的表名集合
  3. columns表中column_name对应列名,table_schema和table_name对应数据库和表名

获取所有数据库名方法:

 获取的结果:

 获取特定数据库的表名方法:

 获取的结果:

获取特定表的列名方法:

 获取的结果:

 3. Load_file以及into outfile的使用


通过mysql利用load_file()函数和selectt ... into outfile ...来读写文件

1. load_file()函数读内容

Windows下如果在mysql配置文件my.ini中

1. 默认secure_file_priv选项默认不存在(即secure_file_priv=null),查询时会显示为NULL。这种情况下不允许对任何文件通过mysql进行读写(mysql5.2以后的版本都默认是这个设置)

2. 限制mysqld的导入, 导出只能发生在/tmp目录下secure_file_priv=/tmp/

3. 不对mysqld的导入, 导出做限制 secure_file_priv=

Linux下则是/etc/my.cnf文件

利用mysql远程读取了一个文件 

 

使用load_file函数读取文件内容需满足的条件:

  1. 绝对路径
  2. 必须是root读取文件的权限
  3. secure_file_priv不能为NULL

2. select into outfile写内容

 不允许把文件名转成16进制来进行写入

使用select...into outfile...写入文件内容需满足的条件:

  1. 绝对路径
  2. 必须是root写入文件的权限
  3. secure_file_priv不能为NULL
  4. 能够使用单双引号(因为文件必须要单双引号)

 尝试使用load_file读取本地内容:

  获取的结果:

 发现好像没有结果, 选择查看源码后立即就找到了文件内容

 尝试使用select ... into outfile ...写入文件

 4. Error-based SQL注入(报错注入)


利用前提:

页面上无需显示位, 但是需要输出sql语句执行错误信息, 比如mysqli_error()或mysql_error()

优点:

不需要显示位

缺点:

需要mysql_error()或者mysqli_error()报错信息

如果配合联合查询注入需要考虑列的问题

-- 这里注意报错注入中是2列,因为其中一列有错误。如果开始的select中有多列则要在后面联合注入内添加新的列
select 1, 2 union select count(*), (concat(floor(rand(0)*2), (select user())))x from mysql.user group by x;

第二种方法不需要考虑列的数目, 只要where后面即可:

select 1 from dual where 1=1 and (select 1 from (select count(*),concat((select user()),floor(rand(0)*2))x from mysql.user group by x)a)

来举一个例子:

extractvalue报错注入

这里通过extractvalue()进行报错注入, 内容显示有限制只能显示部分

为了解决这个问题利用substr函数来获取后面内容

 extractvalue(xml string, 需提取的字符串)中原本后面应写入类似'/../../...'格式的字符串,但我们这里在最前面加上的是0x7e即~符。所以导致报错,其会返回需提取字符串内容,即我们需要的信息

updatexml报错注入

同样它也有显示不完整的问题,为了获取完整内容可以通过substr分段显示的方式获取

在sqli上尝试利用extractvalue报错注入和xmlupdate报错注入:

这是利用extractvalue报错注入:

这是利用xmlupdate报错注入:

 5. Boolean-based blind SQL注入(bool盲注)


利用前提:

页面上没有显示位, 也没有输出SQL语句执行错误信息

只能通过页面返回正不正常

优点:

不需要显示位, 不需要错误信息

缺点:

速度慢, 耗费大量时间

这里使用了sleep()函数,如果执行成功会睡眠10秒

这里1=2绝对不成立,如果id是双引号包裹,那这里就能正常执行(双引号直接提取1忽略后面非数字内容),如果是单引号,应该是错误即啥也不显示。

这种方法通过一个个字母进行二分法判断来找到对应的用户名

为何会没有回显位?

如果碰到无论sql语句执行正确或者失败都没有回显的情况, 只能利用如下bool注入尝试注入并配合二分法,如果出现延迟即ascii位于对应范围内

 尝试实验:

 6. 堆叠SQL注入


这种注入方式出现的很少,因为现在使用mysqli_multi_query()函数,该函数并非多条sql一起执行,而是只执行一条。

如果想要执行多条sql语句,需要加上mysqli_next_result()执行。

 这样就导致堆叠注入只会执行第一条sql语句,后方语句无法执行,看一个例子:

 后面的select sleep(5)无法执行,除非mysqli_next_result()调用。

7. 宽字符注入


宽字符注入的目的是为了解决addslashes()之类的函数添加转义的问题。其使用的前提是mysql会使用宽字节,比如设置宽字节编码的sql语句:

set names gbk/gb2312/GB18030/BIG5...

利用phpstorm调试,举一个例子:

 这里使用的是联合注入方式, 内部调用了3次preg_replace(), 把\\替换成了\\\\\\, 并转义了单引号和双引号。 

函数返回后就发现单引号被转义了

 如果把字符集设置成了gbk或者gb2312之类的宽字符。mysql会把ascii码大于128(%80)的字符当作汉字字符的第一个字节(汉字一共有2个字节, 大于128后面的那个字节会被连带作为汉字的第二字节)

利用这个特性,我们构造这样的注入语句

结果输入部分成这样

 整条语句变成这样了,这就除去了转义的问题。

 这样就成功显示了内容

如果发现单双引号被过滤了,那可以考虑使用宽字节注入。

如何快速判断是否有sql注入?

  1. 四则运算, 看是否返回对应的值, 比如2-1和1是否相等,如果相等可能是整型或者没注入问题
  2. 看是否报错, 利用'和"符号,看是否有错误信息
  3. and 1=1/and 1=2 看页面是否变化, 如果没有变化则很大可能没有注入
  4. 基于时间and sleep(10)看页面是否经过一段时间返回

注入方式分类:

  1. 联合查询注入
  2. 基于报错注入
  3. 基于bool注入
  4. 基于时间注入
  5. 堆叠注入

注入类型分类:

  1. 字符型注入
  2. 整型注入

(未完)

以上是关于SQL注入的主要内容,如果未能解决你的问题,请参考以下文章

如何防范sql注入攻击

出浅入深玩转SQL注入

面试题--如何防止sql注入,使用PreparedStatement的预编译,传入的内容就不会和原来的语句发生任何匹配的关系,达到防止注入的方法

SQL注入语句(详细)

SQL注入的初始&深入

JDBC基础02