Changes in Oracle Database 12c password hashes

Oracle has made improvements to user password hashes within Oracle Database 12c. By using a PBKDF2-based SHA512 hashing algorithm, instead of simple SHA1 hash, password hashing is more secure. With this post, I'll explain some of the changes and their security implications.

With Oracle Database 11g, the spare4 column from the sys.user$ table stores user password hashes.

This is an example of the sys.user$.spare4 entry for user 'demo' with password 'epsilon' (pluggable database):

S:8F2D65FB5547B71C8DA3760F10960428CD307B1C6271691FC55C1F56554A;H:DC9894A01797D91D92ECA1DA66242209;T:23D1F8CAC9001F69630ED2DD8DF67DD3BE5C470B5EA97B622F757FE102D8BF14BEDC94A3CC046D10858D885DB656DC0CBF899A79CD8C76B788744844CADE54EEEB4FDEC478FB7C7CBFBBAC57BA3EF22C

Step-by-step:

SQL> create user demo identified by epsilon;

User created.

SQL> select spare4 from sys.user$ where name = 'DEMO';

SPARE4
--------------------------------------------------------------------------------
S:8F2D65FB5547B71C8DA3760F10960428CD307B1C6271691FC55C1F56554A;H:DC9894A01797D91D92ECA1DA66242209;T:23D1F8CAC9001F69630ED2DD8DF67DD3BE5C470B5EA97B622F757FE102D8BF14BEDC94A3CC046D10858D885DB656DC0CBF899A79CD8C76B788744844CADE54EEEB4FDEC478FB7C7CBFBBAC57BA3EF22C

sys.user$.password value for the same user:

SQL> select password from sys.user$ where name = 'DEMO';

PASSWORD
--------------------------------------------------------------------------------
2B7983437FE9FEB6

This will omit the password value discussion: it is calculated using the same algorithm (uppercase and concatenate username and password then do 3DES hashing) as in previous Oracle Database versions.

The spare4 column's value has three parts ("S:", "H:", and "T:") separated by semicolons.

The "S:" part length is 60 characters or 30 bytes:

8F2D65FB5547B71C8DA3760F10960428CD307B1C6271691FC55C1F56554A

The "H:" part length is 32 characters or 16 bytes:

DC9894A01797D91D92ECA1DA66242209

Finally, the "T:" part length is 160 characters or 80 bytes:

23D1F8CAC9001F69630ED2DD8DF67DD3BE5C470B5EA97B622F757FE102D8BF14BEDC94A3CC046D10858D885DB656DC0CBF899A79CD8C76B788744844CADE54EEEB4FDEC478FB7C7CBFBBAC57BA3EF22C

So what do they mean exactly?

The S part

In Oracle Database 11g there is "S:" part and it is created as follows:

password hash (20 bytes) = sha1(password + salt (10 bytes))

(Visit http://marcel.vandewaters.nl/oracle/security/password-hashesfor more detail.)

The same is true of Oracle Database 12c: the simple test below proves that.

For the S value from above (8F2D65FB5547B71C8DA3760F10960428CD307B1C6271691FC55C1F56554A):

hash is 8F2D65FB5547B71C8DA3760F10960428CD307B1C
salt is 6271691FC55C1F56554A

Password is "epsilon", so let's calculate SHA1 hash from 'epsilon' + 0x6271691FC55C1F56554A:

import hashlib
sha1 = hashlib.sha1()
sha1.update("epsilon")
sha1.update('\x62\x71\x69\x1f\xc5\x5c\x1f\x56\x55\x4a')
sha1.hexdigest().upper()

That calculation produces:

'8F2D65FB5547B71C8DA3760F10960428CD307B1C'

This is identical to the 11g algorithm.

The H part

When looking through SQL files under $ORACLE_HOME/rdbms/admin one can spot this:

create or replace view DBA_DIGEST_VERIFIERS
  (USERNAME, HAS_DIGEST_VERIFIERS, DIGEST_TYPE) as
select u.name, 'YES', 'MD5' from user$ u where instr(spare4, 'H:')>0
union
select u.name, 'NO', NULL from user$ u where not(instr(spare4, 'H:')>0) or spare4 is null
/

So it appears to be a MD5 hash.

Note that there is SQL code under $ORACLE_HOME/rdbms/admin that modifies the spare4 column's value to remove the H: on downgrade.

This is how spare4.H is calculated: the username is uppercased, then the MD5 hash is calculated from it, and 'XDB' and password are separated by colons:

import hashlib
m = hashlib.md5()
m.update('DEMO:XDB:epsilon')
m.hexdigest().upper()
'DC9894A01797D91D92ECA1DA66242209'

This makes it possible to attack built-in user passwords using pre calculated hashes for dictionary words prefixed with constants like 'SYSTEM:XDB:'.

The H value seems to be used for digest authentication in XDB.

The T part

This applies to 12.1.0.2 only. For previous 12c versions the T part is not available.

Let's enable 12c passwords hashes only by updating the sqlnet.ora file (assuming the client is from 12.1.0.2 distribution too):

# sqlnet.ora
SQLNET.ALLOWED_LOGON_VERSION_SERVER = 12a

Then re-create the demo user (reconnect the client first):

drop user demo;
create user demo identified by epsilon;
select spare4 from sys.user$ where name = 'DEMO';

H:DC9894A01797D91D92ECA1DA66242209;T:E3243B98974159CC24FD2C9A8B30BA62E0E83B6CA2FC7C55177C3A7F82602E3BDD17CEB9B9091CF9DAD672B8BE961A9EAC4D344BDBA878EDC5DCB5899F689EBD8DD1BE3F67BFF9813A464382381AB36B

Note that the spare4 value no longer has the S: part, only the H: and T: components are there.

In Oracle Database 12c documentation we can find this:

About the 12C Verifier
... is based on a de-optimized algorithm involving PBKDF2 and SHA512...

So the password should be processed via PBKDF2 followed by SHA512 to produce T.

During authentication the server sends so called AUTH_VFR_DATA (which matches the last 16 bytes of the spare4.T value) to the client:

-- Server to client packet snippet
39 39 39 00 00 00 00 0D-00 00 00 0D 41 55 54 48 999.........AUTH
5F 56 46 52 5F 44 41 54-41 20 00 00 00 20 38 44 _VFR_DATA.....8D
44 31 42 45 33 46 36 37-42 46 46 39 38 31 33 41 D1BE3F67BFF9813A
34 36 34 33 38 32 33 38-31 41 42 33 36 42 15 48 464382381AB36B.H

So we can divide the T value into two parts (first 64 bytes and the AUTH_VFR_DATA):

E3243B98974159CC24FD2C9A8B30BA62E0E83B6CA2FC7C55177C3A7F82602E3BDD17CEB9B9091CF9DAD672B8BE961A9EAC4D344BDBA878EDC5DCB5899F689EBD (first 128 chars or 64 bytes)
8DD1BE3F67BFF9813A464382381AB36B (last 32 chars or 16 bytes - AUTH_VFR_DATA)

Let's assume that the AUTH_VFR_DATA is randomly generated when a password is set/reset. Thus Python code to produce the first 64 bytes of T is (requires PBKDF2 Python module):

import pbkdf2, hashlib
AUTH_VFR_DATA = b'\x8d\xd1\xbe\x3f\x67\xbf\xf9\x81\x3a\x46\x43\x82\x38\x1a\xb3\x6b' # This is received from the server once the latest protocol is negotiated
salt = AUTH_VFR_DATA + b'AUTH_PBKDF2_SPEEDY_KEY'
key = pbkdf2.PBKDF2("epsilon", salt, 4096, hashlib.sha512) # Password
key_64bytes = key.read(64) # This 64-byte derived key is encrypted by the client and sent to the server as AUTH_PBKDF2_SPEEDY_KEY

t = hashlib.sha512() # This happens on the server after they key is decrypted from the AUTH_PBKDF2_SPEEDY_KEY value
t.update(key_64bytes)
t.update(AUTH_VFR_DATA)
t.hexdigest().upper() # First 64 bytes of spare4.T: value if password is correct

This produces:

E3243B98974159CC24FD2C9A8B30BA62E0E83B6CA2FC7C55177C3A7F82602E3BDD17CEB9B9091CF9DAD672B8BE961A9EAC4D344BDBA878EDC5DCB5899F689EBD

Summing up

Oracle has added MD5 hash and PBKDF2-based SHA512 hash in 12c. A quote from Oracle documentation:

The cryptographic hash function used for generating the 12C verifier is based on a de-optimized algorithm involving PBKDF2 and SHA-512. The PBKDF2 algorithm is used to introduce computational asymmetry in the challenge facing an intruder seeking to recover the original password when in possession of the 12C verifier.

When the MD5 hash is there it weakens security since it is easier to brute force than the PBKDF2-based SHA512 alone.

Trustwave reserves the right to review all comments in the discussion below. Please note that for security and other reasons, we may not approve comments containing links.