20th Dec 2001 [SBWID-4947]
COMMAND
SQL Server stored procedure encryption == NULL
SYSTEMS AFFECTED
SQL Server 2000
PROBLEM
shoeboy posted :
It\'s well known that the stored procedure encryption in SQL Server
2000 has been cracked, but I\'ve been unable to find a discussion of
the algorithm used and what its weaknesses are. I did some digging and
found that not only can the key be retrieved by anyone with sa
privileges (as dOMNAR has so aptly demonstrated with his dSQLSRVD
utility), but the algorithm is incorrectly implemented, making both key
retrieval and sa privileges unneccessary.
So here\'s how stored procedure (and view and trigger) encryption works
on SQL Server 2000:
1. Take the the database\'s GUID (generated when the db is created), the
object id (from sysobjects) and the colid (from syscomments) and
concatenate them.
2. Hash the key using SHA.
3. Use the SHA hash as an RC4 key, generate a sequence of bytes equal in length to the stored procedure text.
4. XOR this stream of bytes against the stored procedure text.
This is a bit short on detail and is based on a dimly remembered
conversation with an MS employee I bummed a cigarette off while
visiting the campus, so it may not be 100 % accurate.
Anyway, there are 2 ways to set about recovering the plaintext. One is
to retrieve the components of the key (the guid is retrievable through
dbcc dbinfo, but you have to be sa to run that command) and this is the
approach taken by dSQLSRVD.
The second option is to find a way to encrypt your own plaintext with
the same key. If you can do this, the encryption algorithm degenerates
to simple XOR encryption with a reusable pad.
It turns out that it\'s trivial to do this thanks to the \"ALTER
PROCEDURE\" statement. Kind of makes you wonder why Microsoft chose to
waste cpu cycles with SHA and RC4 since it doesn\'t buy any extra
security.
Anyway, here\'s some sample code:
[Note that at the end I replace the second version of the bob procedure
with the original. If you\'re using this approach on a production code,
don\'t leave that out.]
SET NOCOUNT ON
CREATE TABLE #tempcomments (
ID int PRIMARY KEY NOT NULL
,ctext nvarchar(4000) NOT NULL
)
GO
CREATE PROCEDURE bob
WITH ENCRYPTION
AS
PRINT \'I encrypted this procedure and forgot to check the source into cvs!\'
PRINT \'Now I don\'\'t work here any more and you can\'\'t find me!\'
GO
INSERT INTO #tempcomments
SELECT 1, ctext FROM syscomments WHERE id = object_id(\'bob\')
GO
ALTER PROCEDURE bob
WITH ENCRYPTION
AS
------------------------------------------------------------------------------
------------------------------------------------------------------------------
print \'I know a secret.\'
GO
INSERT INTO #tempcomments
SELECT 2, ctext FROM syscomments WHERE id = object_id(\'bob\')
GO
DECLARE @origcryptstr nvarchar(4000)
,@origplainstr nvarchar(4000)
,@knownplainstr nvarchar(4000)
,@knowncryptstr nvarchar(4000)
DECLARE @length int
,@counter int
SELECT @origcryptstr = ctext FROM #tempcomments WHERE ID = 1
SELECT @knowncryptstr = ctext FROM #tempcomments WHERE ID = 2
SELECT @knownplainstr = N\'CREATE PROCEDURE bob
WITH ENCRYPTION
AS
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
print \'\'I know a secret.\'\'
\'
set @length = datalength(@origcryptstr)
set @origplainstr = replicate(N\'A\', (@length / 2))
set @counter = 1
while (@counter <= (@length / 2))
begin
SELECT @origplainstr = stuff(@origplainstr, @counter, 1,
NCHAR(UNICODE(substring(@origcryptstr, @counter, 1)) ^
(UNICODE(substring(@knowncryptstr, @counter, 1)) ^
UNICODE(substring(@knownplainstr, @counter, 1)))))
set @counter = @counter + 1
end
select @origplainstr
exec(\'drop procedure bob\')
exec(@origplainstr)
GO
drop table #tempcomments
GO
SOLUTION
Nothing.
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH