一、SQL注入类型 1、 UNION注入 联合注入是最简单的一种注入方式,但是要求页面必须存在显位 (即从数据库提取的数据被显示在前端),否则无法注入。 联合查询一般步骤: (1) 先判断是否存在注入,如果存在判断是字符型还是整型注入 (2) 判断列数 (3) 找到显示位,需要隐藏正常结果才能爆出显示位,因此sqli-lab中id=-1’ union select 1,2,3--+,其中id=-1是为了防止正确的结果输出影响我们需要的数据输出,而select 1,2,3是为了找到哪几列能够输出,从而方便输出里面的数据。 (4) 获取数据库名 (5) 获取表名 (6) 获取列名 (7) 获取字段信息 2、 报错查询注入 有些网站在开发调试阶段开启了报错提示信息,如果没有关闭就有可能存在报错注入,有些注入没有显位,但是查询时有报错提示信息,因此报错注入的条件就是网页必须开启了mysqli_error这个函数。常见的报错注入函数有: floor函数: select count(\*) from users group by concat(payload,floor(rand(0)\*2)); 我们通常使用上面的语句来进行注入,其中payload就是我们想要的执行的sql语句。那么如何理解这个呢? 首先rand()是一个数学函数,返回一个随机浮点值[0,1],若指定一个整数参数N,则它被作用于种子值用来产生重复序列,rand(0)的值重复计算是固定的。 floor()同样是一个数学函数,返回一个不大于x的最大整数值,所以floor(rand(0)*2)的值就是重复的011011。 concat()是字符串拼接函数,拼接多个字符串,如果字符串中含有NULL,则返回的结果为NULL。 count(*)是一个聚合函数,返回值的数目,它与count()的区别是它不排除NULL。 group by 在执行时,会依次取出查询表中的记录并创建一个临时表,group by的对象便是该临时表的主键,如果临时表以及存在该主键,则值增加1,如果不存在,则将该主键插入到临时表中。 那么是为何会进行报错呢? 当group by 与rand()使用时,如果临时表没有该主键,则在插入前rand()会再计算一次,这个特性就导致了主键重复并报错。 当group by取第一条from表记录时,此时group by的是security0发现临时表并没有security0的主键,这个时候rand(0)*2会再计算一次,经过floor()后,率先插入临时表的主键不是security0而是security1并计数1,然后取第二条记录group by的key中的1仍由floor(rand(0)*2)继续计算获得,也就是security1此时临时表中已经有security1的主键了,所以count(*)直接加1就可以了。继续从from的表单中去第三条记录,再次计算floor(rand(0),2)结果为0,与database()拼接为security0,临时表中主键不存在,在插入前floor(rand(0)*2)又计算了一次,拼接后为security1,但是是直接插入,即使临时表中已经有主键security1,从而导致主键重复。 extractvalue()函数 extractvaule():对XML文档进行查询的函数,extractvalue(目标xml文档,xml路径)第二个参数xml中的位置是可操作的地方,xml文档中查找字符位置是用/xx/xx/xx/..这种格式,如果我们写入其他格式,就会报错,并且返回我们写入的非法格式内容,而这个非法内容正是我们需要查询的内容。正常查询时第二个参数的位置为/xx/xx/xx即使查询不到也不会报错,没有语法错误不会报错,但是如果故意写入语法错误就会显示错误内容,比如如下: select usernamefrom security.user where id=1 and (extractvalue(‘anything’,concat(‘~’,(selectdatabase())))) 由于使用了concat()这个函数,所以extractvalue()这个函数的第二个参数是~xxx并不是正确的格式,因此会报错,显示无法识别的内容是啥,这样我们就可以在第二个参数插入我们想要执行的sql语句了。值得注意的是extractvalue()能查询的最大字符串长度为32,就是说如果我们想要的结果超过32位,就需要用substring()函数截取,一次查看32位,如下这里先查看数据库名从1到5位的字符串 select usernamefrom security.user where id=1 and (extractvalue(‘anything’,concat(‘#’,substring(hex((selectdatabase())),1,5)))) updatexml()函数 updatexml()函数与extractvalue()函数类似,是更新xml文档的函数,updatexml(目标文档,xml路径,更新内容),报错原因与extractvalue()函数相同,使用方法也相同。 3、 布尔注入 如果页面即不存在显位也没有报错提示的啥试试话可以使用布尔注入,通过插入一些语句查看结果判断是否存在布尔注入。布尔注入常用的几个函数如下:length(select databse())>5判断查询结果的长度 exists()判断查询结果是否存在 ascii()把查询结果转换成ascii的值 substr(string,pos,length)用来截取查询结果,string可以用查询语句代替,pos表示截取位置,length表示截取的长度 4、 延时注入 延时注入就是通过if()和sleep()函数的联合使用,通过是否延时来判断我们是否成功执行sql语句,延时注入的基本格式为:if(condition,a,b)当condition为true时,返回a,当condition为fasle时,返回b例如:判断当前数据库长度 id=3’ and if(length(database())>10,sleep(5),1),如果长度大于10就会延时。 5、 堆叠查询注入 在堆叠注入的页面当中,程序获取的get参数id,使用PDO的方式进行数据查询,但仍然将参数ID拼接到查询语句当中,导致PDO没有起到预编译的效果,程序仍然存在sql注入漏洞。 程序的正常sql语句如下: select * fromusers where ‘id’=’1’;这里我们能控制的参数id,这里我们将参数id写成1’;selectversion();那么代码构造出来的sql语句为 select * fromusers where ‘id’=’ 1’;select version();那么我们可以看出这里执行了两条sql语句堆叠查询就是利用这个特点,在第二个SQL语句中构造自己想要执行的SQL语句,第二条sql的结果如果不能再页面中显示出来,那么我们就可以通过时间盲注来进行数据的穷举。 6、 二次注入 我们在向数据库插入数据的时候插入了恶意数据,虽然对该恶意数据进行了转义,但是插入数据库的时候还是保留了原来的内容,使得数据库本身已经包含恶意数据,然后再利用查询读取恶意数据,但是没有对该数据进行检查,这样我们就可以利用这个恶意数据,这就是二次注入的原理。 7、 宽字节注入 如果我们传入的参数是id=1’时,传入的单引号会被转义符转义,导致参数ID无法逃逸单引号的包围,所以一般情况下是不存在注入的,但是有一个特例。当数据的编码为gbk编码时,可以使用宽字节注入,宽字节的格式是在地址后面加一个%df再加单引号,因为反斜杠的编码为%5c,而再gbk编码当中,%df%5c是一个繁体字所以这样就成功逃逸单引号,爆出MYSQL数据库错误。所以我们可以结合UNION注入来利用这个注入。 8、 cookie注入 顾名思义,cookie注入可注入的参数在cookie中,通过$_cookie获取参数id,然后直接将参数拼接到sql语句当中。参考UNION注入的方法来利用cookie注入。 9、 base64注入 参数经过了base64编码,那么如果这个参数可以注入,我们只需把载荷进行base64编码,然后像UNION注入那样利用这个注入。而且如果存在WAF,WAF会对参数id进行检查,但是由于传输中id经过了base64编码,那么此时WAF很可能检测不到危险代码,从而绕过了WAF。 10、 XFF注入攻击 通过bp抓包可以发现HTPP请求头中有一个头部参数X-Forward-for,简称为xff头。它代表客户端真实的ip,通过修改客户端真实的值可以伪造客户端IP,将XFF设置为127.0.0.1,然后访问该URL,页面返回正常,将XFF设置为127.0.0.1’,再次访问该URL,返回MYSQL报错信息,说明这里可能存在注入点。之后利用UNION注入就可以将里面的数据给显示出来。 二、SQL注入绕过技巧 1、 大小写绕过 有时候开发人员只是单纯对一些关键字符进行了过滤,但是并没有对其大小写进行过滤,那么我们可以尝试对关键字进行大写或者大小写混用的方式来绕过这个过滤。 2、 双写绕过 在进行过滤的时候会将关键字给过滤,但是只过滤了一个关键字,那么我们可以采取双写进行绕过,例如过滤了关键字符串and,我们就可以尝试用anandd来进行注入。 3、 编码绕过 当我们尝试用URL全编码方式进行绕过的时候,服务器会自动对URL进行一次URL解码,所以需要把关键字进行编码两次,值得注意的是URL编码需要选择全编码,而不是普通的URL编码。除了URL编码之外,我们还可以使用html编码、十六进制编码、unicode编码来进行绕过。 4、 注释绕过 例如使用内联注释进行绕过,可利用/*!*/包含关键字进行绕过,在mysql数据库中/*!*/并不是注释的意思而是取消注释内容。 5、 相同功能替换 函数替换: substring/mid/sub ascii/hex/bin benchmark/sleep 变量替换 user()/@@user 符号和关键字 and/& or/| 6、 HTTP参数污染 HTTP参数污染注入源于网站对提交的相同的参数的不同处理方法导致的。例如: 假设输a?key=select&key=1,2,3,4fromtable服务器端可能会将key处理为select 1,2,3,4from table从而导致了SQL注入。
|