In Microsoft SQL Server 2005 and in later versions, multiple instances of SQL Server use the same cryptographic salt for the built-in sa login. Because the salt is the same for all installations, certain kinds of brute force attacks become more practical if the attacker can first gain access to the hashed password. Hashed passwords are available only to the administrators of SQL Server.
Symptoms
In SQL Server 2005 and in later versions, the cryptographic salt is generated together with the sa login. If CHECK_POLICY is enabled, the cryptographic salt is not regenerated when the user changes the password in order to be consistent with the password history. By default, CHECK_POLICY is enabled for SQL Server 2005. When the CHECK_POLICY is disabled, the salt consistency is no longer needed for the sa login, and a new salt is regenerated on the next password change.
Although this is true for all accounts, the sa login account is generated during the build process. Therefore, its salt is created during the same build process and is maintained during an instance of SQL Server setup. Note For SQL Server 2008, this issue also affects the default logins that are used by Policy Based Management feature but the risk is reduced. By default, these logins are disabled.Mitigations
Even if the cryptographic salt remains the same across multiple installations, it would not be sufficient to compromise the password hash. To exploit this behavior, the malicious user would have to have administrative access to an instance of SQL Server in order to obtain the password hash. If best practices are followed, ordinary users will be unable to retrieve the password hash. Therefore, they would be unable to exploit the lack of cryptographic salt variation.
Cause
Service pack information for SQL Server 2005
To resolve this problem, obtain the latest service pack for SQL Server 2005. For more information, click the following article number to view the article in the Microsoft Knowledge Base:
913089 How to obtain the latest service pack for SQL Server 2005
Service pack information for SQL Server 2008
To resolve this problem, obtain the latest service pack for SQL Server 2008. For more information, click the following article number to view the article in the Microsoft Knowledge Base:
968382How to obtain the latest service pack for SQL Server 2008
Resolution
For SQL Server 2005 Service Pack 2 or later versions, you can run the following script to reset the cryptographic salt of the sa login account. To run the script, you must be logged on with an account that has CONTROL SERVER permissions, or the account has to be a member of the sysadmin server role. You should be aware that, after you reset the cryptographic salt, the password history for the sa login will also be reset.
-- Work around for SQL Server 2005 SP2+
-- -- Sets the password policy check off for [sa] -- Replaces [sa] password with a random byte array -- NOTE: This effectively replaces the sa password hash with -- a random bag of bytes, including the salt, -- and finally sets the password policy check on again -- -- After resetting the salt, -- it is necessary to set the sa password, -- or if preferred, disable sa -- CREATE PROC #sp_set_new_password_and_set_for_sa(@new_password sysname, @print_only int = null) AS DECLARE @reset_salt_pswdhash nvarchar(max) DECLARE @random_data varbinary(24) DECLARE @hexstring nvarchar(max) DECLARE @i int DECLARE @sa_name sysname; SET @sa_name = suser_sname(0x01); SET @random_data = convert(varbinary(16), newid()) + convert(varbinary(8), newid()) SET @hexstring = N'0123456789abcdef' SET @reset_salt_pswdhash = N'0x0100' SET @i = 1 WHILE @i <= 24 BEGIN declare @tempint int declare @firstint int declare @secondint int select @tempint = convert(int, substring(@random_data,@i,1)) select @firstint = floor(@tempint/16) select @secondint = @tempint - (@firstint*16) select @reset_salt_pswdhash = @reset_salt_pswdhash + substring(@hexstring, @firstint+1, 1) + substring(@hexstring, @secondint+1, 1) set @i = @i+1 END DECLARE @sql_cmd nvarchar(max) SET @sql_cmd = N'ALTER LOGIN ' + quotename(@sa_name) + N' WITH CHECK_POLICY = OFF; ALTER LOGIN ' + quotename(@sa_name) + N' WITH PASSWORD = ' + @reset_salt_pswdhash + N' HASHED; ALTER LOGIN ' + quotename(@sa_name) + N' WITH CHECK_POLICY = ON; ALTER LOGIN ' + quotename(@sa_name) + N' WITH PASSWORD = ' + quotename(@new_password, '''') + ';' IF( @print_only is not null AND @print_only = 1 ) print @sql_cmd ELSE EXEC( @sql_cmd ) go --------------------------------------------------------------------------------------- -- Usage example: -- DECLARE @new_password sysname -- Use tracing obfuscation in order to filter the new password from SQL traces -- http://blogs.msdn.com/sqlsecurity/archive/2009/06/10/filtering-obfuscating-sensitive-text-in-sql-server.aspx -- SELECT @new_password = CASE WHEN 1=1 THEN -- TODO: replace password placeholder below with a strong password -- ##[MUST_CHANGE: replace this placehoder with a new password]##: ELSE EncryptByPassphrase('','') END EXEC #sp_set_new_password_and_set_for_sa @new_password go DROP PROC #sp_set_new_password_and_set_for_sa go
For SQL Server 2008, you can run the following script. To run the script, you must be logged on with an account that has CONTROL SERVER permissions, or the account has to be a member of the sysadmin server role.
-- Work around for SQL Server 2008
-- ------------------------------------------------------------------------ -- Set the password policy check off for [sa] -- Reset the password -- Set the password policy check on for [sa] once again -- -- NOTE: The password history will be deleted -- CREATE PROC #sp_set_new_password_and_set_for_sa(@new_password sysname, @print_only int = null) AS DECLARE @sql_cmd nvarchar(max); DECLARE @sa_name sysname; -- Get the current name for SID 0x01. -- By default the name should be "sa", but the actual name may have been chnaged by the system administrator -- SELECT @sa_name = suser_sname(0x01); -- NOTE: This password will not be subject to password policy or complexity checks -- if desired, this step can be replaced with a "throw away" password for -- and set the real password after the check policy setting has been set -- SELECT @sql_cmd = 'ALTER LOGIN ' + quotename(@sa_name) + ' WITH CHECK_POLICY = OFF; ALTER LOGIN ' + quotename(@sa_name) + ' WITH PASSWORD = ' + quotename(@new_password, '''') + '; ALTER LOGIN ' + quotename(@sa_name) + ' WITH CHECK_POLICY = ON;' IF( @print_only is not null AND @print_only = 1 ) print @sql_cmd ELSE EXEC( @sql_cmd ) go --------------------------------------------------------------------------------------- -- Usage example: -- DECLARE @new_password sysname -- Use tracing obfuscation in order to filter the new password from SQL traces -- http://blogs.msdn.com/sqlsecurity/archive/2009/06/10/filtering-obfuscating-sensitive-text-in-sql-server.aspx -- SELECT @new_password = CASE WHEN 1=1 THEN -- TODO: replace password placeholder below with a strong password -- ##[MUST_CHANGE: replace this placehoder with a new password]##: ELSE EncryptByPassphrase('','') END EXEC #sp_set_new_password_and_set_for_sa @new_password go DROP PROC #sp_set_new_password_and_set_for_sa go
In SQL Server 2008, the cryptographic salt for the Policy Based Management logins can be reset by using the following script. To run the script, you must be logged on with an account that has CONTROL SERVER permissions, or the account has to be a member of the sysadmin server role.
------------------------------------------------------------------------
-- Set the password policy check off for the Policy principals -- Reset the password -- Set the password policy check on for them once again -- -- NOTE: -- These principals are not intended to establish connections to SQL Server -- So this SP will also make sure they are disabled -- CREATE PROC #sp_reset_password_and_disable(@principal_name sysname, @print_only int = null) AS DECLARE @random_password nvarchar(max) SET @random_password = convert(nvarchar(max), newid()) + convert(nvarchar(max), newid()) DECLARE @sql_cmd nvarchar(max) SET @sql_cmd = N'ALTER LOGIN ' + quotename(@principal_name) + N' WITH CHECK_POLICY = OFF; ALTER LOGIN ' + quotename(@principal_name) + N' WITH PASSWORD = ''' + replace(@random_password, '''', '''''') + N'''; ALTER LOGIN ' + quotename(@principal_name) + N' WITH CHECK_POLICY = ON; ALTER LOGIN ' + quotename(@principal_name) + N' DISABLE;' IF( @print_only is not null AND @print_only = 1 ) print @sql_cmd ELSE EXEC( @sql_cmd ) go EXEC #sp_reset_password_and_disable '##MS_PolicyEventProcessingLogin##'; EXEC #sp_reset_password_and_disable '##MS_PolicyTsqlExecutionLogin##'; go SELECT name, password_hash, is_disabled FROM sys.sql_logins go
Workaround
Microsoft has confirmed that this is a problem in the Microsoft products that are listed in the "Applies to" section.
This problem was first corrected in SQL Server 2005 Service Pack 4 for SQL Server 2005. This problem was first corrected in SQL Server 2008 Service Pack 2 for SQL Server 2008.Status
Microsoft thanks the following for working with us to help protect customers: