密码哈希值在Oracle 12c数据库中调整

Simeon

原文链接:

https://www.trustwave.com/Resources/SpiderLabs-Blog/Changes-in-Oracle-Database-12c-password-hashes/

OracleOracle Database 12c中对用户密码哈希进行了改进。通过使用基于PBKDF2的SHA512哈希算法,替代过去简单的SHA1哈希加密,使得密码哈希更安全。在这篇文章中,我将解释一些变化及其安全影响。

1.Oracle密码哈希值

使用Oracle 11g数据库时,sys.user$表中的spare4列存储用户密码哈希值。

这是sys.user$.spare4 用户demo使用密码“epsilon”(可插入数据库)的输入示例:

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

详细步骤:

SQL> create user demo identified by epsilon;

User created.

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

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

笔者注:查询oracle用户必须是大写名称

sys.user$.passworddemo用户的密码值一样

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

运行结果:

2B7983437FE9FEB6

笔者注:

执行查询select password from sys.user$ where name = SYS';如图1所示。

图片1.png 

1获取SYS用户密码值

这将省略password值的讨论:它使用相同的算法(大写和连接用户名和密码,然后执行3DES散列)计算,跟以前的Oracle数据库版本中使用一样的算法。

spare4 列的值由分号分隔(“ S:”,“ H:”和“ T:”)的三个部分组成。

“S:”部分长度是60个字符或30个字节:

8F2D65FB5547B71C8DA3760F10960428CD307B1C6271691FC55C1F56554A

“ H:”部分长度是32个字符或16个字节:

DC9894A01797D91D92ECA1DA66242209

最后,“ T:”部分长度是160个字符或80个字节:

23D1F8CAC9001F69630ED2DD8DF67DD3BE5C470B5EA97B622F757FE102D8BF14BEDC94A3CC046D10858D885DB656DC0CBF899A79CD8C76B788744844CADE54EEEB4FDEC478FB7C7CBFBBAC57BA3EF22C

那么他们究竟有什么含义呢?

2.S部分

Oracle数据库11g中有“ S:”部分,它的创建过程如下:

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

(有关更多详细信息,请访问http://marcel.vandewaters.nl/oracle/security/password-hashes。)

Oracle Database 12c也是如此:下面的简单测试证明了这一点。

对于S上面的值(8F2D65FB5547B71C8DA3760F10960428CD307B1C6271691FC55C1F56554A):

哈希是8F2D65FB5547B71C8DA3760F10960428CD307B1Csalt是6271691FC55C1F56554A

密码是“epsilon”,所以让我们来计算SHA1哈希'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()

该计算产生:' 8F2D65FB5547B71C8DA3760F10960428CD307B1C',如图2所示。

 图片2.png

2计算epsilon的密码值

这与11g算法相同。

3.H部分

$ORACLE_HOME/rdbms/admin下查看一个SQL文件时可以发现这一点:

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

/

所以它似乎是一个MD5哈希加密。

请注意,下面的SQL代码$ORACLE_HOME/rdbms/admin 会修改spare4 列的值以删除H: 降级。

这是spare4.H如何计算的:用户名是大写的,然后MD5哈希加密计算其值,' XDB'和密码用冒号分隔(也即md5('DEMO:XDB:epsilon')32位加密)

import hashlib

m = hashlib.md5()

m.update('DEMO:XDB:epsilon')

m.hexdigest().upper()

'DC9894A01797D91D92ECA1DA66242209'

这使得可以使用预先计算密码字典的散列值来对内置的用户密码进行***,例如使用前缀SYSTEM:XDB:的系统用户密码。

H值似乎用于XDB中的摘要式身份验证。

4.T部分

这仅适用于12.1.0.2。对于12c之前的版本,T部分不可用。

我们通过更新sqlnet.ora 文件(假设客户端来自12.1.0.2发行版)来启用12c密码哈希:

# sqlnet.ora

SQLNET.ALLOWED_LOGON_VERSION_SERVER = 12a

然后重新创建演示用户(首先重新连接客户端):

drop user demo;

create user demo identified by epsilon;

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

H:DC9894A01797D91D92ECA1DA66242209;T:E3243B98974159CC24FD2C9A8B30BA62E0E83B6CA2FC7C55177C3A7F82602E3BDD17CEB9B9091CF9DAD672B8BE961A9EAC4D344BDBA878EDC5DCB5899F689EBD8DD1BE3F67BFF9813A464382381AB36B

注意到该spare4值不再包含“S”部分,只有“H:”部分“T”部分在那里。

Oracle Database 12c文档中,我们可以找到这个:

5.关于12C验证器

基于一个包含PBKDF2和SHA512的优化算法,所以密码应该通过PBKDF2,随后SHA512来生成T(部分)。在认证期间,服务器发送所谓的AUTH_VFR_DATA (匹配值spare4.T的最后16个字节)到客户端:

-- 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

所以我们可以把这个T 值分成两部分(前64个字节和AUTH_VFR_DATA):

E3243B98974159CC24FD2C9A8B30BA62E0E83B6CA2FC7C55177C3A7F82602E3BDD17CEB9B9091CF9DAD672B8BE961A9EAC4D344BDBA878EDC5DCB5899F689EBD (前128个字符或64个字节)8DD1BE3F67BFF9813A464382381AB36B (最后32个字符或16个字节AUTH_VFR_DATA)

假设AUTH_VFR_DATA 密码设置/重置时随机生成。因此Python代码生成的第一个64字节T 是(需要PBKDF2 Python模块):

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

运行结果:

E3243B98974159CC24FD2C9A8B30BA62E0E83B6CA2FC7C55177C3A7F82602E3BDD17CEB9B9091CF9DAD672B8BE961A9EAC4D344BDBA878EDC5DCB5899F689EBD

总结

Oracle在12c中添加了MD5哈希和基于PBKDF2的SHA512哈希加密。在Oracle文档中有一个引用:

用于生成12C 验证的哈希密码函数是基于包含PBKDF2和SHA-512的非优化算法。PBKDF2算法通常被介绍为当拥有12C的验证者找回原密码时,面对***者所面临的挑战时的非对称计算算法。

  笔者注:由于Oracle 12C验证器存在,通过抓包,可以通过暴力破解来获取Oracle的密码,后续内容为笔者补充。

6.PBKDF2简介及其算法

1)PBKDF2简介

