In diesem Tipp zeigen wir eine Möglichkeit zur Bewertung verschiedener potentieller Gefahren in der Oracle Datenbank, welche individualisiert überprüft werden können. Da wären SQL Injection-anfällige Eingaben oder auch die Gefährlichkeit der Rechte bzw. Rollen von Oracle Datenbanknutzern.
SQL INJECTION
Um SQL Injection-Angriffe abzuwehren, bietet Oracle das Package dbms_assert welches Eingaben bzw. Strings auf Gültigkeit überprüft. Möchte man allerdings eine Unterscheidung bzw. Einschätzung der potentiellen Gefahr haben, hilft einem dieses Package nicht weiter. Deshalb erstellen wir die Funktion "get_score", die einen zu überprüfenden String entgegennimmt. Zurückgegeben wird ein Scoring-Wert.
CREATE OR REPLACE FUNCTION get_score (p_text IN VARCHAR2) RETURN NUMBER
IS
TYPE wordsArray IS TABLE OF VARCHAR2(64);
l_words wordsArray := wordsArray(
'WITH','SELECT','FROM','WHERE','LIKE','IN','UNION','OR','DUAL','CREATE',
'ALTER','DROP','GATHER','STATS','EXECUTE','GRANT','HOST','TABLE',
'TABLESPACE','DATABASE','INDEX','TRIGGER','REPLACE','DELETE','FUNCTION',
'PROCEDURE','BEGIN','RETURN','END','FOR','LOOP','IF','THEN','SYS','DBMS',
'UTL','DBA','ALL','PRIVS','WWV','v\$','SDO','UTIL','CHECK','SECURITY',
'MDSYS','JAVA',';','--','(1)=\1','[;].[--]');
v_pattern VARCHAR2(32700);
v_score NUMBER:=0;
BEGIN
FOR i IN 1 .. l_words.count LOOP
v_pattern:=v_pattern||'|'||l_words(i);
END LOOP;
v_score:=v_score+REGEXP_COUNT(p_text,LTRIM(v_pattern,'|'),1,'i');
RETURN v_score;
END;
Innerhalb dieser Funktion legen wir uns ein Array mit allen Schlüsselwörter an, die als verdächtig erachtet werden. Da "REGEXP_COUNT" nur ein Schlüsselwort erwartet, konkatenieren wir die Schlüsselwörter jeweils mit einem Pipe "|". Der Parameter "1" legt den ersten Buchstaben als Start fest und "i" beachtet keinen Unterschied zwischen Groß- und Kleinschreibung. Sobald "REGEXP_COUNT" einen Match gefunden hat, wird der Score um eins erhöht.
Die Scoring-Abfrage auf einen typischen SQL Injection-String sieht folgend aus:
select GET_SCORE('falscher_nutzer'' OR 1=1 ; DROP TABLE users --') as score
from dual;
SCORE
----------
6
Hinweis: Oracle kann nur einen Befehl dynamisch ausführen, der DROP TABLE würde sowieso nicht funktionieren. Aber manche Hacker gehen von SQL Server oder MYSQL Datenbanken aus, da geht so etwas.
Folgende Schlüsselwort wurden also abgefangen: "OR", "1=1", "DROP", "TABLE", ";", "--"
Regex aus dem oberen Beispiel für Schlüsselwörter erklärt:
"(1)=\1": Wahrheitsvergleich 1=1 der sehr beliebt ist
"[;].[--]": Typisches Ende einer SQL Injection: ";--" oder auch "; DROP TABLE wichtig -------"
Da das Thema Reguläre Ausdrücke sehr komplex ist, verweisen wir auf eine gute Stelle im Internet: Opens external link in new windowhttp://www.sqlsnippets.com/en/topic-10764.html
RECHTE & ROLLEN
Das oben beschriebene Prinzip bleibt das selbe:
In ein Array speichern wir potentiell gefährliche Rechte, die wir abfragen möchten. Die Funktion bekommt einen Benutzernamen übergeben, den es zu überprüfen gilt, und zurückgegeben wird ein Scoring-Wert. Es werden nicht nur direkte Rechte abgefragt, sondern auch Rechte die über eine Rolle erteilt wurden.
CREATE OR REPLACE PACKAGE priv_check IS
/* Notwendige Rechte:
grant select on sys.sysauth$ to system;
grant select on sys.user$ to system;
grant select on sys.system_privilege_map to system;
grant select on sys.DBA_ROLE_PRIVS to system;
*/
FUNCTION get_syspriv_score (p_user IN VARCHAR2) RETURN NUMBER;
END;
/
CREATE OR REPLACE PACKAGE BODY priv_check IS
FUNCTION get_syspriv_score (p_user IN VARCHAR2) RETURN NUMBER
IS
TYPE privArray IS TABLE OF VARCHAR2(64);
l_privs privArray := privArray(
'ALTER ANY LIBRARY',
'ALTER DATABASE',
'ALTER USER',
'BECOME USER',
'CREATE ANY LIBRARY',
'CREATE EXTERNAL JOB',
'CREATE LIBRARY',
'CREATE USER',
'DROP ANY INDEX',
'DROP ANY LIBRARY',
'DROP ANY PROCEDURE',
'DROP ANY ROLE',
'DROP ANY SYNONYM',
'DROP ANY TABLE',
'DROP TABLESPACE',
'DROP ANY TRIGGER',
'DROP ANY VIEW',
'DROP PUBLIC DATABASE LINK',
'DROP USER',
'EXECUTE ANY LIBRARY',
'EXEMPT ACCESS POLICY',
'EXEMPT IDENTITY POLICY',
'EXPORT FULL DATABASE',
'GRANT ANY PRIVILEGE',
'GRANT ANY OBJECT PRIVILEGE',
'IMPORT FULL DATABASE',
'SYSDBA',
'SYSOPER',
'SYSBACKUP'
);
v_score NUMBER:=0;
begin
-- Direkte Rechte
FOR c IN (
select
u.name username,
spm.name privilege
from
sys.user$ u,
sys.sysauth$ s,
sys.system_privilege_map spm
where
u.user#=s.grantee# and
s.privilege#=spm.privilege and
U.NAME=p_user
)
LOOP
FOR i IN 1 .. l_privs.count LOOP
IF l_privs(i)= c.privilege THEN
v_score:=v_score+1;
END IF;
END LOOP;
END LOOP;
-- Rechte via Rollen
FOR c IN (
SELECT
u1.name USERNAME,
U2.NAME ROLENAME,
SUBSTR(SPM.NAME,1,27) PRIVILEGE
FROM
SYS.SYSAUTH$ SA1,
SYS.SYSAUTH$ SA2,
SYS.USER$ U1,
SYS.USER$ U2,
SYS.SYSTEM_PRIVILEGE_MAP SPM
WHERE
SA1.GRANTEE# = U1.USER# AND
SA1.PRIVILEGE# = U2.USER# AND
U2.USER# = SA2.GRANTEE# (+) AND
SA2.PRIVILEGE# = SPM.PRIVILEGE (+) AND
(U1.NAME IN (
SELECT
GRANTEE
FROM
sys.DBA_ROLE_PRIVS connect by prior
granted_role=GRANTEE start with GRANTEE IN (
SELECT
NAME
FROM
sys.USER$
WHERE
user# in (
select
privilege#
from
sys.sysauth$ t1,
sys.user$ t2
where
t1.grantee#=t2.user# and
t2.name=p_user
)
)
UNION
SELECT
GRANTED_ROLE
FROM
DBA_ROLE_PRIVS connect by prior
granted_role=GRANTEE start with GRANTEE IN (
SELECT
NAME
FROM
sys.USER$
WHERE
user# in (
select
privilege#
from
sys.sysauth$ t1,
sys.user$ t2
where
t1.grantee#=t2.user# and
t2.name=p_user
)
)
) OR
U1.NAME=p_user
)
)
LOOP
FOR i IN 1 .. l_privs.count LOOP
IF l_privs(i)= c.privilege THEN
v_score:=v_score+1;
END IF;
END LOOP;
END LOOP;
RETURN v_score;
EXCEPTION WHEN OTHERS THEN
RETURN sqlcode;
END get_syspriv_score;
END;
/
Eine Abfrage des Scores selektiert man wie folgt:
SELECT
*
FROM
(
SELECT
username,
priv_check.get_syspriv_score(username) AS score
FROM
dba_users
)
WHERE
score>0
ORDER BY
score DESC;
USERNAME SCORE
--------------------------- ----------
SYS 140
SYSTEM 74
WMSYS 8
APEX_050000 5
APEX_040200 5
DVSYS 4
SPATIAL_CSW_ADMIN_USR 2
SPATIAL_WFS_ADMIN_USR 2
OLAPSYS 2
GSMADMIN_INTERNAL 2
SYSBACKUP 2
MDSYS 2
SYSDG 1
XDB 1
Der administrative Benutzer "SYS" besitzt logischerweise den höchsten Scoring-Wert.
Um überhaupt herauszufinden welche Benutzer direkt oder über eine Rolle solche Rechte besitzen, kann man dies mithilfe von folgendem Statement herausfinden. Der SELECT listet alle betroffenen Benutzer und zeigt diese in hierachischer Struktur an.
SELECT
lpad(' ', 2*level) || c "Privilege, Roles and Users"
FROM (
/* THE PRIVILEGES */
select
null p,
name c
from
system_privilege_map
where
name in (
'DROP TABLESPACE',
'CREATE USER',
'BECOME USER',
'ALTER USER',
'DROP USER',
'DROP ANY TABLE',
'DROP ANY INDEX',
'DROP ANY SYNONYM',
'SYSDBA',
'SYSOPER',
'DROP ANY VIEW',
'DROP PUBLIC DATABASE LINK',
'DROP ANY ROLE',