Déchiffrement d’objets MSSQL 2000, 2005, 2008 et 2012

24
janv.
2013
  • Google Plus
  • LinkedIn
  • Viadeo
Posted by: Yann C.  /   Category: Administration réseaux et systèmes / Base de données SQL / Cryptanalyse / Cryptographie / Cryptologie / Windows   /   Pas de commentaire

Les serveurs SQL de Microsoft connus sous le nom MSSQL ont la faculté depuis la version 6.5 (puis 7,  2000, 2005, 2008 et enfin 2012) de protéger les objets via un algorithme de chiffrement.

Les serveurs MSSQL autorisent le chiffrement sur :

  • Les vues (view – v)
  • Les fonctions (function – fct)
  • Les procédures stockées (stored procedure – sp)
  • Les déclencheurs (trigger)

Cette protection se caractérise par un petit cadenas apposé sur ces dits objets au travers du client « Microsoft SQL Server Management Studio » (SSMS). Leur édition devient impossible, tout comme la visualisation du code source de ces objets.

Objet chiffré dans MSSQL2012 visible via SSMS

Objet chiffré dans MSSQL2012 visible via SSMS

Ces objets chiffrés ne peuvent être édités, modifiés, ou visualisés :

Edition restreinte de l'objet chiffré

Edition restreinte de l’objet chiffré

Ces mécanismes sont largement employés par de nombreux éditeurs et développeurs de produits logiciels basés sur MSSQL, afin de protéger la propriété intellectuelle et le droit d’auteur de leurs scripts. Toutefois, l’algorithme en place dans chacune des versions de MSSQL Server s’avère faible et peut être facilement inversé.

Comment définir un objet chiffré au sein d’une base MSSQL ?

La méthode pour protéger ces objets est d’une grande simplicité. Il suffit de déclarer chacun d’eux en utilisant les mots clés « WITH ENCRYPTION ». Exemple pour une procédure simple :

CREATE PROCEDURE helloWorld
WITH ENCRYPTION
AS
PRINT 'Encrypted procedure !'

Dès que ces mots clés sont renseignés, la procédure, fonction, trigger ou vue est protégée de l’édition et de la consultation. Il convient donc de toujours conserver une version en clair de l’objet, pour pouvoir le mettre à jour aisément.

État de l’Art du déchiffrement MSSQL toutes versions

Depuis de nombreuse années, de multiples outils permettent de déchiffrer les objets des base MSSQL. La plupart sont payants, et dispose d’une version d’essai qui limite le déchiffrement à N caractères. De plus, certains ne prennent pas en compte toutes les versions de MSSQL, encore moins la dernière MSSQL 2012.

Liste non-exhaustive de solutions existantes :

Déchiffrement d’objets MSSQL 2000, 2005 et 2008 via Optillect SQL Decryptor

Au sein de cette jungle d’outil, il y en a un qui sort du lot pour sa simplicité d’exploitation, d’utilisation et ses fonctionnalités. Un autre avantage est qu’il est totalement gratuit. Cet outil est « Optillect SqlDecryptor_v2.0.0 – freeware » (de 2011). Il assure le déchiffrement d’objet de toutes les versions des bases MSSQL sauf de la dernière en date : la 2012 (sortie en avril de la même année). L’éditeur de ce programme ne semble plus actif et aucune mise à jour n’a l’air prévu.

Après son installation, il suffit de lancer l’outil, renseigner le serveur MSSQL auquel se connecter ainsi que les crédentiels associés et la liste des objets devient visible (un MSSQL 2008 est exploité dans cet exemple) :

Connexion via Optillect SQL Decryptor

Connexion via Optillect SQL Decryptor

Il suffit par la suite de choisir l’entité à déchiffrer. Un double-clic permet de visualiser directement le code en clair (avec les commentaires) de l’objet. Un clic droit permet d’accéder aux diverses fonctionnalités sur l’entité, comme son remplacement direct dans la base de données.

Déchiffrement d'un objet via Optillect SQL Decryptor

Déchiffrement d’un objet via Optillect SQL Decryptor

Déchiffrement d’objets MSSQL 2000, 2005, 2008 et 2012 via une procédure stockée

Lorsque les outils disponibles ne permettent pas de déchiffrer des objets d’une version récente (MSSQL Server 2012 notamment), il est possible de définir une procédure manuellement dans la base à ces fins. Cette procédure implémente l’algorithme de déchiffrement complet.

Microsoft a fait évoluer la structure et le schéma de ses tables systèmes entre les versions 2000 (et antérieur) et les versions supérieures à 2000. Toutefois, entre les versions 2005, 2008 et 2012, l’algorithme et la structure des tables n’a pas évolué. Autrement dit, il est possible de déchiffrer n’importe quel objet via une procédure stockée SQL spécifique, quelque soit la version de MSSQL.

