任务一介绍
想象一下,你刚下载了一个6GB的文件,想知道你下载的副本是否和原始文件一点点一样。你会怎么做?或者如果有好心人把这个6GB的文件交给你,放在U盘上,你怎么确定它和你想下载的文件完全一样?
上述两个问题的答案在于比较这两个文件的哈希值;如果两个哈希值相等,你可以非常确定两个文件是相同的。那么,什么是哈希值?
哈希值是由哈希函数计算的固定大小字符串或字符。哈希函数接收任意大小的输入,返回固定长度的输出,即哈希值。我们将在本房间介绍哈希函数和值的各种有趣且巧妙的应用。
关于术语的说明:我们更倾向于使用“哈希函数”和“哈希值”这两个术语。不过,我们偶尔会用“哈希”作为动词来表示计算哈希值;此外,我们偶尔单独使用“哈希”一词作为名词来指代哈希值。
学习目标
完成该展厅后,您将了解到:
- 哈希函数与碰撞
- 哈希在认证系统中的作用
- 识别存储的哈希值
- 破解哈希值
- 哈希在完整性保护中的应用
任务二哈希函数
什么是哈希函数?
哈希函数不同于加密。没有密钥,而且从输出回到输入是不可能(或计算上不切实际的)。
哈希函数会接收任意大小的输入数据,并生成该数据的摘要或摘要。输出有固定大小。很难预测任何输入的输出,反之亦然。好的哈希算法计算速度相对较快,但反转(即从输出确定输入)的速度极慢。输入数据中的任何细微变化,哪怕只有一个比特,都应该引起输出显著变化。
我们来看一个例子。在下面的终端中,我们可以看到两个文件;第一个包含字母T,第二个包含字母U。如果你在ASCII表中检查T和U,或者使用,你会注意到这两个字母相差一位。hexdump
- 字母T是十六进制,即二进制。5401010100
- 字母U是十六进制,即二进制。5501010101
因此,以下两个文件相差一位。然而,如果比较它们的MD5(消息消化算法5)、SHA1(安全哈希算法1)或SHA-256(安全哈希算法256)哈希,我们会发现它们完全不同。我们建议你自己试试下面的命令。文件位于 。~/Hashing-Basics/Task-2/
航站楼
<span style="color:#151c2b"><span style="background-color:#ffffff"><span style="background-color:#282c33"><span style="color:#bac8d4"><code class="language-shell-session">strategos@g5000 ~> cat file1.txt
T⏎
strategos@g5000 ~> cat file2.txt
U⏎
strategos@g5000 ~> hexdump -C file1.txt
00000000 54 |T|
00000001
strategos@g5000 ~> hexdump -C file2.txt
00000000 55 |U|
00000001
strategos@g5000 ~> md5sum *.txt
b9ece18c950afbfa6b0fdbfa4ff731d3 file1.txt
4c614360da93c0a041b22e537de151eb file2.txt
strategos@g5000 ~> sha1sum *.txt
c2c53d66948214258a26ca9ca845d7ac0c17f8e7 file1.txt
b2c7c0caa10a0cca5ea7d69e54018ae0c0389dd6 file2.txt
strategos@g5000 ~> sha256sum *.txt
e632b7095b0bf32c260fa4c539e9fd7b852d0de454e9be26f24d0d6f91d069d3 file1.txt
a25513c7e0f6eaa80a3337ee18081b9e2ed09e00af8531c8f7bb2542764027e7 file2.txt</code></span></span></span></span>
哈希函数的输出通常是原始字节,然后对这些字节进行编码。常见的编码方式是base64或十六进制。, , , ,并以十六进制格式输出。请记住,十六进制格式会将每个原始字节打印为两个十六进制数字。md5sumsha1sumsha256sumsha512sum
为什么哈希很重要?
哈希在我们日常使用互联网中扮演着至关重要的角色。像其他密码学功能一样,哈希对用户保持隐藏。哈希有助于保护数据完整性并确保密码的机密性。
请考虑这个哈希被用来保护网络安全的例子。当你登录TryHackMe时,服务器会用哈希来验证你的密码。事实上,根据良好的安全规范,服务器不会记录你的密码;它记录的是你密码的哈希值。每当你想登录时,它会用记录的哈希值计算你提交的密码的哈希值。同样,当你登录电脑时,哈希在验证密码时起作用。你与哈希的交互比你想象的要间接得多,而且几乎每天都在密码的背景下进行。
什么是哈希碰撞?
哈希碰撞是指两个不同的输入产生相同的输出。哈希函数的设计目的是尽量避免碰撞。此外,哈希函数的设计目的是防止攻击者故意制造,即工程碰撞。然而,由于输入数量几乎无限,而可能输出的数量有限,这导致了鸽子套效应。
举个数值例子,如果哈希函数产生4位哈希值,我们只有16种不同的哈希值。可能的哈希值总数为 2nu mber_of_bits = 2,4 = 16。碰撞的概率相对非常高。
鸽笼效应指出,物品数量(鸽子)多于容器数量(鸽笼);有些容器必须装多个物品。换句话说,在这个语境下,哈希函数有固定数量的输出值,但你可以给它任何大小的输入。由于输入多于输出,某些输入必然会给出相同的输出。如果你有21只鸽子和16个鸽巢,有些鸽子会共用鸽巢。因此,碰撞是不可避免的。然而,良好的哈希函数能确保碰撞的概率可以忽略不计。
MD5 和 SHA1 曾遭到攻击,因能够工程哈希碰撞而被认为不安全。然而,目前还没有攻击同时在两个算法中发生碰撞,所以如果你比较MD5和SHA1哈希,会发现它们是不同的。你可以在MD5碰撞演示页面查看MD5碰撞示例;此外,您还可以在Shattered网站上阅读SHA1碰撞攻击的详细信息。因此,你不应信任任何一种算法来进行密码或数据的哈希。

