unsicheres Passwort, identisch mit Username oder „meistgenutzt“ (auf Liste)
Sicherheitslücke: User mit leicht zu erratenden Passwort
Ein weiterer Aspekt, der es potentiellen Angreifern leichter macht ist der Fall, dass ein User sein Passwort als Usernamen gewählt hat.
Wir stellen also ein einfaches Script vor, das die User ermittelt die in diese Sicherheitsklasse fallen.
Wir gehen von der Annahme aus, der User hätte Username = Passwort.
Schritte zu unserer Lösung:
- Loopen über alle DB-User
- hashen unseres angenommen Passworts nach der Standardmethode
- ziehen des gespeicherten Hashs
- compare. Bei Treffer, Listung des Users
Um einen Testfall zu haben, machen wir folgendes, ausgehend von User SCOTT, oder einem neu anzulegenden User TEST, um einen Testfall für das Script zu haben:
ALTER USER SCOTT IDENTIFIED BY SCOTT;
Wir lassen unser Script laufen:
SCOTT is UNSAFE PWD guessed IS SCO******
TEST is UNSAFE PWD guessed IS TES******
PL/SQL-Prozedur erfolgreich abgeschlossen.
Oracle Passwörter sind jedoch nicht mehr Case-Insensitiv.
Was ist, wenn des Users Passwort „Scott“ lautet? Es sind immerhin nur wenige Versuche für den potentiellen Angreifer, die KOmbinationen SCOTT, Scott sowie scott durchzuprobieren. Wir testen das aus:
ALTER USER SCOTT IDENTIFIED BY Scott;
Ergebnis:
SCOTT is UNSAFE PWD guessed IS Sco******
TEST is UNSAFE PWD guessed IS TES******
PL/SQL-Prozedur erfolgreich abgeschlossen.
Unser Script findet das auch! Natürlich haben wir das schon bedacht, obige Steps 2-4 erfolgen jeweils für die Varianten „angenommenes Passwort Username“ in den beschriebenen drei Varianten.
- Loopen über alle DB-User
- Erstelle Passwort wie Username in Lowercase ( jump to 5)
- Erstelle Passwort wie Username in Uppercase ( jump to 5)
- Erstelle Passwort wie Username in Mixedcase (Capitals) ( jump to 5)
- hashen unseres angenommen Passworts nach der Standardmethode
- ziehen des gespeicherten Hashs
- compare. Bei Treffer, Listung des Users.
- jump back – next step
Wie sieht das script aus? Hier haben wir es:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| SET serveroutput ON size 1000000
DECLARE
unsecure BOOLEAN;
DUMMY VARCHAR2(80);
c_username VARCHAR2(256);
chk_token VARCHAR2(256);
dbname v$database.name%TYPE;
lv_pwd_raw RAW(128);
lv_enc_raw RAW(2048);
LV_HASH_FOUND VARCHAR2(300);
CURSOR c_main(cp_user IN VARCHAR2)
IS
SELECT SUBSTR(spare4,3,40) hash,
SUBSTR(spare4,43,20) salt,
spare4
FROM sys.USER$
WHERE name=cp_user;
lv_user c_main%ROWTYPE;
CURSOR cu_user
IS
SELECT username
FROM dba_users
WHERE ACCOUNT_STATUS LIKE 'OPEN%';
FUNCTION COMPARE_PW_HASH(luser IN VARCHAR2, lpw IN VARCHAR2)
RETURN NUMBER IS
BEGIN
OPEN c_main(UPPER(luser));
FETCH c_main INTO lv_user;
CLOSE C_MAIN;
lv_pwd_raw := UTL_RAW.cast_to_raw(lpw)||HEXTORAW(lv_user.salt);
lv_enc_raw := sys.dbms_crypto.hash(lv_pwd_raw, 3);
lv_hash_found:=UTL_RAW.cast_to_varchar2(lv_enc_raw);
IF LV_ENC_RAW = LV_USER.hash THEN
-- DBMS_OUTPUT.PUT_LINE('PWD found for User ' || luser );
RETURN 1;
ELSE
-- dbms_output.put_line('PWD not found for Safe User ' || luser);
RETURN 0;
END IF;
END;
BEGIN
DBMS_OUTPUT.ENABLE(10000000);
SELECT NAME INTO dbname FROM v$database;
--DBMS_OUTPUT.PUT_LINE('Start Checking DB ' || dbname);
--DBMS_OUTPUT.PUT_LINE('');
OPEN cu_user;
LOOP
FETCH cu_user INTO c_username;
EXIT WHEN cu_user%NOTFOUND;
unsecure := FALSE;
IF COMPARE_PW_HASH ( c_username , c_username ) > 0 THEN
unsecure := TRUE;
chk_token := c_username;
ELSIF COMPARE_PW_HASH ( c_username , UPPER(c_username) ) > 0 THEN
unsecure := TRUE;
chk_token := UPPER ( c_username) ;
ELSIF COMPARE_PW_HASH ( c_username , UPPER(SUBSTR(c_username,1,1))
|| LOWER(SUBSTR(c_username,2,LENGTH(c_username)))
) > 0 THEN
unsecure := TRUE;
chk_token := UPPER(SUBSTR(c_username,1,1))
|| LOWER(SUBSTR(c_username,2,LENGTH(c_username)));
ELSIF COMPARE_PW_HASH ( c_username , '123456' ) > 0 THEN
unsecure := TRUE;
chk_token := '123456';
END IF;
IF unsecure THEN
DBMS_OUTPUT.PUT_LINE( c_username || ' is UNSAFE PWD guessed IS '
|| SUBSTR(chk_token,1,3) || '******' );
ELSE
--DBMS_OUTPUT.PUT_LINE(c_username );
NULL;
END IF;
END LOOP;
CLOSE cu_user;
--
END; |
Prüfung auf beliebte, damit unsichere Passwörter
Jetzt verfeinern wir das Script noch um einen kleinen Aspekt. Erstaunlicherweise gibt es tatsächlich „beliebte Passwörter“.
Hier Artikel zu dem Thema:
https://www.com-magazin.de/news/sicherheit/25-unsichersten-passwoerter-2015-1072804.html
http://www.maclife.de/news/diese-passwoerter-sollten-keinen-fall-verwenden-10086942.html
Passwort Hitliste weltweit 2015:
- 123456
- password
- 12345678
- qwerty
- …. (siehe Link)
Das heisst, Passwörter wie „123456“ sollten wir auch nicht verwenden.
Dieses Passwort verwenden laut dieser Untersuchungen signifikant viele Personen, daher wären es für den potentiellen Angreifer auch nur einige wenige Versuche die ersten 20% Wahrscheinlichkeit des benutzten Passwortes abzuchecken. Daher nehmen wir diese in eine erweitere Variante des Scriptes auf. Die eingebaute Liste der Passwörter ist natürlich nachzupflegen. In der Luxusvariante läge sie in einer Tabelle, in der mindestens jährlich die neuesten Passworthits eingepflegt werden, uns reicht hier für das Prinzip jedoch eine fest verdrahtete Liste.
Wir testen das:
ALTER USER SCOTT IDENTIFIED BY 123456;
Ausgabe des neuen Scriptes:
SCOTT is UNSAFE PWSTYLE IS 4 **
TEST is UNSAFE PWSTYLE IS 1 **
PL/SQL-Prozedur erfolgreich abgeschlossen.
Was hat sich geändert? Nun, es wird nicht mehr das Passwort , auch nicht dessen Anfang, im Klartext wiederholt, auch der Admin muss das gar nicht wissen. Es reicht die Kategorisierung in eine Fehlerklasse.
SCOTT fällt jetzt in die Fehlerklasse 4. Ein zu implementierender Automatismus könnte ihn also anmailen, oder der Admin ansprechen und darauf hinweisen: „Lieber User SCOTT, du hast ein unsicheres Passwort. Dieses steht auf der Liste der weltweit am meisten genutzten Passwörter. Bitte denke dir aus Sicherheitsgründen, zu unsere aller Beruhigung, ein neues Passwort aus.„.
Kategorien der Fehlerklassen:
- Passwort wie username (in Lowercase )
- Passwort wie USERNAME (in Uppercase )
- Passwort wie Username (in Mixedcase (Capitals) )
- befindet sich in Hitliste der beliebtesten Passwörter
Hier noch zu guter Letzt das erweiterte Script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| SET serveroutput ON size 1000000
DECLARE
TYPE string_list IS VARRAY(40) OF VARCHAR(16);
pw_list STRING_LIST := string_list(
'123456','password','12345678','qwerty','12345','123456789','football',
'1234','1234567','baseball','welcome','1234567890','abc123','111111',
'1qaz2wsx','dragon','master','monkey','letmein','login','princess',
'qwertyuiop', 'solo', 'passw0rd', 'starwars', 'hallo', 'passwort',
'hallo123', 'schalke04', 'passwort1', 'qwertz', 'schatz', 'hallo1',
'fussball', NULL, NULL, NULL,NULL,NULL,NULL ); -- hier Erweiterungen eintragen
pwstyle PLS_INTEGER;
DUMMY VARCHAR2(80);
c_username VARCHAR2(256);
dbname v$database.name%TYPE;
lv_pwd_raw RAW(128);
lv_enc_raw RAW(2048);
LV_HASH_FOUND VARCHAR2(300);
CURSOR c_main(cp_user IN VARCHAR2)
IS
SELECT SUBSTR(spare4,3,40) hash,
SUBSTR(spare4,43,20) salt,
spare4
FROM sys.USER$
WHERE name=cp_user;
lv_user c_main%ROWTYPE;
CURSOR cu_user
IS
SELECT username
FROM dba_users
WHERE ACCOUNT_STATUS LIKE 'OPEN%';
FUNCTION COMPARE_PW_HASH(luser IN VARCHAR2, lpw IN VARCHAR2)
RETURN NUMBER IS
BEGIN
OPEN c_main(UPPER(luser));
FETCH c_main INTO lv_user;
CLOSE C_MAIN;
lv_pwd_raw := UTL_RAW.cast_to_raw(lpw)||HEXTORAW(lv_user.salt);
lv_enc_raw := sys.dbms_crypto.hash(lv_pwd_raw, 3);
lv_hash_found:=UTL_RAW.cast_to_varchar2(lv_enc_raw);
IF LV_ENC_RAW = LV_USER.hash THEN
-- DBMS_OUTPUT.PUT_LINE('PWD found for User ' || luser );
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
BEGIN
DBMS_OUTPUT.ENABLE(10000000);
SELECT NAME INTO dbname FROM v$database;
OPEN cu_user;
LOOP
FETCH cu_user INTO c_username;
EXIT WHEN cu_user%NOTFOUND;
pwstyle := 0;
IF COMPARE_PW_HASH ( c_username , c_username ) > 0 THEN
pwstyle := 1; -- PW = username
ELSIF COMPARE_PW_HASH ( c_username , UPPER(c_username) ) > 0 THEN
pwstyle := 2; -- PW = UPPER(USERNAME)
ELSIF COMPARE_PW_HASH ( c_username , UPPER(SUBSTR(c_username,1,1))
|| LOWER(SUBSTR(c_username,2,LENGTH(c_username)))
) > 0 THEN
pwstyle := 3; -- PW = Username
ELSE
FOR i IN 1..36 LOOP
IF COMPARE_PW_HASH ( c_username , pw_list(i) ) > 0 THEN
pwstyle := i+3; -- PW ist PW(i) aus Liste der meistgenutzten
EXIT;
END IF;
END LOOP;
END IF;
IF pwstyle > 0 THEN
DBMS_OUTPUT.PUT_LINE( c_username || ' is UNSAFE PWSTYLE IS ' || PWSTYLE || ' **' );
END IF;
END LOOP;
CLOSE cu_user;
--
END;
/ |
Wissen zum Thema:
https://www.experts-exchange.com/articles/855/How-Oracle-Stores-Passwords.html