Cette procédure pour MSSQL 2005, 2008 et 2012 est la suivante :

/*==================================================================================
 
NAME: Decrypt SQL 2005, 2008 and 2012 stored procedures, functions, views, and triggers
 
DESCRIPTION: HEADS UP: In order to run this script you must log in
to the server in DAC mode: To do so, type
ADMIN:<SQLInstanceName> as your server name and use the "sa"
or any other server admin user with the appropriate password.
 
CAUTION! DAC (dedicated admin access) will kick out all other
server users.
 
The script below accepts an object (schema name + object name)
that were created using the WITH ENCRYPTION option and returns
the decrypted script that creates the object. This script
is useful to decrypt stored procedures, views, functions,
and triggers that were created WITH ENCRYPTION.
 
The algorithm used below is the following:
1. Check that the object exists and that it is encrypted.
2. In order to decrypt the object, the script ALTER (!!!) it
and later restores the object to its original one. This is
required as part of the decryption process: The object
is altered to contain dummy text (the ALTER uses WITH ENCRYPTION)
and then compared to the CREATE statement of the same dummy
content.
 
Note: The object is altered in a transaction, which is rolled
back immediately after the object is changed to restore
all previous settings.
 
3. A XOR operation between the original binary stream of the
enrypted object with the binary representation of the dummy
object and the binary version of the object in clear-text
is used to decrypt the original object.
 
USER PARAMETERS: @ObjectOwnerOrSchema
@ObjectName
 
RESULTSET: NA
 
RESULTSET SORT: NA
 
USING TABLES/VIEWS: sys.sysobjvalues
syscomments
 
REVISIONS
 
DATE DEVELOPER DESCRIPTION OF REVISION VERSION
========= =============== ================================= ===========
01/01/2007 Omri Bahat Initial release 1.00
 
01/18/2013 Yann CAM Test script on MSSQL 2008 and 2012
==================================================================================*/
 
DECLARE @ObjectOwnerOrSchema NVARCHAR(128)
DECLARE @ObjectName NVARCHAR(128)
 
SET @ObjectOwnerOrSchema = 'dbo'
SET @ObjectName = 'helloWorld'
 
DECLARE @i INT
DECLARE @ObjectDataLength INT
DECLARE @ContentOfEncryptedObject NVARCHAR(MAX)
DECLARE @ContentOfDecryptedObject NVARCHAR(MAX)
DECLARE @ContentOfFakeObject NVARCHAR(MAX)
DECLARE @ContentOfFakeEncryptedObject NVARCHAR(MAX)
DECLARE @ObjectType NVARCHAR(128)
DECLARE @ObjectID INT
 
SET NOCOUNT ON
 
SET @ObjectID = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')
 
-- Check that the provided object exists in the database.
IF @ObjectID IS NULL
BEGIN
RAISERROR('The object name or schema provided does not exist in the database', 16, 1)
RETURN
END
 
-- Check that the provided object is encrypted.
IF NOT EXISTS(SELECT TOP 1 * FROM syscomments WHERE id = @ObjectID AND encrypted = 1)
BEGIN
RAISERROR('The object provided exists however it is not encrypted. Aborting.', 16, 1)
RETURN
END
 
-- Determine the type of the object
IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'PROCEDURE') IS NOT NULL
SET @ObjectType = 'PROCEDURE'
ELSE
IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'TRIGGER') IS NOT NULL
SET @ObjectType = 'TRIGGER'
ELSE
IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'VIEW') IS NOT NULL
SET @ObjectType = 'VIEW'
ELSE
SET @ObjectType = 'FUNCTION'
 
-- Get the binary representation of the object- syscomments no longer holds
-- the content of encrypted object.
SELECT TOP 1 @ContentOfEncryptedObject = imageval
FROM sys.sysobjvalues
WHERE objid = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')
AND valclass = 1 and subobjid = 1
 
SET @ObjectDataLength = DATALENGTH(@ContentOfEncryptedObject)/2
 
-- We need to alter the existing object and make it into a dummy object
-- in order to decrypt its content. This is done in a transaction
-- (which is later rolled back) to ensure that all changes have a minimal
-- impact on the database.
SET @ContentOfFakeObject = N'ALTER ' + @ObjectType + N' [' + @ObjectOwnerOrSchema + N'].[' + @ObjectName + N'] WITH ENCRYPTION AS'
 
WHILE DATALENGTH(@ContentOfFakeObject)/2 < @ObjectDataLength
BEGIN
IF DATALENGTH(@ContentOfFakeObject)/2 + 4000 < @ObjectDataLength
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', 4000)
ELSE
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - (DATALENGTH(@ContentOfFakeObject)/2))
END
 
-- Since we need to alter the object in order to decrypt it, this is done
-- in a transaction
SET XACT_ABORT OFF
BEGIN TRAN
 