任务三用于认证的不安全密码存储
哈希在网络安全中有许多用途。在这个房间里,我们将重点讨论两种用途:密码存储和数据完整性。我们称之为密码存储,用于身份验证。
需要注意的是,这不适用于密码管理器,因为你需要以明文方式获取密码。另一方面,认证机制只需确认用户知道密码,即可获得资源访问权;因此,这个问题与密码管理器不同。
关于身份验证中不安全密码存储的故事
大多数网页应用都需要在某个阶段验证用户的密码。将这些密码存储为明文是一种非常不安全的安全做法。你可能已经看到过一些关于公司数据库被泄露的新闻报道。鉴于许多人在多个账户(包括网上银行)使用相同密码,从一个账户泄露密码将危及其他账户的安全。
我们将探讨三种关于密码的不安全做法:
- 以明文存储密码
- 使用已废弃加密存储密码
- 使用不安全的哈希算法存储密码
以明文存储密码
不少数据泄露事件导致明文密码泄露。你可能熟悉Kali Linux上的“rockyou.txt”密码列表,以及许多其他攻击性安全发行版。这份密码列表来自RockYou,一家开发社交媒体应用和小工具的公司。他们把密码存成明文,公司也发生了数据泄露。该文本文件包含超过1400万个密码。你可以在目录中找到。rockyou.txt/usr/share/wordlists
航站楼
<span style="color:#151c2b"><span style="background-color:#ffffff"><span style="background-color:#282c33"><span style="color:#bac8d4"><code class="language-shell-session">strategos@g5000 /usr/share/wordlists> wc -l rockyou.txt
14344392 rockyou.txt
strategos@g5000 /usr/share/wordlists> head rockyou.txt
123456
12345
123456789
password
iloveyou
princess
1234567
rockyou
12345678
abc123</code></span></span></span></span>
使用不安全的加密算法
Adobe这次著名的数据泄露事件则略有不同。公司没有使用安全的哈希函数来存储密码的哈希值,而是采用了一种已废弃的加密格式。此外,密码提示以纯文本形式存储,有时包含密码本身。因此,明文密码可以相对快速地被检索。
使用不安全哈希函数
LinkedIn 也在2012年遭遇了数据泄露事件。LinkedIn 使用了一个不安全的哈希算法 SHA-1 来存储用户密码。此外,没有使用密码加盐。密码加盐是指在密码进行哈希前,添加一个盐,即随机值。