PBKDF2简单而言就是将salted hash进行多次重复计算,这个次数是可选择的。如果计算一次所需要的时间是1微秒,那么计算1百万次就需要1秒钟。假如***一个密码所需的rainbow table有1千万条,建立所对应的rainbow table所需要的时间就是115天。这个代价足以让大部分的***者忘而生畏。

2)PBKDF2算法

 DK = PBKDF2(P,S,c,dkLen)

可选项: RPF 基本伪随机函数(hLen表示伪随机函数输出的字节长度)

输入:

  P 口令,一字节串

  S 盐值,字节串

c 迭代次数,正整数

dkLen 导出密钥的指定字节长度,正整数,最大约(2^32-1)*hLen

输出: DK 导出密钥,长度dkLen字节

步骤:

1. 如果dkLen>(2^32-1)*hLen,输出“derived key too long”并停止。

  2. 假设l是导出密钥的hLen个字节块的个数,r表示最后一个块的字节数。

l = CEIL (dkLen / hLen) ,

  r = dkLen - (l - 1) * hLen .

这里,CEIL(x)是“ceiling”函数,即,大于或等于x的最小整数。

4. 对于导出密钥的每一块,运用函数F于口令P、盐S、迭代次数c和块索引以计算块:

T_1 = F (P, S, c, 1) ,

  T_2 = F (P, S, c, 2) ,

  ...

  T_l = F (P, S, c, l) ,

这里函数F定义为基本伪随机函数PRF应用于口令P和盐S的串联和块索引i的前c次循环的异或和。

F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c

其中

U_1 = PRF (P, S || INT (i)) ,

  U_2 = PRF (P, U_1) ,

  ...

  U_c = PRF (P, U_{c-1}) .

这里,INT(i)是整数i的四字节编码,高字节在先。

3. 串联各块,抽取前dkLen字节以产生导出密钥DK:

DK = T_1 || T_2 || ... || T_l<0..r-1>

4. 输出导出密钥DK。

注意:函数F的构造遵循“belt-and-suspenders”方法。U_i次循环被递归计算以消除敌手的并行度;它们被异或到一起以减少有关递归退化到一个小的值集的担忧。

密码解密:

select username, password from dba_users;

hashcat -m 10200 -a 0 oracle.hash -o oracle.txt --remove oracle.hash wordlist.txt

hashcat -m 1800 -a 3 oracle.hash ?d?d?d?d?d?d?d?d?d?d?d