在大多数情况下,更改MySQL用户密码的说明提供了有关在生产系统上进行此操作的信息,例如重置root密码而无需重启。出于安全原因,定期更改密码甚至被建议。然而,有时候在旧系统上,DBA的职责可能会令人感到意外,需要恢复某些旧用户的原始密码。这并不难:只要存储的是密码的哈希值而不是原始密码,恢复丢失的密码的唯一方法就是通过强制使用已知的哈希值。有趣的是,如果黑客能够访问密码的哈希值并嗅探MySQL的流量,那么他们无需恢复明文密码。由于MySQL协议的设计,密码的强度和auth插件中的哈希算法的强度并无关系,嗅探到的哈希值足以连接到具有修补程序版本的MySQL客户端的数据库。这意味着,如果黑客能够访问数据库备份和流量,他们将自动获得所有所需的信息(SHA),以连接到正在运行的数据库。有关攻击的详细信息,请参见相关文档。
从MySQL 8.0开始,默认使用cachingsha2password作为auth插件,该插件提供了更强大的SHA256功能,而不是mysqlnativepassword插件中使用的SHA1。对于使用cachingsha2password插件进行身份验证的情况,只需要一个哈希值并且能够嗅探流量就足够了。然而,如果您希望密码能够与未修改的客户端一起使用,那么需要进行一些修改,请参阅下面的说明。
转储哈希值
让我们回到密码恢复的问题。首先,我们需要转储哈希值。对于MySQL 5.7,默认使用mysqlnativepassword作为auth插件,我们可以使用以下命令转储哈希值:
% mysql -Ns -uroot -e “SELECT SUBSTR(authenticationstring,2) AS hash FROM mysql.user WHERE plugin = ‘mysqlnativepassword’ AND authenticationstring NOT LIKE ‘%THISISNOTAVALIDPASSWORD%’ AND authenticationstring !=”;” > sha1hashes
对于MySQL 8.0,默认使用cachingsha2password作为auth插件,我们可以使用以下命令转储哈希值:
% mysql -Ns -uroot -e “SELECT CONCAT(‘$mysql’,LEFT(authenticationstring,6),’‘,INSERT(HEX(SUBSTR(authenticationstring,8)),41,0,’‘)) AS hash FROM mysql.user WHERE plugin = ‘cachingsha2password’ AND authenticationstring NOT LIKE ‘%INVALIDSALTANDPASSWORD%’ AND authenticationstring !=”;” > sha256_hashes
如果您需要获取root密码的哈希值,并且没有具有对mysql.user表读取权限的用户,则应使用–skip-grant-tables选项启动mysqld。有关详细信息,请参阅官方文档。
运行Linode GPU实例
为了进行密码恢复,需要在一些功能强大的GPU上运行计算,而市场上并没有太多提供GPU实例的云服务提供商。如果您需要一个简单可靠的提供商以及真正有用的支持部门,那么Linode是卓越的云服务提供商之一。Linode提供了一个功能强大的CLI工具,可以大大简化自动化过程。此外,对于更严格的自动化需求,还有官方的Terraform提供商。
128GB GPU Linode实例的密码恢复速度为30000 MH/s(每秒百万个哈希),非常出色。对于MySQL 5.7的8个字符密码的暴力破解(包括大写字母、小写字母和数字),仅需2个小时。实例的价格仅为6美元/小时。相比之下,另一家最大的云服务提供商(4个NVIDIA Tesla V100实例)具有相同的恢复速度,但成本是其两倍,为12.24美元/小时。
准备字典
密码暴力破解是基于字典进行的。我们以一个小的rockyou词典为例,说明其运行过程。您可以在weakpass.com网站上找到非常好的字典资源。然而,即使是最大的字典也可能不足以进行恢复。在这种情况下,您应该检查是否启用了validate_password插件,并根据其准备字典。可以通过以下命令进行检查:
如果此命令的输出为空,则表示插件已禁用。您可以在我们之前的博客文章中找到有关该插件的更多详细信息,标题为“使用validate_password插件提高MySQL密码安全性”。
validatepasswordpolicy字段是最重要的字段之一。它可以具有以下值:
如果设置了validatepasswordpolicy=STRONG并且设置了validatepassworddictionaryfile,则需要从中排除密码validatepassworddictionaryfile:
cat huge-dictionary.txt | pw-inspector -m 8 -M 32 -l -u -n -p | sort -u | grep -F -v -x -f prohibited.txt > reduced-dictionary.txt
在上面的示例中:
- -m 8是密码的最小长度,取决于validatepasswordlength变量的值;
- -M 32是密码的最大长度,对于复制密码,最大长度为32个字符;
- -n密码应包含数字,参考validatepasswordnumber_count变量;
- -l -u密码应包含小写/大写字符,参考validatepasswordmixedcasecount变量;
- -p密码应包含特殊字符;
- prohibited.txt是来自validatepassworddictionary_file变量的文件;
- huge-dictionary.txt是初始字典;
- reduced-dictionary.txt是不包含单词prohibited.txt的新字典。
如果字典攻击失败,则必须创建自己的字典进行暴力破解。在这种情况下,我们建议使用压缩、掩码处理器或Hashcat选项之一。
编译Hashcat
对于MySQL 8.0,应编译最新版本的hashcat的master分支,因为该版本的代码尚未发布。
% sudo apt -y install make gcc
% git clone
% cd hashcat
% make
% sudo make install
为NVIDIA启用OpenCL
更新到最新的软件版本,禁用nouveau驱动程序并重新启动:
% sudo apt update && sudo apt full-upgrade -y
% echo -e “blacklist nouveaunoptions nouveau modeset=0nalias nouveau off” | sudo tee /etc/modprobe.d/blacklist-nouveau.conf
% sudo update-initramfs -u
% reboot
安装专有驱动程序并重新启动
% sudo apt install -y nvidia-cuda-toolkit ocl-icd-libopencl1
% sudo apt install -y nvidia-driver-440 nvidia-utils-440
% sudo apt remove mesa-opencl-icd
% reboot
检查驱动程序
% sudo nvidia-smi
% hashcat -I
运行密码恢复
对于mysqlnativepassword(MySQL 5.7),请使用300代码:
% hashcat -m 300 -a 0 -D 2 -O -w 3 ./sha1_hashes ./rockyou.txt
对于cachingsha2password(MySQL 8.0),请使用7401代码:
% hashcat -m 7401 -a 0 -D 2 -O -w 3 ./sha256_hashes ./rockyou.txt
如果密码成功恢复,可以再次运行相同的命令,并带上–show选项来显示密码:
% hashcat -m 300 -a 0 -D 2 ./sha1_hashes ./rockyou.txt –show
0913bf2e2ce20ce21bfb1961af124d4920458e5f:new_password
这里的new_password就是正确的答案。
结论
在Linode GPU实例上,可以在2小时内恢复MySQL 5.7的8个字符密码,包括大小写字母和数字。对于MySQL 8.0的相同密码,恢复时间可能需要2.8年。然而,通常情况下,黑客根本不需要恢复明文密码(请参见上面的“mysql-unsha1攻击”部分)。为了降低风险,需要保护mysql.user表的内容,可以采取一些措施,例如不在MySQL本身中存储哈希值,而是使用LDAP插件对Percona Server进行身份验证,或者使用静态加密通过HashiCorp Vault插件进行备份。