任务1:介绍
虑这样一个场景:您刚刚下载了一个 6 GB 的文件,并想知道您下载的副本是否与原始文件一点一点地相同。你会怎么做呢?或者,如果一位好心的撒玛利亚人将这个 6 GB 的文件交给您 USB 存储驱动器,您如何确定它与您要下载的文件相同?
以上两个问题的答案在于比较两个文件的哈希值;如果两个哈希值相等,则可以非常肯定地说这两个文件相同。但什么是哈希值?
哈希值是由哈希函数计算的固定大小的字符串或字符。哈希函数采用任意大小的输入并返回固定长度的输出,即哈希值。在这个房间里,我们将介绍哈希函数和值的各种令人兴奋和巧妙的用法。
术语说明:我们更喜欢术语 hash function 和 hash value。但是,我们偶尔会使用哈希一词作为动词来表示计算哈希值;此外,我们偶尔会单独使用 hash 这个词作为名词来指代 hash 值。
会议室先决条件
这个房间是三个关于密码学的介绍性房间中的第三个。由于我们将引用前两个聊天室中的主题,因此建议您在尝试此聊天室之前完成最后两个聊天室。
学习目标
完成此房间后,您将了解:
- 哈希函数和冲突
- 哈希在身份验证系统中的作用
- 识别存储的哈希值
- 破解哈希值
- 使用哈希进行完整性保护
首先,让我们通过按下面的 Start Machine 按钮来启动虚拟机。
本机将在分屏视图中启动。如果 VM 不可见,请使用页面顶部的蓝色 Show Split View 按钮。
您还可以使用以下凭证通过 SSH 在 IP 地址访问虚拟机:MACHINE_IP
- 用户名:
user
- 密码:
Tryhackme123!
任务2:哈希函数
什么是哈希函数?
哈希函数与加密不同。没有key,并且意味着不可能(或在计算上不切实际)从 output 回到 input。
哈希函数采用一些任意大小的输入数据,并创建该数据的摘要或摘要。输出具有固定大小。很难预测任何输入的输出,反之亦然。好的哈希算法计算速度相对较快,而反转速度慢得令人望而却步,即从输出开始并确定输入。输入数据的任何微小变化,即使是单个 bit,都应该导致 output 发生重大变化。
让我们看一个例子。在下面的终端中,我们可以看到两个文件;第一个包含字母 T,而第二个包含字母 U。如果在 ASCII 表中或使用 hexdump
检查 T 和 U,您将注意到这两个字母相差一位。
- 字母 T 是十六进制的
54
,01010100
即二进制的。 - 字母 U 是十六进制的
55
,01010101
即二进制的。
因此,以下两个文件相差一位。但是,如果我们比较它们的 MD5(消息摘要算法 5)哈希值、SHA1(安全哈希算法 1)哈希值或 SHA-256(安全哈希算法 256)哈希值,我们会注意到它们完全不同。我们建议您自己尝试以下命令。这些文件位于~/Hashing-Basics/Task-2/
中。
哈希函数的输出通常是原始字节,然后对其进行编码。常见编码为 base64 或十六进制。md5sum,
sha1sum,
sha256sum,
sha512sum
并以十六进制格式生成其输出。请记住,十六进制格式将每个原始字节打印为两个十六进制数字。
为什么哈希很重要?
哈希在我们日常使用互联网中起着至关重要的作用。与其他加密函数一样,哈希对用户保持隐藏。哈希有助于保护数据的完整性并确保密码的机密性。
考虑这个用于保护网络安全的哈希示例。当您登录 TryHackMe 时,服务器使用哈希来验证您的密码。事实上,根据良好的安全实践,服务器不会记录您的密码;它记录密码的哈希值。每当您想登录时,它都会计算您提交的密码的哈希值和记录的哈希值。同样,当您登录计算机时,哈希在验证您的密码中发挥作用。您与哈希的交互比您想象的要间接得多,而且几乎每天都在密码的上下文中。
什么是哈希冲突?
哈希冲突是指两个不同的输入给出相同的输出。哈希函数旨在尽可能避免冲突。此外,哈希函数旨在防止攻击者能够故意创建冲突,即设计冲突。但是,由于输入的数量实际上是无限的,而可能的输出数量是有限的,这会导致鸽子洞效应。
举个数值示例,如果哈希函数生成一个 4 位哈希值,则我们只有 16 个不同的哈希值。可能的哈希值总数为 2number_of_bits = 24 = 16。发生碰撞的概率相对非常高。
鸽子洞效应表明物品的数量(鸽子)大于容器的数量(鸽子洞);某些容器必须容纳多个项目。换句话说,在此上下文中,哈希函数有固定数量的不同输出值,但您可以为其提供任何大小的输入。由于 inputs 多于 outputs,因此某些 inputs 必须不可避免地提供相同的 output。如果你有 21 只鸽子和 16 个鸽子洞,一些鸽子将共享鸽子洞。因此,碰撞是不可避免的。但是,一个好的哈希函数可以确保冲突的概率可以忽略不计。
MD5 和 SHA1 已受到攻击,由于能够制造哈希冲突,现在被认为不安全。但是,还没有攻击同时在两种算法中产生冲突,因此如果您比较 MD5 和 SHA1 哈希值,您会发现它们是不同的。您可以在 MD5 Collision Demo 页面上查看 MD5 碰撞示例;此外,您可以在 Shattered 上阅读 SHA1 冲突攻击的详细信息。因此,您不应该相信任何一种算法来哈希密码或数据。
任务3:用于身份验证的不安全密码存储
哈希在网络安全中有很多用途。在这个房间里,我们将重点介绍两个用途:密码存储和数据完整性。当用于身份验证时,我们指的是密码存储。
请务必注意,这不适用于密码管理器,您必须以明文形式检索密码。另一方面,身份验证机制只需要确认用户知道密码,以便可以授予他们访问资源的权限;因此,此问题与密码管理器不同。
用于身份验证的不安全密码存储的故事
大多数 Web 应用程序都需要在某个时候验证用户的密码。以明文形式存储这些密码是一种非常不安全的安全做法。您可能已经看过有关数据库泄露公司的新闻报道。知道许多人在他们的各种账户(包括他们的网上银行)上使用相同的密码,从一个账户泄露密码会危及所有其他账户的安全。
我们将访问密码方面的三种不安全做法:
- 以明文形式存储密码
- 使用已弃用的加密方式存储密码
- 使用不安全的哈希算法存储密码
以明文形式存储密码
相当多的数据泄露事件泄露了明文密码。您可能熟悉 Kali Linux 上的“rockyou.txt”密码列表,以及许多其他令人反感的安全发行版。此密码列表来自 RockYou,这是一家开发社交媒体应用程序和小部件的公司。他们将密码以明文形式存储,该公司发生了数据泄露事件。该文本文件包含超过 1400 万个密码。您可以在目录/home/usr
中找到rockyou.txt
。
使用不安全的加密算法
Adobe 值得注意的数据泄露事件略有不同。该公司没有使用安全的哈希函数来存储密码的哈希值,而是使用了已弃用的加密格式。此外,密码提示以纯文本形式存储,有时包含密码本身。因此,可以相对较快地检索到纯文本密码。
使用不安全的哈希函数
LinkedIn 在 2012 年也遭受了数据泄露。LinkedIn 使用不安全的哈希算法 SHA-1 来存储用户密码。此外,没有使用密码加盐。(Password salting)密码加盐是指在对密码进行哈希处理之前向密码添加盐,即随机值。
任务4:使用哈希进行安全密码存储
使用哈希来存储密码
这就是哈希的用武之地。如果您只是使用安全的哈希函数存储其哈希值,而不是存储密码,该怎么办?此过程意味着您永远不必存储用户的密码,如果您的数据库泄露,攻击者将不得不破解每个密码才能找出密码是什么。
这只有一个问题。如果两个用户具有相同的密码怎么办?由于哈希函数总是将相同的输入转换为相同的输出,因此您将为每个用户存储相同的密码哈希。这意味着如果有人破解了该哈希值,他们就可以访问多个帐户。这也意味着有人可以创建一个 Rainbow Table 来破解哈希值。
Rainbow Table 是将哈希值转换为明文的查找表,因此您可以从哈希值中快速找出用户的密码。Rainbow Table 用时间来破解哈希值来换取硬盘空间,但创建需要时间。下面是一个快速示例,可以了解彩虹表的外观。
散 列 | 密码 |
---|---|
02c75fb22c75b23dc963c7eb91a062cc | zxcvbnm |
b0baee9d279d34fa1dfd71aadb908c3f | 11111 |
c44a471bd78cc6c2fea32b9fe028d30a | asdfghjkl |
D0199F51D2728DB6011945145A1B607A | 篮球 |
DCDDB75469B4B4875094E14561E573D8 | 000000 |
e10adc3949ba59abbe56e057f20f883e | 123456 |
e19d5cd5af0378da05f63f891c7467af | 编号:abcd1234 |
e99a18c428cb38d5f260853678922e03 | 美国广播公司 (ABC)123 |
FCEA920f7412b5da7be0cf42b8c93759 | 1234567 |
4c5923b6a6fac7b7355f53bfe2b8f8c1 | inS3CyourP4$$ |
CrackStation 和 Hashes.com 等网站内部使用大量彩虹表来为没有盐的哈希值提供快速密码破解。在排序的哈希列表中进行查找比尝试破解哈希更快。
防范 Rainbow 表
为了防止彩虹表,我们在密码中添加了盐。盐是存储在数据库中的随机生成的值,对于每个用户来说应该是唯一的。理论上,您可以为所有用户使用相同的盐,但重复的密码仍将具有相同的哈希值,并且仍然可以为具有该盐的密码创建彩虹表。
在对密码进行哈希处理之前,会将盐添加到密码的开头或结尾,这意味着每个用户都将具有不同的密码哈希值,即使他们具有相同的密码。Bcrypt 和 Scrypt 等哈希函数会自动处理此问题。盐不需要保密。
安全存储密码的示例
您可以在网上找到许多很好的指南,这些指南促进了存储密码时的最佳安全实践。在采用密码之前,请检查存储密码时是否需要遵循任何标准。在存储用户密码时,请考虑遵循以下良好安全做法的此示例:
- 我们选择一个安全的哈希函数,例如 Argon2、Scrypt、Bcrypt 或 PBKDF2。
- 我们在密码中添加一个唯一的盐,例如
Y4UV*^(=go_!
- 将密码与唯一的 salt 连接起来。例如,如果密码为 ,则结果字符串将为
AL4RMc10k
AL4RMc10kY4UV*^(=go_!
- 计算 password 和 salt 组合的哈希值。在此示例中,使用所选算法,您需要计算 的哈希值。
AL4RMc10kY4UV*^(=go_!
- 存储哈希值和使用的唯一盐 (
Y4UV*^(=go_!
) 。
使用加密存储密码
考虑到保存密码进行身份验证的问题,为什么我们不加密密码,而不是所有这些繁琐的步骤呢?原因是,即使我们选择一种安全的哈希算法来加密密码,然后再存储密码,我们仍然需要存储使用的密钥。因此,如果有人获得了密钥,他们可以轻松解密所有密码。
任务5:识别密码哈希
从网络防御安全的角度来看,我们介绍了如何安全地为身份验证系统存储密码。让我们从进攻性安全的角度来解决这个问题;如果我们从一个哈希开始,我们如何识别它的类型,最终破解它,并恢复原始密码?
存在自动哈希识别工具,例如 hashID,但对于许多格式来说并不可靠。对于具有前缀的哈希,这些工具是可靠的。使用上下文和工具的健康组合。如果您在 Web 应用程序数据库中找到哈希值,则它更可能是 MD5 而不是 NTLM (NT LAN Manager)。自动哈希识别工具通常会混淆这些哈希类型,从而凸显了自我学习的重要性。
Linux的密码
在 Linux 上,密码哈希存储在/etc/shadow
中,通常只有 root 才能读取。它们曾经存储在/etc/passwd
中,每个人都可以读取。
该文件包含密码信息。每行包含 9 个字段,用冒号 (:
) 分隔。前两个字段是登录名和加密密码。有关其他字段的更多信息,可以通过在 Linux 系统上执行man 5 shadow
来找到。
加密的密码字段包含经过哈希处理的密码,其中包含四个组成部分:prefix(算法 ID)、options(参数)、salt (盐值)和 hash。它以 $prefix$options$salt$hash
格式保存。前缀使识别 Unix 和 Linux 样式的密码变得容易;它指定用于生成哈希的哈希算法。
以下是您可能会遇到的一些最常见的 Unix 样式密码前缀的快速表格。它们按强度递减的顺序列出。您可以通过查看手册页来阅读有关它们的更多信息man 5 crypt
。
前缀 | 算法 |
---|---|
$y$ | yescrypt 是一种可扩展的哈希方案,是新系统的默认和推荐选择 |
$gy$ | gost-yescrypt 使用 GOST R 34.11-2012 哈希函数和 yescrypt 哈希方法 |
$7$ | scrypt 是一种基于密码的密钥派生函数 |
$2b$ , , ,$2y$ $2a$ $2x$ | bcrypt 是基于 Blowfish 分组密码的哈希值,最初是为 OpenBSD 开发的,但在最新版本的 FreeBSD、NetBSD、Solaris 10 和更高版本以及多个 Linux 发行版上受支持 |
$6$ | sha512crypt 是基于 SHA-2 的哈希值,具有 512 位输出,最初是为 GNU libc 开发的,通常用于(较旧的)Linux 系统 |
$md5 | SunMD5 是基于最初为 Solaris 开发的 MD5 算法的哈希 |
$1$ | md5crypt 是基于 MD5 算法的哈希值,最初是为 FreeBSD 开发的 |
现代 Linux 示例
请考虑现代 Linux 系统的密码文件中的以下行。shadow
root@TryHackMe# sudo cat /etc/shadow | grep strategosstrategos:$y$j9T$76UzfgEM5PnymhQ7TlJey1$/OOSg64dhfF.TigVPdzqiFang6uZA4QA1pzzegKdVm4:19965:0:99999:7:::
字段之间用冒号分隔。重要的参数是 username 和 hash 算法、salt 和 hash value。第二个字段的格式为 .$prefix$options$salt$hash
在上面的示例中,我们有四个部分,由 :$
y
表示使用的哈希算法 yescryptj9T
是传递给算法的参数76UzfgEM5PnymhQ7TlJey1
是用的盐/OOSg64dhfF.TigVPdzqiFang6uZA4QA1pzzegKdVm4
是哈希值
字段之间用冒号分隔。重要的参数是 username 和 hash 算法、salt 和 hash value。第二个字段的格式为 .$prefix$options$salt$hash
在上面的示例中,我们有四个部分,由 :$
y
表示使用的哈希算法 yescryptj9T
是传递给算法的参数76UzfgEM5PnymhQ7TlJey1
是用的盐/OOSg64dhfF.TigVPdzqiFang6uZA4QA1pzzegKdVm4
是哈希值
MS Windows 密码
MS Windows 密码使用 NTLM(MD4 的一种变体)进行哈希处理。它们在视觉上与 MD4 和 MD5 哈希相同,因此使用 context 来确定哈希类型非常重要。
在 MS Windows 上,密码哈希存储在 SAM (Security Accounts Manager) 中。MS Windows 试图阻止普通用户转储它们,但像 mimikatz 这样的工具存在来规避 MS Windows 安全性。值得注意的是,在那里找到的哈希分为 NT 哈希和 LM 哈希。
查找更多哈希格式和密码前缀的好地方是 Hashcat 示例哈希页面。对于其他哈希类型,您通常需要检查长度或编码,甚至对生成它们的应用程序进行一些研究。永远不要低估研究的力量。
任务6:密码破解
我们已经提到了 rainbow table 作为破解不使用盐的哈希值的方法,但如果涉及盐怎么办?
您无法“解密”密码哈希。它们没有加密。您必须通过对许多不同的输入进行哈希处理来破解哈希值(例如rockyou.txt
,因为它涵盖了许多可能的密码),如果有的话,可能会添加盐并将其与目标哈希值进行比较。一旦匹配,您就知道密码是什么。Hashcat 和 John the Ripper 等工具通常用于这些目的。
使用 GPU 破解密码
现代 GPU(图形处理单元)具有数千个内核。他们专门从事数字图像处理和加速计算机图形学。虽然它们不能做与 CPU 相同的工作,但它们非常擅长哈希函数中涉及的一些数学计算。您可以使用显卡快速破解许多哈希类型。一些哈希算法(例如 Bcrypt)的设计使得 GPU 上的哈希不会比使用 CPU 提供任何速度改进;这有助于他们抵抗开裂。
破解 VM?
值得一提的是,VM(虚拟机)通常无法访问主机的显卡。根据您使用的虚拟化软件,您可以进行设置,但这很麻烦。此外,当您使用来自虚拟化操作系统的 CPU 时,性能会下降,当您的目的是破解哈希时,您需要每个额外的 CPU 周期。
如果您想运行 Hashcat,最好在主机上运行它以充分利用您的 GPU(如果可用)。如果您更喜欢 MS Windows,那么您很幸运;网站上提供了 MS Windows 版本,您可以从 PowerShell 运行它。您可以在 VM 中让 Hashcat 与 OpenCL 一起工作,但速度可能比在主机上破解要差。
John the Ripper 默认使用 CPU,并在开箱即用的 VM 中运行,但您可以在主机操作系统上运行它以获得更快的速度,以避免任何虚拟化开销并充分利用 CPU 内核和线程。
是时候破解一些哈希值了
我将提供哈希值。破解它们。您可以选择如何操作。您需要使用在线工具、Hashcat 或 John the Ripper。虽然您可以使用在线彩虹表来解决以下问题,但我们强烈建议不要这样做,因为这会限制您的学习体验。对于前三个问题,使用 along with 就足以找到答案。hashcat
rockyou.txt
Hashcat 使用以下基本语法:,其中:hashcat -m <hash_type> -a <attack_mode> hashfile wordlist
-m <hash_type>
以数字格式指定哈希类型。例如,is for NTLM.查看官方文档 () 和示例页面以查找要使用的哈希类型代码。-m 1000
man hashcat
-a <attack_mode>
指定 attack-mode。例如,是 straight 的,即从 wordlist 中尝试一个密码接一个密码。-a 0
hashfile
是包含您要破解的哈希的文件。wordlist
是要在攻击中使用的安全字词列表。
例如,将哈希视为 Bcrypt 并尝试文件中的密码。
hashcat -m 3200 -a 0 hash.txt /usr/share/wordlists/rockyou.txt
这里我很sb,我这里没有直接显示,是因为生成在了文件里面。
命令: hashcat --show -m 3200 -o 1.txt hash1.txt /usr/share/wordlists/rockyou.txt
命令: hashcat -m 1400 -o 2.txt hash2.txt /usr/share/wordlists/rockyou.txt
命令: hashcat -m 1800 -o 3.txt hash3.txt /usr/share/wordlists/rockyou.txt
这个一看就是md5的hash值
命令: hashcat -m 0 -o 4.txt hash4.txt /usr/share/wordlists/rockyou.txt
找的在线解密
任务7:用于完整性检查的哈希
在任务 3 中,我们提到我们将专注于哈希的两种用途:密码存储和数据完整性。我们已经广泛讨论了哈希如何保护身份验证系统中的密码。在本任务中,我们将讨论如何使用哈希函数来检查文件的完整性。
完整性检查
哈希可用于检查文件是否未被更改。如果你输入相同的数据,你总是会得到相同的数据。即使单个位发生变化,哈希也会发生显著变化,如任务 2 所示。这意味着您可以使用它来检查文件是否未被修改,或确保您下载的文件与 Web 服务器上的文件相同。下面列出的文本文件显示了两个 Fedora Workstation ISO 文件的 SHA256 哈希值。如果对您下载的文件运行返回此签名文件中列出的相同哈希值,则可以确信您的文件与官方文件相同。sha256sum

HMAC
HMAC(Keyed-Hash Message Authentication Code,密钥哈希消息身份验证代码)是一种消息身份验证代码 (MAC),它使用加密哈希函数和密钥来验证数据的真实性和完整性。
HMAC 可用于确保创建 HMAC 的人是他们所说的那个人,即确认真实性;此外,它证明消息没有被修改或损坏,即保持了完整性。这是通过使用密钥来证明真实性,并使用哈希算法来生成哈希值并证明完整性来实现的。
以下步骤让您对 HMAC 的工作原理有一个大致的了解。
- 密钥被填充到哈希函数的块大小中。
- 填充键与常量(通常是 0 或 1 块)进行 XOR 运算。
- 使用带有 XOR 键的 hash 函数对消息进行哈希处理。
- 然后,使用相同的哈希函数再次对步骤 3 的结果进行哈希处理,但使用填充键与另一个常量进行 XOR 运算。
- 最终输出是 HMAC 值,通常是一个固定大小的字符串。
下图应该阐明上述步骤。

从技术上讲,HMAC 函数使用以下表达式计算:
HMAC(K,M) = H((K⊕opad)||H((K⊕ipad)||M))
请注意,M 和 K 分别表示消息和键。
sha256sum libgcrypt-1.11.0.tar.bz2

任务8:结论
这个房间从各个角度介绍了哈希函数及其用途。在继续之前,我们应该区分哈希、编码和加密。
如前所述,哈希是一个获取输入数据并生成哈希值(固定大小的字符串,也称为摘要)的过程。此哈希值唯一地表示数据,数据中的任何更改,无论多小,都应导致哈希值的变化。哈希不应与加密或编码混淆;哈希是单向的,您无法反转获取原始数据的过程。
编码将数据从一种形式转换为另一种形式,以使其与特定系统兼容。ASCII、UTF-8、UTF-16、UTF-32、ISO-8859-1 和 Windows-1252 是英语的有效编码方法。请注意,UTF-8、UTF-16 和 UTF-32 是 Unicode 编码,它们可以表示其他语言(如阿拉伯语和日语)的字符。
发送或保存数据时常用的另一种编码类型不用于任何特定语言。示例包括 Base32 和 Base64 编码。请考虑以下 using base64
编码和解码的示例。
不应将编码与加密混淆,因为使用特定编码不会保护消息的机密性。编码是可逆的;任何人都可以使用正确的工具更改数据编码。
只有加密(我们在前面的房间里介绍过)使用加密密码和密钥来保护数据机密性。加密是可逆的,前提是我们知道密码并且可以访问密钥。
要继续学习本模块,请加入 John the Ripper;但是,如果您想获得有关密码学的更深入信息,请考虑这个房间,它深入探讨了更多概念并展示了更多与密码学相关的工具。