0x00 SQL注入概述
什么是SQL注入漏洞
攻击者利用Web应用程序对用户输入验证上的疏忽,在输入的数据包中包含对某些数据库具有特殊意义的符号和命令,让攻击者有机会直接对后台数据库下达命令,进而入侵后台数据库乃至整个应用系统。
主流的数据库
SQL注入原理
SQL注入攻击的本质,服务端没有过滤用户输入的恶意数据,直接把它当成SQL命令来执行了,从而影响数据库安全和平台安全。
两个条件
- 用户能够控制输入;
- 原本程序要执行的SQL语句,拼接了用户输入的恶意数据。
导致的危害
- 绕过登录验证:使用万能密码登录网站后台等
- 获取敏感数据:获取网站管理员账号、密码等
- 文件系统操作:列目录,读取、写入文件等
- 注册表操作:读取、写入、删除注册表等
- 执行系统命令:远程执行命令
造成的原因
(1)动态字符串构建引起
- 不正确的处理转义字符(宽字节注入)
- 不正确的处理类型(报错泄露信息)
- 不正确的处理联合查询
- 不正确的处理错误(报错信息泄露)
- 不正确的处理多次提交(二次注入)
(2)后台存在的问题
- 后台无过滤或者编码用户数据
- 数据库可以拼接用户传递的恶意代码
(3)错误处理不当
- 详细的内部错误消息显示给用户或攻击者
- 错误信息可以直接给攻击者提供下一步攻击帮助
(4)不安全的数据库配置
- 默认账户
SQL Server "sa"作为数据库系统管理员账户
MySQL使用"root"和"anonymous"用户账户
Oracle在创建数据库时通常默认会创建SYS、SYSTEMS DBSNMP和OUTLN账户 - 权限
问题:系统和数据库管理员在安装数据库服务器时允许以roots SYSTEM或Administrator特权系统用户账户身份执行操作。
正确方法:应该始终以普通用户身份运行服务器上的服务,降低用户权限,将用户权限仅限于本服务
SQL注入方法
- 自动化注入工具
- SQL注入工具
Sqlmap Havij Sqlid - ASP/JSP注入工具
NBSI 阿D注入软件 明小子注入软件 - PHP注入工具
穿山甲注入软件 海阳顶端注入软件
- 手工注入过程
- 判断是否存在注入点
- 判断字段长度
- 判断字段回显位置
- 判断数据库信息
- 查找数据库名
- 查找数据库表
- 查找数据库表中所有字段以及字段值
- 猜解账号密码
- 登录管理员后台
—————————————————————————————————
0x01 SQL注入技术
(1)常见SQL语法
- 用于与关系型数据库交互的标准SQL命令有:
Create、Select、Insert、Update、Delete 和 Drop - 分为三组:
- 数据定义(Create、Drop)
- 数据操纵(Select、Insert、Update、Delete)
- 数据控制(Grant、Revoke)
命令 | 描述 |
---|---|
CREATE | 创建新的表、视图或其他数据库中的对象 |
ALTER | 修改现存数据库对象,比如一张表 |
DROP | 删除表、视图或数据库中的其他对象 |
SELECT | 从一张或多张表中检索特定的数据 |
INSERT | 创建一条新纪录 |
UPDATE | 修改记录 |
DELETE | 删除记录 |
GRANT | 赋予用户特权 |
REVOKE | 收回赋予用户的特权 |
- SQL高级操作:排序、分组、限定条数
排序Order by
SELECT * FROM test_table ORDER BY userid;
分组Group by
SELECT name,count(*) FROM test_table GROUP BY name;
限定条数limit
SELECT * FROM test_table limit 0,10;
SELECT * FROM test_table limit 1,5;
组合使用
SELECT * FROM test_table LIMIT 0,5 ORDER BY userid;
-
SQL高级操作:联合查询union:
因为查询语句构造问题,可直接否认掉之前的查询,执行一个全新的查询语句,查询的列应当与之前的列对应。 -
SQL高级操作:结合其他函数操作:
在MySQL中,把information_schema(MySQL自带的)看作是一个信息数据库,其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。
information_schema.SCHEMATA表中的SCHEMA_NAME查看所有的数据库:
select * from hehe where id=3 and 1=2 union select 0,0 SCHEMA_NAME from information_schema.SCHEMATA limit 1,2;
(2)万能密码绕过登录
通过在用户名处传入参数 ’ or 1=1 # 进行万能密码登录。
SELECT username, password FROM users WHERE username=‘textvalue’ or 1=1 # ’ AND password=‘textvalue2’
- 输入字符
formusr = ’ or 1=1 - -
formpwd = anything - 实际的查询代码
SELECT * FROM users WHERE username=’ ’ or 1=1 - - AND password= ‘anything’
(3)数字型注入
- 输入的参数为整数,如ID、年龄、页码等,如果存在注入型漏洞,则为数字型注入。
http://www.testweb.com/user.php?id=8 - 存在数字型注入测试方法
http://www.testweb.com/user.php?id=8‘ | 返回错误,未对单引号做处理 |
---|---|
http://www.testweb.com/user.php?id=8 and 1=1 | 运行正常 |
http://www.testweb.com/user.php?id=8 and 1=2 | 运行异常 |
(4)字符型注入
-
输入的参数为字符串
与数组型注入的区别在于:字符型注入一般要用单引号或双引号等来闭合
http://www.testweb.com/user.php?user=admin’ -
存在字符型注入测试方法
http://www.testweb.com/user.php?id=8‘ | 返回错误 |
---|---|
http://www.testweb.com/user.php?user=admin’ and ‘1’='1 | 运行正常 |
http://www.testweb.com/user.php?user=admin’ and ‘1’='2 | 运行异常 |
(5)SQL盲注
SQL盲注是不能通过直接显示的途径来获取数据库数据的方法。
在盲注中,攻击者根据返回页面的不同来判断信息(可能是页面内容不同,也可以是响应时间不同)。一般情况下,盲注分为三种:
- 布尔盲注
某些查询语句是不需要返回结果的,仅判断查询语句是否正确执行即可,所以其返回值可以看作一个布尔值,正常时显示true,报错或不正常显示false。
查询语句:
SELECT userid FROM member WHERE u_name=$name AND u_pass= $pass;
注入语句:
name=-1' and (select mid(u_name,1,1) from member where userid=1)='a'
name=-1' and (select mid(u_name,2,1) from member where userid=1)='d'
name=-1' and (select mid(u_name,3,1) from member where userid=1)='m'
name=-1' and (select mid(u_name,4,1) from member where userid=1)='i'
name=-1' and (select mid(u_name,5,1) from member where userid=1)='n'
- 时间盲注
某些查询语句是不需要返回结果的,仅判断查询语句是否正确执行即可,但是由于某些限制,布尔盲注的关键字符代入不进去,这时候就可以利用sleep()函数来进行时间盲注,取页面执行时间(结束时间-开始时间)来判断sleep()函数是否正常执行,所以其是否正常执行可以看作一个布尔值,正常时显示true,报错或不正常显示false。
查询语句:
SELECT userid FROM member WHERE u_name=$name AND u_pass= $pass;
注入语句:
name=-1' and (select mid(u_name,1,1) from member where userid=1)='a' and (select sleep(3))
name=-1' and (select mid(u_name,2,1) from member where userid=1)='d' and (select sleep(3))
name=-1' and (select mid(u_name,3,1) from member where userid=1)='m' and (select sleep(3))
name=-1' and (select mid(u_name,4,1) from member where userid=1)='i' and (select sleep(3))
name=-1' and (select mid(u_name,5,1) from member where userid=1)='n' and (select sleep(3))
基于时间的盲注在使用延迟函数上可以有两个选择:
(1)BENCHMARK(count,expr)函数 //将expr语句执行count次来延迟时间
(2)sleep(time)函数 //直接延迟time时间
- 报错型盲注
正确时浏览器返回"You are in …",错误会有报错语句。
(6)SQL文件头注入
HTTP Header内容:
- User-Agent:使得服务器可以识别客户使用的操作系统,浏览器版本等(很多客户量大的网站会记录客户使用的操作系统或浏览器版本等存入数据库中)
- Cookie:网站为了辨别用户身份、进行session追踪而储存在用户本地终端上的数据(通常经过加密)
- X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP(通常一些网站的防注入功能可能会记录请求端真实IP并写入数据库 or 某文件中【通过修改XFF头可以实现伪造IP】
- Clien-IP:同上
- Referer:浏览器告诉WEB服务器我是从哪个页面链接过来的
- Host:客户端指定自己想访问的WEB服务器的域名/IP地址和端口号
常见的HTTP Header注入类型:
- Cookie注入
- Referer注入
- UA注入
- XFF注入
(7)宽字节注入
统一国际规范的理想:程序都使用unicode编码,所有的网站都使用utf-8编码。
国内及国外(特别是非英语国家)的一些cms,仍然使用自己国家的一套编码方式,比如gbk,作为自己的默认编码类型。也有一些cms为了照顾老用户,出了gbk和utf-8两个版本。
一个gbk编码汉字,占2个字节,一个utf-8编码汉字,占3个字节。简体中文系统中ANSI就代表是GBK。
我们通常使用单引号来探测是否有SQL注入,但我们有可能在前面使用了转义函数,导致输入id=1’ 变成了 id=1\’ ,探测失效了。
于是,输入id=1%df’ ,会变成 id=1%df\’ ,url编码就是id=1%df%5c%27,gbk是多字节编码,任务两个字节代表一个汉字,所以%df和后面的\也就是%5c变成了一个汉字,而 ’ 逃逸出来,可以进行SQL注入探测。
———————————————————————————————————
0x02 SQL注入检测绕过
- 大小写绕过
比如拦截了union,那就使用Union UnloN等等。 - 编码绕过
WAF检测关键字,比如检测union,用%55也就是U的16进制编码来代替U,union写成%55nloN,结合大小写也可以绕过一些WAF。 - 注释绕过
适用于WAF只是过滤了一次危险的语句,而没有阻断我们整个的查询
/?id=1+union+select+1,2,3/*
对于上面这条语句,WAF过滤了一次union和select,在之前再写一个注释语句,让他把注释里面的过滤掉,并不影响我们的查询。
绕过语句就是
/?id=1/*union*/union/*select*/select+1,2,3/*
- 分隔重写绕过
适用于那种WAF使用了正则表达式的情况,会检测所有的敏感字
/?id=1+un/**/ion+sel/**/elct+1,2,3-
至于重写绕过,适用于WAF过滤了一次的情况,和我们上传aaspsp马的原理一样,我们可以写出类似Ununionion这样的,过滤了一次union就会执行我们的查询了。
/?id=1 Ununionion select 1,2,3-
- 同功能函数转换
Substring()函数可以用mid()和substr()这些函数来替换,都是用来取字符串的某一位字符的。
Ascii()编码可以用hex(),bin()也就是16进制,二进制编码转换。
时间盲注Benchmark()也可以用sleep()来替换。
sqlmap中有一个tamper目录,主要存放的是waf绕过脚本,比如:base64encode.py,between.py,greatest.py等。
- 等号被过滤了---->[equaltolike.py]---->like代替等号
Example:
Input: SELECT * FROM users WHERE **id=1**
Output: SELECT * FROM users WHERE **id LIKE 1**
- 替换字符和单引号被过滤---->[apostrophenullencode.py]
Example:
("1 AND '1'='1")
"1 AND %00%271%00%27=%00%271"
———————————————————————————————————
0x03 SQL注入的防护策略
- 使用参数化查询,检查变量数据类型和格式
SQL注入因为要操作数据库,所以一般会查找SQL语句关键字:insert,delete,update,select,查看传递的变量参数是否为用户可控,有无做过安全处理
- 采用sql语句预编译和绑定变量
String sql="select id,no from user where id=?";
PreparedStatement ps =conn.prepareStatement(sql);
ps.setInt(1,id);
ps.executeQuery();
采用了PreparedStatement,将sql语句"select id,no from user where id=?"
预先编译好,即SQL引擎会先进行语法分析,产生语法树,生成执行计划,这样后面无论你输入什么参数,都不会影响该sql语句的语法结构了。
- 不要随意开启生产环境中Webserver的错误显示
- 做好数据库账号权限管理
- 严格加密处理用户的机密信息
- 使用WAF等专业的防护软件系统
————————————————————
以上来自360网络安全学院课程,侵权删~~