EXEC(@ContentOfFakeObject)
 
IF @@ERROR <> 0
ROLLBACK TRAN
 
-- Get the encrypted content of the new "fake" object.
SELECT TOP 1 @ContentOfFakeEncryptedObject = imageval
FROM sys.sysobjvalues
WHERE objid = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')
AND valclass = 1 and subobjid = 1
 
IF @@TRANCOUNT > 0
ROLLBACK TRAN
 
-- Generate a CREATE script for the dummy object text.
SET @ContentOfFakeObject = N'CREATE ' + @ObjectType + N' [' + @ObjectOwnerOrSchema + N'].[' + @ObjectName + N'] WITH ENCRYPTION AS'
 
WHILE DATALENGTH(@ContentOfFakeObject)/2 < @ObjectDataLength
BEGIN
IF DATALENGTH(@ContentOfFakeObject)/2 + 4000 < @ObjectDataLength
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', 4000)
ELSE
SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - (DATALENGTH(@ContentOfFakeObject)/2))
END
 
SET @i = 1
 
--Fill the variable that holds the decrypted data with a filler character
SET @ContentOfDecryptedObject = N''
 
WHILE DATALENGTH(@ContentOfDecryptedObject)/2 < @ObjectDataLength
BEGIN
IF DATALENGTH(@ContentOfDecryptedObject)/2 + 4000 < @ObjectDataLength
SET @ContentOfDecryptedObject = @ContentOfDecryptedObject + REPLICATE(N'A', 4000)
ELSE
SET @ContentOfDecryptedObject = @ContentOfDecryptedObject + REPLICATE(N'A', @ObjectDataLength - (DATALENGTH(@ContentOfDecryptedObject)/2))
END
 
WHILE @i <= @ObjectDataLength
BEGIN
--xor real & fake & fake encrypted
SET @ContentOfDecryptedObject = STUFF(@ContentOfDecryptedObject, @i, 1,
NCHAR(
UNICODE(SUBSTRING(@ContentOfEncryptedObject, @i, 1)) ^
(
UNICODE(SUBSTRING(@ContentOfFakeObject, @i, 1)) ^
UNICODE(SUBSTRING(@ContentOfFakeEncryptedObject, @i, 1))
)))
 
SET @i = @i + 1
END
 
-- PRINT the content of the decrypted object in chunks of 2K characters
 
SET @i = 0
 
WHILE DATALENGTH(@ContentOfDecryptedObject)/2 > (@i + 1)*2000
BEGIN
PRINT(SUBSTRING(@ContentOfDecryptedObject, 1 + 2000*@i, 2000*(@i + 1)))
SET @i = @i + 1
END
 
-- Now print the last chunk, or the only chunk
-- (if @ContentOfDecryptedObject does not exceed 2K characters)
PRINT(SUBSTRING(@ContentOfDecryptedObject, 1 + 2000*@i, 2000*(@i + 1)))

Cette procédure, développée par Omri Bahat et publiée le 01/01/2007 est toujours fonctionnelle pour les dernières versions de Microsoft SQL Server (2005, 2008 et 2012).

Il est nécessaire de se connecter sous un compte d’administration au client SSMS pour exécuter cette procédure, via une connexion DAC (Dedicated Administrator Connection). Une telle connexion est possible bien que d’autres connexions soient déjà établies. Seule une connexion DAC est possible à la fois. Par défaut, les connexions DAC sont autorisées qu’en local ; pour les activer à distance, exécuter les commandes suivantes dans un terminal « en tant qu’administrateur » sur la machine accueillant le serveur SQL :

sqlcmd
sp_configure 'remote admin connections', 1;
GO
RECONFIGURE;
GO
Activation de l'accès DAC distant

Activation de l’accès DAC distant

Pour ouvrir une connexion via le client SSMS en tant qu’administrateur DAC, faire « File > New > Database Engine Query ». Renseigner les crédentiels nécessaires à la connexion tout en préfixant le « Server name » de « admin: » (la casse n’a pas d’importance) :

Préfixer le "Server name" de "admin:" pour une connexion DAC

Préfixer le « Server name » de « admin: » pour une connexion DAC

Un nouvel onglet d’exécution de requête apparaît. Celui-ci dispose des privilèges d’administration DAC. Copier/coller la procédure de déchiffrement d’objet MSSQL précédente, et modifier en amont de cette procédure le nom de l’objet à déchiffrer (fonction, procédure, trigger ou vue). Enfin, cliquer sur « Execute » et le code en clair de l’objet apparaît en tant que résultat :

Déchiffrement d'objet MSSQL Server 2012 via une procédure stockée

Déchiffrement d’objet MSSQL Server 2012 via une procédure stockée

