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
Eine Antwort auf „ORACLE: Passwort = Username, unsichere Passwörter ermitteln“