任务四使用哈希进行安全密码存储
使用哈希存储密码
这就是哈希发挥作用的地方。如果你不存储密码,而是用安全的哈希函数存储密码的哈希值呢?这个过程意味着你永远不必存储用户的密码,如果数据库泄露,攻击者必须破解每个密码才能查明密码。
但这里有一个问题。如果两个用户有相同的密码怎么办?由于哈希函数总是将相同的输入转化为相同的输出,你会为每个用户存储相同的密码哈希值。这意味着如果有人破解了哈希值,他们就能访问多个账号。这也意味着有人可以创建一个彩虹表来打破哈希值。
彩虹表是一个将哈希值与明文进行查找表,因此你可以仅凭哈希快速查出用户密码。彩虹表用破解哈希的时间换取硬盘空间,但生成需要时间。这里有一个快速的例子,帮助你了解彩虹桌的样子。
| 02C75fb22C75B23dc963C7eb91a062cc | ZXCVBNM |
| b0baee9d279d34fa1DFD71AADb908C3F | 11111 |
| C44A471BD78cc6C2FEA32B9FE028D30A | 阿斯德夫格尔克尔 |
| d0199f51d2728db6011945145a1b607a | 篮球 |
| DCDDB75469b4B4875094E14561E573D8 | 000000 |
| e10ADC3949ba59abbe56E057F20F883E | 123456 |
| E19D5CD5af0378DA05F63F891C7467AF | ABCD1234 |
| E99A18C428CB38D5F260853678922E03 | ABC123 |
| FCEA920F7412B5DA7BE0CF42B8C93759 | 1234567 |
| 4c5923b6a6fac7b7355f53bfe2b8f8c1 | 在S3CyourP4$$ |
像CrackStation和 Hashes.com 这样的网站内部使用巨大的彩虹表,提供无需加盐的哈希快速破解密码。在排序好的哈希列表里查找比试图破解哈希要快。

防范彩虹桌
为了防止彩虹表,我们在密码中添加了盐。盐是随机生成的值,存储在数据库中,每个用户都应该是独一无二的。理论上,你可以为所有用户使用相同的盐,但重复的密码哈希值仍然相同,并且仍然可以为带有该盐的密码创建一个彩虹表。
盐会在密码哈希前加入,或在密码的开头或结尾处加值,这意味着即使用户密码相同,哈希也会不同。像Bcrypt和Scrypt这样的哈希函数会自动处理这些。盐不需要保密。
安全存储密码示例
你可以在网上找到许多推荐存储密码的最佳安全实践的好指南。在采用密码之前,请确认存储密码时是否有任何需要遵守的标准。请参考以下示例,遵循存储用户密码时的良好安全实践:
使用加密存储密码
考虑到保存密码以供认证的问题,为什么我们不直接加密密码,而不必经历这些繁琐的步骤呢?原因是即使我们选择了安全的哈希算法来加密密码再存储,我们仍然需要存储所使用的密钥。因此,如果有人拿到密钥,就能轻松解密所有密码。

任务5识别密码哈希
从网络防御安全的角度,我们介绍了如何安全存储密码以供认证系统使用。让我们从进攻安全角度来探讨这个问题;如果我们从哈希开始,如何识别它的类型,最终破解它,并恢复原始密码?
存在自动哈希识别工具如hashID,但对许多格式来说不够可靠。对于带有前缀的哈希,这些工具是可靠的。使用健康的背景和工具组合。如果你在网页应用数据库中找到哈希值,更可能是MD5而不是NTLM(NT LAN管理器)。自动哈希识别工具经常会混淆这些哈希类型,这凸显了自我学习的重要性。
Linux密码
在 Linux 上,密码哈希存储在 ,通常只有 root 权限才能读取。它们以前存储在 ,大家都能读取。/etc/shadow/etc/passwd
该文件包含密码信息。每行包含九个字段,中间用冒号()。前两个字段是登录名和加密密码。关于其他字段的更多信息,可以通过在 Linux 系统上执行找到。shadow:man 5 shadow
加密密码字段包含包含四个组件的哈希密码短语:前缀(算法ID)、选项(参数)、盐和哈希。它以格式保存。前缀使识别Unix和Linux风格密码变得容易;它指定了用于生成哈希的哈希算法。$prefix$options$salt$hash
这里有一张你可能遇到的一些最常见的Unix风格密码前缀的简要表。它们按强度递减排列。你可以查看man页面了解更多相关信息。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 是一种基于最初为 FreeBSD 开发的 MD5 算法的哈希 |
现代Linux示例
请考虑现代Linux系统密码文件中的以下一句话。shadow
航站楼
<span style="color:#151c2b"><span style="background-color:#ffffff"><span style="background-color:#282c33"><span style="color:#bac8d4"><code class="language-shell-session"><span style="color:#f8f8f2"><span style="color:#bac8d4">root@TryHackMe</span></span><span style="color:#fd971f"><strong>#</strong></span> <span style="color:#ffaf00">sudo</span> <span style="color:#ffaf00">cat</span> /etc/shadow <span style="color:#f8f8f2">|</span> <span style="color:#ffaf00">grep</span> strategos
strategos:$y$j9T$76UzfgEM5PnymhQ7TlJey1$/OOSg64dhfF.TigVPdzqiFang6uZA4QA1pzzegKdVm4:19965:0:99999:7:::</code></span></span></span></span>
这些田块之间用冒号分隔。重要的是用户名和哈希算法、盐值和哈希值。第二个字段格式为 。$prefix$options$salt$hash
在上述例子中,我们有四个部分,分隔为:$
- y表示所使用的哈希算法,yescrypt
- j9T是传递给算法的参数
- 76UzfgEM5PnymhQ7TlJey1是所使用的盐
- /OOSg64dhfF.TigVPdzqiFang6uZA4QA1pzzegKdVm4是哈希值
微软Windows密码
微软Windows密码使用NTLM进行哈希,NTLM是MD4的一个变体。它们在视觉上与MD4和MD5的哈希完全相同,因此利用上下文来确定哈希类型非常重要。
在微软Windows上,密码哈希存储在SAM(安全账户管理器)中。微软Windows试图防止普通用户转储这些文件,但像mimikatz这样的工具可以绕过微软Windows的安全。值得注意的是,这里发现的哈希分为NT哈希和LM哈希