L’objet déchiffré contient toujours les mots clés « WITH ENCRYPTION ». Il suffit de supprimer ces mots clé et de ré-exécuter le code d’altération (ALTER) de l’objet pour le ré-enregistrer en clair dans la base.

L’archive suivante [Stored_procedures_to_decrypt_MSSQL_object_2000-2005-2008-2012], issue initialement de l’article très complet de Omri Bahat, comprend tous les fichiers .SQL des procédures de déchiffrement quelque soit la version MSSQL en place.

Analyse de l’algorithme de déchiffrement

Le stockage des différents objets MSSQL (dans les versions supérieures ou égales à 2005) se fait dans la colonne « imageval » de la table « sys.sysobjvalues ». Cette table est protégée lors d’une connexion simple via SSMS à la base, même sous un compte d’administration. C’est pourquoi il est nécessaire de faire une connexion DAC.

L’algorithme de chiffrement effectue un simple XOR sur le code SQL de l’objet à protéger avant de le stocker dans cette table.

La technique de déchiffrement qui est utilisée dans la procédure précédente est la suivante :

  1. Récupération des données chiffrées de définition de l’objet (à partir de son identifiant unique), dans la colonne « imageval » de la table « sys.sysobjvalues » et stockage de cette valeur dans une variable « @ContentOfEncryptedObject ».
  2. Calcul de la taille de l’objet dans « @ContentDataLength » via la fonction « DATALENGTH(@ContentOfEncryptedObject)/2 » (stockage en hexadécimal, d’où le /2).
  3. Création d’une instruction « ALTER PROCEDURE » complétée par le caractère de commentaire MSSQL « – » jusqu’à la taille de l’objet définie précédemment. Exemple : « ALTER PROCEDURE [dbo].[helloWorld] WITH ENCRYPTION AS———–[…] »
  4. Exécution de l’instruction d’altération de la procédure. Ceci à pour effet de supprimer la procédure effective précédente, que l’on cherche à déchiffrer. Cette procédure a été sauvegardée dans « @ContentOfEncryptedObject ».
  5. Récupération de la nouvelle procédure redéfinie (complétée avec des « -« ) dans la variable « @ContentOfFakeEncryptedObject ».
  6. Annulation de la modification de la procédure (RollBack), pour restaurer la version chiffrée ciblée (toutefois la fausse procédure a été stockée dans « @ContentOfFakeEncryptedObject »).
  7. Création d’une instruction « CREATE PROCEDURE » complétée par le caractère de commentaire MSSQL « – » jusqu’à la taille de l’objet « @ContentDataLength ». Exemple : « CREATE PROCEDURE [dbo].[helloWorld] WITH ENCRYPTION AS—————[…] » et stockage de cette instruction dans la variable « @ContentFakeObject »

Une fois les différentes variables initialisées, l’algorithme de décodage via un « OU exclusif » (XOR) peut être appliqué. Celui-ci est réalisé caractère par caractère en fonction de la taille de objet définie (@i = 1 to @ObjectDataLength), entre les données de la procédure chiffrée cible, les données de la fausse procédure chiffrée et les données de l’instruction de création de la procédure (en clair). Soit :

NCHAR(
UNICODE(SUBSTRING(@ContentOfEncryptedObject, @i, 1)) ^
(
UNICODE(SUBSTRING(@ContentOfFakeObject, @i, 1)) ^
UNICODE(SUBSTRING(@ContentOfFakeEncryptedObject, @i, 1))
)
)

L’idée est de définir la clé du XOR à partir des deux « fausses » procédures ; puis d’utiliser cette clé sur la véritable procédure cible. Le résultat est le code (commenté) de l’objet en clair.

Renforcement de sécurité

Pour renforcer les mécanismes de protection intégrés à MSSQL, différentes solutions existent.  On peut citer l’une d’entre elle, nommée « {3S} SQL Smart Security ».

Cette solution est un add-in de « Microsoft SQL Server Management Studio » (SSMS) pour les versions 2005, 2008, 2008 R2, 20012 et leurs versions EXPRESS respectives. La version actuelle de ce produit (1.1) ne traite que les procédures stockées. L’éditeur prévoit à l’avenir de l’étendre aux fonctions, déclencheurs et aux vues.

L’idée de cette solution est d’enrichir et de renforcer le mécanisme intrinsèque à MSSQL « WITH ENCRYPTION » via de nouveaux mécanismes cryptographiques. A surveiller pour l’avenir 😉 !

Sources & ressources :

  • Google Plus
  • LinkedIn
  • Viadeo
Yann C.

About the Author : Yann C.

Consultant en sécurité informatique et s’exerçant dans ce domaine depuis le début des années 2000 en autodidacte par passion, plaisir et perspectives, il maintient le portail ASafety pour présenter des articles, des projets personnels, des recherches et développements, ainsi que des « advisory » de vulnérabilités décelées notamment au cours de pentest.