The algorithm is very simple and easy to guess. Once you realise that the SHA1 hash stored in SYS.USER$.SPARE4 is too long. I mentioned this before here in this blog. The SHA1 verifier should be 160 bits but the spare4 column holds S: and a further 240 bits of hash. It was clear from some investigations that the password function used a salt and also it was obvious that the salt is stored also in SPARE4. the data is even prepended by S:. It then becomes a simple guess as to whether the algorithm is SHA1(userpwdsalt), SHA1(saltuserpwd), SHA1(pwdsalt) or SHA1(saltpwd) a few experiements with PL/SQL and DBMS_CRYPTO reveals the result.
The algorithm is simple. This can be described as:
sys.user$spare4 = SHA1(pwd concat with salt) contat with salt
or to test
substr(spare4,3,40) = SHA1(pwd concat with substr(spare4,43,10)
Here is a simple test case to show how to verify the Oracle 11gR1 password algorithm:
Connected to: Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production With the Partitioning, OLAP, Data Mining and Real Application Testing options SQL> SQL> connect sys@ora11g as sysdba Enter password: ****** Connected. SQL> create user g identified by g; User created. SQL> @c:\11g_notes\sha1.sql NAME OF USER TO CHECK [system]: g PWD to test [manager]: g PWD found PL/SQL procedure successfully completed. SQL> |
Then try an invalid password:
SQL> @c:\11g_notes\sha1.sql NAME OF USER TO CHECK [system]: g PWD to test [manager]: G PWD not found PL/SQL procedure successfully completed. SQL> |
OK, the simple algorithm works. the code in sha1.sql is:
SQL> get c:\11g_notes\sha1.sql 1 set feed on 2 set head on 3 set arraysize 1 4 set space 1 5 set verify off 6 set pages 25 7 set lines 80 8 set termout on 9 set serveroutput on size 1000000 10 undefine user_to_find 11 undefine pwd_guess 12 accept user_to_find char prompt 'NAME OF USER TO CHECK [system]: ' default system 13 accept pwd_guess char prompt 'PWD to test [manager]: ' default manager 14 DECLARE 15 lv_pwd_raw RAW(128); 16 lv_enc_raw RAW(2048); 17 lv_hash_found varchar2(300); 18 cursor c_main(cp_user in varchar2) is 19 select substr(spare4,3,40) hash, 20 substr(spare4,43,20) salt, 21 spare4 22 from sys.user$ 23 where name=cp_user; 24 lv_user c_main%rowtype; 25 BEGIN 26 open c_main(upper('&&user_to_find')); 27 fetch c_main into lv_user; 28 close c_main; 29 lv_pwd_raw:= utl_raw.cast_to_raw('&&pwd_guess')||hextoraw(lv_user.salt); 30 lv_enc_raw := sys.dbms_crypto.hash(lv_pwd_raw, 3); 31 lv_hash_found:=utl_raw.cast_to_varchar2(lv_enc_raw); 32 if lv_enc_raw = lv_user.hash then 33 dbms_output.put_line('PWD found'); 34 else 35 dbms_output.put_line('PWD not found'); 36 end if; 37* END; 38 |
11g brings case sensitivity but a major flaw exists in that the older 10gR2 and lower algorithm is also still used and the SYS.USER$.PASSWORD column still contains the lower version case insensitive password. As I have discussed here recently there have been improvements to protect the hashes but if the SHA1 hash is available then the older one is also and this makes cracking the passwords via a dictionary or brute force attack easier.
FX has also added a blog entry to his blog titled "Oracle 0xDEADF00D" which details Recurity Labs efforts to crack the Oracle 11g SHA1 Password algorithm using rather more technical methods than I used. its well worth reading how he used IDA Pro, GDB and FindCrypt to locate the function responsible for creating the hash in the Oracle 11g binary on Linux.
September 29th, 2007 at 01:11 pm
Pete Finnigan says:
Check http://freeworld.thc.org/thc-orakelcrackert11g/ for a recently released cracker.