本文内容来自蚁景网安课程。
数据库基本概念
数据库基础
数据库用于存放数据,用户可以对文件中的数据新增、截取、更新、删除等。
数据库管理系统
数据库管理系统(DataBase Management System,DBMS)是用于管理数据库的软件系统,一般具有存储、截取、安全保障、备份等基础功能。
数据库管理系统主要分为:关系型数据库和非关系型数据库。
管理型数据库:存储格式能直观反映实体间关系,和创建的表格相似,表与表之间有复杂的关联关系:MySQL、Oracle、Microsoft SQL Server、Access和PostgreSQL等。
非关系型数据库:指分布式、非关系型、不保证遵循ACID原则的数据存储系统:MongoDb、redis、Hbase、Cloudant等。
SQL
SQL(Structured Query Language 结构化查询语句)是一种特定目的程序语言,专门用来与数据库沟通。
MySQL基础
数据类型
MySQL支持多种类型,大致分为:数值、日期/时间和字符串(字符)类型。
MySQL约束类型
约束是一种限制,通过对表的行和列的数据做出限制,来确保表中数据的完整性、唯一性。
约束类型 | 关键字 |
---|---|
主键 | primary key |
外键 | foreign key |
唯一 | unique |
非空 | not null |
自增 | auto_increment |
默认值 | default |
常见MySQL语句
mysql -u root -p
连接数据库。
|
|
列出数据库管理系统的所有数据库。
|
|
创建数据库
|
|
删除数据库
|
|
在数据库里新建表,语法格式:create table 表名(字段名1 数据类型 约束, 字段名2 数据类型 约束);
数据库操作
增
|
|
改
|
|
查
|
|
删
|
|
order by排序
|
|
用于根据指定的列对结果集进行排序。
order by特性:当order by的数字大于列数时就会报错
limit控制输出
|
|
用于控制输出数据的数量,在注入时若无回显可以用来控制数据然后进行对比。
注释符
1、#
2、--
注意:后面有一个空格。
3、/* */
其他
version() @@version:版本信息。
@@datadir:路径。
@@version_compile_os:操作系统。
session_user():连接数据库的用户名。
system_user():系统用户名。
database():当前数据库。
MySQL逻辑函数
and && 与
or || 或
真 and 真 真
真 and 假 假
真 or 假 真
假 or 假 假
MySQL特性
information_schema
MySQL5.0以上,MySQL自带information_schema数据库,5.0以下没有。
SCHEMATA:
SCHEMA_NAME(该列保存了所有数据库名)
TABLES:
TABLE_NAME(该列存储了所有表名)
TABLE_SCHEMA(来自哪个数据库)
COLUMNS:
COLUMN_NAME(该列存储了所有的列名)
TABLE_NAME(来自哪个数据表)
TABLE_SCHEMA(来自哪个数据库)
实际运用
|
|
|
|
|
|
SQL注入基本概念
SQL注入攻击
通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都时SQL语法的一些组合,通过执行SQL语句进而执行攻击者所要的操作,目前是攻击数据库最常用的手段之一。
Web网站结构
页面层(User Interface Layer)
业务逻辑层(Businesses Logic Layer)
数据访问层(Data Access Layer)
上两层向下请求数据,数据访问层向业务逻辑层提交原始数据,业务逻辑层向表示层提交处理后的数据。
SQL注入如何产生
用户向服务器发起请求,Web服务器会向数据访问层发起SQL查询请求,如果权限验证通过就会执行SQL语句。这种网站内部直接发送的SQL请求一般不会有危险,但实际情况是很多时候需要结合用户的输入数据动态构造SQL语句,如果用户输入的数据被构造成恶意SQL代码,Web应用又未对动态构造的SQL语句使用的参数进行审查,则会带来意想不到的危险。
SQL注入流程
客户端:参数值等数据被修改。
服务端:未经检查和过滤就把被修改的数据注入到SQL命令中,SQL命令功能被修改。
数据库引擎:执行被修改的SQL命令。
服务端:将被注入的结果返回给客户端。
客户端:根据上一次注入获取到的敏感信息构造注入语句进行进一步注入。
SQL注入危害
绕过登录验证:使用万能密码登录网站后台等。
获取敏感数据:获取网站管理员账号、密码等。
文件系统操作:列目录,读取、写入文件等。
注册表操作:读取、写入、删除注册表等。
执行系统命令:远程执行命令。
注入常见类型
根据注入位置分类:GET注入、POST注入、Head头注入。
根据结果反馈注入:有回显注入(显错注入)、无回显注入(盲注)
根据数据类型分类:字符型注入、数字型注入。
还有一些根据数据库不同进行分类等。
SQL注入攻击流程
漏洞判断依据
根据客户端返回的结果来判断提交的测试语句是否成功被数据库引擎执行,如果测试语句被执行了,说明存在注入漏洞。
判断漏洞存在
利用单引号或双引号或\
检测是否存在注入,如果报出SQL错误,或者不回显90%存在错误。
如
http://127.0.0.1/sqli/Less-1/?id=1
后端SQL语句为
|
|
输入一个单引号语句就变成了
|
|
判断注入类型
可以使用and来进行判断
and 逻辑与,特性:当条件表达式两边都为真才是真,有一边假则是假。
判断表中列数
可以使用order by进行判断,当order by的数字大于当前列数时就会报错,SQL注入利用这个特性来判断列数。
注释多余语句
GET型注入可以使用#
的URL编码%23
或者--+
注释
把
|
|
中的' limit 0,1
注释掉。
确定显示位
在一个网站的正常页面,服务端执行SQL语句查询数据库中的数据,客户端将数据展示在页面中,这个展示数据的位置就叫展示位。
UNION操作符用于合并两个或多个SELECT语句的结果集,UNION结果集中的列名总是等于UNION中第一个SELECT语句中的列名,并且UNION内部的SELECT语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条SELECT语句中的列的顺序必须相同。
获取数据
查询当前数据库
|
|
查询当前数据库的表
|
|
group_concat()函数,将多行合并成一行
获取当前数据库全部数据表
|
|
这里的security数据库名、表名等的字符串表示,需要用单引号、双引号包裹住,或者可以使用对应的十六进制形式。
查询当前数据库中某个表的列
|
|
获取列中的数据
|
|
SQL注入之宽字节注入
宽字节注入基本概念
背景
当某字符的大小为一个字节时,称其字符为窄字节。
当某字符的大小为两个字节时,称其字符为宽字节。
所有英文默认占一个字节,汉字占两个字节
常见的宽字节编码:GB2312,GBK,GB18030,BIG5,Shift_JIS等。
程序员为了防止sql注入,对用户输入中的单引号'
进行处理,在单引号前加上斜杠\
进行转义,这样被处理后的sql语句中,单引号不再具有作用,仅仅是内容而已,即这个单引号无法发挥和前后单引号闭合的作用,仅仅成为内容。
宽字节概念
宽字节就是两个以上的字节,宽字节注入产生的原因就是各种字符编码的不当操作,使得攻击者可以通过宽字节编码绕过SQL注入防御。
宽字节注入原理
现在大多数网站对于SQL注入都做了一定的防御方法,如使用MySQL中转义的函数addslashes,mysql_real_escape_string,mysql_escape_string等,还有一种是配置magic_quote_gpc,不过PHP高版本已经移除此功能。其实这些函数就是为了过滤用户输入的一些数据,对特殊的字符加上反斜杠\
进行转义。
网站开启了magic_quote_gpc,或者使用了上面的转义函数,数据库设置成gbk编码(不是HTML编码)
在编码中gbk编码中占用2个字符,ascii占用一个字符。攻击者通过恶意构造吃掉ascii字符,就能进行下一步攻击。
原理讲解
http://abc.com/SQL-GBK/index.php?id=1
当提交id=1' and 1=1%23
时,MySQL运行的SQL语句为
|
|
提交id=1%df' and 1=1%23
,SQL语句为
|
|
将反斜杠吃掉了。
宽字节注入攻击流程
判断漏洞存在
利用%df
或%df'
进行测试。
判断注入类型
可以使用and来判断。
判断表中列数
使用order by 判断。
注释多余语句
使用%23
或--+
进行注释。
确定显示位
利用UNION查询
获取数据
查询当前数据库select database();
|
|
查询当前数据库的表
|
|
获取当前数据库全部数据表
|
|
查询当前数据库的某个表中的列
|
|
获取列中的数据
|
|
SQL注入之盲注
盲注的基本概念
盲注就是在SQL注入过程中,SQL语句执行后,查询到的数据不能回显到前端页面,此时需要一些方法进行判断或尝试,这个过程称之为盲注。
盲注的分类
基于布尔的盲注:SQL语句执行后,页面不返回具体数据,只返回有或者没有(真或假)。
基于时间的盲注:SQL语句执行后,页面无任何数据返回。
盲注的基本流程
判断是否存在注入(单或双引号判断)
获取数据库长度
逐字猜解数据库名
猜解表名数量
猜解某个表名长度
逐字猜解表名
猜解表名数量
猜解某个表名长度
逐字猜解列名
判断数据数量
猜解某条数据长度
逐位猜解数据
基于布尔的盲注
布尔盲注的特性
在页面中,如果正确执行了SQL语句,则返回一种页面,如果SQL语句执行错误,则执行另一种页面。基于两种页面,来判断SQL语句正确与否,达到获取数据的目的。
判断是否存在注入
第一步:正常查询,返回True(语句执行成功)或者返回False(语句执行成功没有查询到内容)
第二步:输入引号查看页面变化,页面返回False,可能存在漏洞,也有可能没查询到内容。
第三步:利用and进行判断,比如:1 and 1=1和1 and 1=2,1’ and 1=1--+和1' and 1=2--+,如果前者执行的结果不一样,说明是数字型的布尔盲注。如果后者执行的结果不一样,说明是字符型的布尔盲注。
获取数据库名
由于页面中没有回显,直接查询即使攻击者构造的SQL语句执行成功了,页面也不会将结果回显,所以只能靠猜。
首先获取数据库名的长度。
length()查询字符串的长度。
|
|
可以使用substr()函数截取字符串。
substr(string, start, length)截取字符串。
注意:MySQL的start从1开始。
|
|
除了以上方法之外,还可以将值转换成ascii码查询,如s的ascii码是115
|
|
获取数据库表名
获取表名之前可以先获取数据库里表的数量。
count():返回数组中元素的个数。
获取表名长度,无回显只能利用substr截取字符串,但查询数据有多行时会报错。
可以使用limit控制输出行数,也可以使用group_concat()函数合并多行。
LIMIT接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。
初始记录行的偏移量是0(不是1)。
|
|
判断表名长度
|
|
获取字段名
在获取字段名前可以先获取数量
|
|
可能有不止一个emails表,所以可以给它限定条件,只限定于当前数据库
|
|
为了避免后面substr函数截取无用的长度,所以可以先判断字段的长度。
|
|
获取字段名
|
|
获取最终数据,使用group_concat()函数合并多行,不需要用limit控制输出行数了。
|
|
课外学习
基于时间的盲注
时间盲注的特性
在页面中,不管用户输入什么,页面都没有任何改变。
判断是否存在注入
第一步:正常查询,无任何返回。
第二步:输入PayLoad测试,如1 and sleep(3)或1' and sleep(3)--+,看页面的响应时间,如果页面加载有延迟,则说明存在漏洞。
常用函数
sleep(n):将程序挂起一段时间n秒。
if(expr1, expr2, expr3):判断语句,如果第一个语句正确就执行第二个语句,如果第二个语句错误就执行第三个语句。
判断PayLoad:
|
|
依据:正确会延迟,错误不会延迟。
攻击流程
与布尔盲注类似,只不过多了sleep函数。
|
|
SQLmap注入
SQLmap基本介绍
SQLmap是一个开源的渗透测试工具,可以用来进行自动化检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。
SQLmap安装
依赖:
python环境,新版本已经支持python3
安装:
python sqlmap.py -h
快捷使用
在环境变量里的系统变量里的path将SQLmap的路径填进去。
SQLmap用法
使用手册
GET型单目标
-u指定URL
python sqlmap.py -u http://test.com/index.php?id=1
--dbs获取数据库名
-p指定参数
python sqlmap.py -u http://test.com/index.php?id=1&page=2 -p page --dbs
-D指定数据库名
--tables获取数据库表名
python sqlmap.py -u http://test.com/index.php?id=1 -D database --tables
-T指定表名
--columns获取字段名
python sqlmap.py -u http://test.com/index.php?id=1 -D database -T table --columns
-C字段名
--dump获取最终数据(拖库)
sqlmap.py -u http://127.0.0.1/sqli/Less-1/?id=1 -D security -T users -C id, password, username --dump
--cookie要测试的页面只有在登录状态下才能访问,登录状态用cookie识别。
python sqlmap.py -u http://test.com/index.php?id=1 -cookie "name=xxx,sessid=abc" -dbs
--batch自动化测试,需要选择全部默认Y(注入过程可能比较久)
GET型多目标
-m指定文件
python sqlmap.py -m file.txt --dbs
POST型注入
-r指定数据包文件
python sqlmap.py -r test.txt -p uname
针对json格式的数据,可以将值修改为*
python sqlmap.py -r test.txt -p * --dbs
HEAD头注入
SQLmap默认测试所有的GET和POST参数,当--level的值大于等于2时也会测试HTTP Cookie头的值,当大于等于3的适合也会测试User-Agent和HTTP Referer头的值。最高可到5。
sqlmap.py -u http://eg.com/index.php --cookie "uname=Dumb" --level 2 --dbs