任务6密码破解
我们已经提到过彩虹表作为破解不使用盐的哈希值的方法,但如果有盐呢?
密码哈希是不能“解密”的。它们没有加密。你必须通过对许多不同的输入进行哈希(比如覆盖多种密码)来破解哈希,如果有盐,可能还要添加盐,并与目标哈希进行比较。一旦匹配成功,你就知道密码是什么了。像Hashcat和开膛手约翰这样的工具常用于这些目的。rockyou.txt
用GPU破解密码
现代GPU(图形处理单元)拥有数千个核心。他们专注于数字图像处理和计算机图形加速。虽然它们无法完成CPU那样的作业,但在哈希函数相关的一些数学计算方面表现非常出色。你可以用显卡快速破解多种哈希类型。一些哈希算法,如Bcrypt,设计成GPU上的哈希不会比CPU速度提升;这有助于防止开裂。
虚拟机破解?
值得一提的是,虚拟机(虚拟机)通常无法访问主机的显卡。根据你使用的虚拟化软件,你可以设置,但过程很繁琐。此外,当你使用虚拟化作系统的CPU时,性能下降就会发生,而当你的目的是破解哈希时,你需要每多出一个CPU周期。
如果你想运行Hashcat,最好在主机上运行,以最大化GPU的使用。如果你更喜欢微软Windows,那你很幸运;微软Windows版本可以在官网上提供,你也可以用PowerShell运行。你可以让 Hashcat 在虚拟机里用 OpenCL 运行,但速度可能比主机破解还差。
《开膛手约翰》默认使用CPU,开箱即用虚拟机运行,虽然你在主机作系统上运行它可能会更快,以避免虚拟化开销,最大化CPU核心和线程的使用。
是时候破解一些哈希值了
我来提供哈希值。敲开它们。你可以选择如何做。你需要使用在线工具,比如Hashcat或开膛手约翰。虽然你可以用在线彩虹表来解决以下问题,但我们强烈建议不要这样做,因为这会限制你的学习体验。对于前三个问题,使用“与”即可找到答案。hashcatrockyou.txt
Hashcat 使用以下基本语法:,其中:hashcat -m <hash_type> -a <attack_mode> hashfile wordlist
- -m <hash_type>以数值格式指定哈希类型。例如,是NTLM的。查看官方文档()和示例页面,查找需要使用的哈希类型代码。-m 1000man hashcat
- -a <attack_mode>指定攻击模式。例如,是直的,也就是说,从单词列表中连续尝试一个密码。-a 0
- hashfile是包含你想要破解的哈希的文件。
- wordlist是你想在攻击中使用的安全词表。

