密码哈希值在Oracle 12c数据库中调整
成都创新互联是网站建设专家,致力于互联网品牌建设与网络营销,专业领域包括网站建设、成都网站制作、电商网站制作开发、微信小程序开发、微信营销、系统平台开发,与其他网站设计及系统开发公司不同,我们的整合解决方案结合了恒基网络品牌建设经验和互联网整合营销的理念,并将策略和执行紧密结合,且不断评估并优化我们的方案,为客户提供全方位的互联网品牌整合方案!
Simeon
原文链接:
https://www.trustwave.com/Resources/SpiderLabs-Blog/Changes-in-Oracle-Database-12c-password-hashes/
Oracle在Oracle 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$.password跟demo用户的密码值一样:
SQL> select password from sys.user$ where name = 'DEMO';
运行结果:
2B7983437FE9FEB6
笔者注:
执行查询select password from sys.user$ where name = SYS';如图1所示。
图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):
哈希是8F2D65FB5547B71C8DA3760F10960428CD307B1C,salt是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计算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 41D1BE3F67BFF9813A
34 36 34 33 38 32 33 38-31 41 42 33 36 42 15 48464382381AB36B.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