任务7完整性检查的哈希
在任务三中,我们提到将重点关注哈希的两种用途:密码存储和数据完整性。我们已经详细讨论了哈希如何在认证系统中保护密码。在本任务中,我们将讨论如何利用哈希函数检查文件的完整性。
完整性检查
哈希可以用来检查文件是否被更改。如果你输入相同的数据,输出的数据总是一样。即使只改变一个比特,哈希值也会显著变化,如任务2所示。这意味着你可以用它来检查文件是否被修改,或者确保你下载的文件与服务器上的文件完全相同。下面列出的文本文件展示了两个Fedora工作站ISO文件的SHA256哈希值。如果你下载的文件返回的哈希值与该签名文件中列出的相同,你可以放心你的文件与官方文件完全相同。sha256sum
AttackBox 终端
<span style="color:#151c2b"><span style="background-color:#ffffff"><span style="background-color:#282c33"><span style="color:#bac8d4"><code class="language-shell-session"><span style="color:#f8f8f2"><span style="color:#bac8d4">root@AttackBox</span></span><span style="color:#fd971f"><strong>#</strong></span> <span style="color:#ffaf00">head</span> Fedora-Workstation-40-1.14-x86_64-CHECKSUM
—–BEGIN PGP SIGNED MESSAGE—–
Hash: SHA256
<span style="color:#fd971f"><strong>#</strong></span> Fedora-Workstation-Live-osb-40-1.14.x86_64.iso: <span style="color:#ffaf00">2623733760</span> bytes
SHA256 (Fedora-Workstation-Live-osb-40-1.14.x86_64.iso) = 8d3cb4d99f27eb932064915bc9ad34a7529d5d073a390896152a8a899518573f
<span style="color:#fd971f"><strong>#</strong></span> Fedora-Workstation-Live-x86_64-40-1.14.iso: <span style="color:#ffaf00">2295853056</span> bytes
SHA256 (Fedora-Workstation-Live-x86_64-40-1.14.iso) = dd1faca950d1a8c3d169adf2df4c3644ebb62f8aac04c401f2393e521395d613
[…]</code></span></span></span></span>
你也可以用哈希来查找重复文件;如果两个文档的哈希值相同,它们就是同一份文档。这对查找和删除重复文件非常方便。
HMACs(健康管理机构)
HMAC(密钥哈希消息认证码)是一种消息认证码(MAC),它结合密码学哈希函数与密钥来验证数据的真实性和完整性。
HMAC可用于确保创建者本人身份,即确认真实性;此外,它证明消息没有被修改或损坏,也就是说,完整性得到了维护。这通过使用密钥证明真实性,以及哈希算法生成哈希并证明完整性来实现。
以下步骤将让您对HMAC的工作原理有个大致了解。
下面的示意图应该能澄清上述步骤。
从技术角度讲,HMAC函数的计算方式如下:
HMAC(K,M) = H((K⊕opad)||H((K⊕ipad)||M))
注意,M 和 K 分别代表消息和密钥。

第八任务结论
该房间涵盖了哈希函数及其从多个角度的应用。在继续之前,我们应该区分哈希、编码和加密。
如前所述,哈希是一种处理输入数据并生成哈希值的过程,即固定大小的字符串字符,也称为摘要。该哈希值唯一代表数据,任何数据变化,无论多小,都应导致哈希值的变化。哈希不应与加密或编码混淆;哈希是单向的,你无法逆转过程来获得原始数据。
编码将数据从一种形式转换到另一种形式,使其兼容特定系统。ASCII、UTF-8、UTF-16、UTF-32、ISO-8859-1 和 Windows-1252 是英语语言的有效编码方法。请注意,UTF-8、UTF-16 和 UTF-32 是 Unicode 编码,它们可以表示其他语言的字符,如阿拉伯语和日语。
另一种常用的编码类型并非针对任何特定语言。例如,Base32和Base64编码。请考虑以下 的 来编码和解码的例子。base64
航站楼
<span style="color:#151c2b"><span style="background-color:#ffffff"><span style="background-color:#282c33"><span style="color:#bac8d4"><code class="language-shell-session">strategos@g5000 ~> base64
TryHackMe
VHJ5SGFja01lCg==
strategos@g5000 ~> base64 -d
VHJ5SGFja01lCg==
TryHackMe</code></span></span></span></span>
编码不应与加密混淆,因为使用特定编码并不能保护消息的机密性。编码是可逆的;任何人都可以用合适的工具更改数据编码。
只有加密,正如我们在前几次房间讲过的,通过密码学密码和密钥来保护数据机密。只要我们知道密码并能访问密钥,加密是可逆的。



