Introduction 介绍
If you are a pentester and enjoy tinkering with Windows, you have probably come across the following post by Raphael Mudge:
如果您是渗透测试人员并且喜欢摆弄 Windows,您可能已经看到 Raphael Mudge 的以下帖子:
In this post, he explains how the Windows program runas works and how the netonly flag allows the creation of processes where the local identity differs from the network identity (the local identity remains the same, while the network identity is represented by the credentials used by runas).
在这篇文章中,他解释了 Windows 程序 runas 的工作原理,以及 netonly 标志如何允许创建本地标识与网络标识不同的进程(本地标识保持不变,而网络标识由 runas 使用的凭据表示)。
Cobalt Strike provides the make_token command to achieve a similar result to runas /netonly.
Cobalt Strike 提供了 make_token 命令来实现与 runas /netonly 类似的结果。
If you are familiar with this command, you have likely experienced situations in which processes created by Beacon do not “inherit” the new token properly. The inner workings of this command are fairly obscure, and searching Google for something like “make_token cobalt strike” does not provide much valuable information (in fact, it is far more useful to analyse the implementations of other frameworks such as Sliver or Covenant).
如果您熟悉此命令,您可能遇到过 Beacon 创建的进程无法正确“继承”新令牌的情况。这个命令的内部工作原理相当模糊,在谷歌上搜索“make_token cobalt strike”之类的东西并不能提供太多有价值的信息(事实上,分析其他框架(如 Sliver 或 Covenant)的实现要有用得多)。
In Raphael Mudge’s video Advanced Threat Tactics (6 of 9): Lateral Movement we can get more details about the command with statements like:
在 Raphael Mudge 的视频 Advanced Threat Tactics (6 of 9): Lateral Movement 中,我们可以通过如下语句获得有关该命令的更多详细信息:
“If you are in a privileged context, you can use make_token in Beacon to create an access token with credentials”
“如果您处于特权上下文中,则可以使用 Beacon 中的make_token创建具有凭据的访问令牌”“The problem with make_token, as much as steal_token, is it requires you to be in an administrator context before you can actually do anything with that token”
“make_token的问题在于,steal_token,它要求你先处于管理员上下文中,然后才能实际使用该令牌做任何事情”
Even though the description does not mention it, Raphael states that make_token requires an administrative context. However, if we go ahead and use the command with a non-privileged user… it works! What are we missing here?
尽管描述中没有提到它,但拉斐尔指出make_token需要一个行政背景。但是,如果我们继续对非特权用户使用该命令……它有效!我们在这里错过了什么?
This post aims to shed more light on how the make_token command works, as well as its capabilities and limitations. This information will be useful in situations where you want to impersonate other users through their credentials with the goal of enumerating or moving laterally to remote systems.
这篇文章旨在更深入地阐明 make_token 命令的工作原理,以及它的功能和局限性。如果您希望通过其他用户的凭据模拟其他用户,目的是枚举或横向移动到远程系统,则此信息非常有用。
It’s important to note that, even though we are discussing Cobalt Strike, this knowledge is perfectly applicable to any modern C2 framework. In fact, for the purposes of this post, we took advantage of the fact that Meterpreter did not have a make_token module to implement it ourselves.
需要注意的是,尽管我们正在讨论 Cobalt Strike,但这些知识完全适用于任何现代 C2 框架。事实上,出于这篇文章的目的,我们利用了 Meterpreter 没有 make_token 模块自己实现它的事实。
An example of the new post/windows/manage/make_token module can be seen below:
新的 post/windows/manage/make_token 模块的示例如下所示:
You can find more information about our implementation in the following links:
您可以在以下链接中找到有关我们实施的更多信息:
- Pr #18022 – Add update_token to MSF + make_token post-ex module
Pr #18022 – 将update_token添加到 MSF + make_token post-ex 模块 - PR #648 – Add update_token function to stdapi
PR #648 – 向 stdapi 添加update_token函数 - Metasploit Weekly Wrap Up 15
Metasploit 每周总结 15
Windows Authentication Theory
Windows 身份验证理论
Let’s begin with some theory about Windows authentication. This will help in understanding how make_token works under the hood and addressing the questions raised in the introduction.
让我们从一些关于 Windows 身份验证的理论开始。这将有助于理解make_token在引擎盖下是如何工作的,并解决引言中提出的问题。
Local Security Context Network Security Context?
本地安全上下文 网络安全上下文?
Let’s consider a scenario where our user is capsule.corp\yamcha and we want to interact with a remote system to which only capsule.corp\bulma has access. In this example, we have Bulma’s password, but the account is affected by a deny logon policy in our current system.
让我们考虑一个场景,我们的用户是 capsule.corp\yamcha,我们希望与只有 capsule.corp\bulma 有权访问的远程系统进行交互。在此示例中,我们有 Bulma 的密码,但该帐户受到当前系统中拒绝登录策略的影响。
If we attempt to run a cmd.exe process with runas using Bulma’s credentials, the result will be something like this:
如果我们尝试使用 Bulma 的凭据使用 runas 运行 cmd.exe 进程,结果将如下所示:
The netonly flag is intended for these scenarios. With this flag we can create a process where we remain Yamcha at the local level, while we become Bulma at the network level, allowing us to interact with the remote system.
netonly 标志适用于这些方案。有了这个标志,我们可以创建一个进程,在该进程中,我们在本地级别保持 Yamcha,而在网络级别成为 Bulma,从而允许我们与远程系统进行交互。
In this example, Yamcha and Vegeta were users from the same domain and we could circumvent the deny log on policy by using the netonly flag. This flag is also very handy for situations where you have credentials belonging to a local user from a remote system, or to a domain user from an untrusted domain.
在此示例中,Yamcha 和 Vegeta 是来自同一域的用户,我们可以使用 netonly 标志来规避拒绝登录策略。对于属于远程系统中的本地用户或不受信任域中的域用户的凭据的情况,此标志也非常方便。
The fundamental thing to understand here is Windows will not validate the credentials you specify to runas /netonly, it will just make sure they are used when the process interacts with the network. That’s why we can bypass deny log on policies with runas /netonly, and also use credentials belonging to users outside our current system or from untrusted domains.
这里要了解的基本是,Windows 不会验证您指定为 runas /netonly 的凭据,它只会确保在进程与网络交互时使用它们。这就是为什么我们可以使用 runas /netonly 绕过拒绝登录策略,还可以使用属于当前系统外部或不受信任域的用户的凭据。
Now… How does runas manage to create a process where we are one identity in the local system, and another identity in the network?
现在。。。runas 如何设法创建一个流程,在该流程中,我们在本地系统中是一个身份,在网络中是另一个身份?
If we extract the strings of the program, we will see the presence of CreateProcessWithLogonW.
如果我们提取程序的字符串,我们将看到 CreateProcessWithLogonW 的存在。
$ strings runas.exe | grep -i createprocess CreateProcessAsUserW CreateProcessWithLogonW
A simple lookup of the function shows that runas is probably using it to create a new process with the credentials specified as arguments.
对该函数的简单查找表明,runas 可能正在使用它来创建一个新进程,并将凭据指定为参数。
Reading the documentation, we will find a LOGON_NETCREDENTIALS_ONLY flag which allows the creation of processes in a similar way to what we saw with netonly. We can safely assume that this flag is the one used by runas when we specify /netonly.
阅读文档,我们将发现一个LOGON_NETCREDENTIALS_ONLY标志,它允许以类似于我们在 netonly 中看到的方式创建流程。我们可以有把握地假设这个标志是 runas 在指定 /netonly 时使用的标志。
The Win32 API provides another function very similar to CreateProcessWithLogonW, but without the process creation logic. This function is called LogonUserA.
Win32 API 提供了另一个与 CreateProcessWithLogonW 非常相似的函数,但没有进程创建逻辑。此函数称为 LogonUserA。
LogonUserA is solely responsible for creating a new security context from given credentials. This is the function that make_token leverages and is commonly used along with the LOGON32_LOGON_NEW_CREDENTIALS logon type to create a netonly security context (we can see this in the implementations of open source C2 frameworks).
LogonUserA 全权负责从给定凭据创建新的安全上下文。这是make_token利用的功能,通常与LOGON32_LOGON_NEW_CREDENTIALS登录类型一起使用,以创建 netonly 安全上下文(我们可以在开源 C2 框架的实现中看到这一点)。
To understand how it is possible to create a process with two distinct “identities” (local/network), it is fundamental to become familiar with two important components of Windows authentication: logon sessions and access tokens.
若要了解如何创建具有两个不同“标识”(本地/网络)的进程,必须熟悉 Windows 身份验证的两个重要组件:登录会话和访问令牌。
Logon Sessions Access Tokens
登录会话访问令牌
When a user authenticates to a Windows system, a process similar to the image below occurs. At a high level, the user’s credentials are validated by the appropriate authentication package, typically Kerberos or NTLM. A new logon session is then created with a unique identifier, and the identifier along with information about the user is sent to the Local Security Authority (LSA) component. Finally, LSA uses this information to create an access token for the user.
当用户对 Windows 系统进行身份验证时,将发生类似于下图的过程。在较高级别上,用户的凭据由相应的身份验证包(通常是 Kerberos 或 NTLM)进行验证。然后,使用唯一标识符创建新的登录会话,并将该标识符以及有关用户的信息发送到本地安全机构 (LSA) 组件。最后,LSA 使用此信息为用户创建访问令牌。
Regarding access tokens, they are objects that represent the local security context of an account and are always associated with a process or thread of the system. These objects contain information about the user such as their security identifier, privileges, or the groups to which they belong. Windows performs access control decisions based on the information provided by access tokens and the rules configured in the discretionary access control list (DACL) of target objects.
关于访问令牌,它们是表示帐户的本地安全上下文的对象,并且始终与系统的进程或线程相关联。这些对象包含有关用户的信息,例如其安全标识符、权限或他们所属的组。Windows 根据访问令牌提供的信息和目标对象的任意访问控制列表 (DACL) 中配置的规则执行访问控制决策。
An example is shown below where two processes – one from Attl4s and one from Wint3r – attempt to read the “passwords.txt” file. As can be seen, the Attl4s process is able to read the file due to the second rule (Attl4s is a member of Administrators), while the Wint3r process is denied access because of the first rule (Wint3r has identifier 1004).
Regarding logon sessions, their importance stems from the fact that if an authentication results in cached credentials, they will be associated with a logon session. The purpose of cached credentials is to enable Windows to provide a single sign-on (SSO) experience where the user does not need to re-enter their credentials when accessing a remote service, such as a shared folder on the network.
关于登录会话,它们的重要性源于这样一个事实,即如果身份验证导致缓存凭据,它们将与登录会话相关联。缓存凭据的目的是使 Windows 能够提供单一登录 (SSO) 体验,用户在访问远程服务(如网络上的共享文件夹)时无需重新输入其凭据。
As an interesting note, when Mimikatz dumps credentials from Windows authentication packages (e.g., sekurlsa::logonpasswords), it iterates through all the logon sessions in the system to extract their information.
有趣的是,当 Mimikatz 从 Windows 身份验证包(例如 sekurlsa::logonpasswords)转储凭据时,它会遍历系统中的所有登录会话以提取其信息。
The following image illustrates the relationship between processes, tokens, logon sessions, and cached credentials:
下图说明了进程、令牌、登录会话和缓存凭据之间的关系:
The key takeaways are: 关键要点是:
- Access tokens represent the local security context of an authenticated user. The information in these objects is used by the local system to make access control decisions
访问令牌表示经过身份验证的用户的本地安全上下文。本地系统使用这些对象中的信息来做出访问控制决策 - Logon sessions with cached credentials represent the network security context of an authenticated user. These credentials are automatically and transparently used by Windows when the user wants to access remote services that support Windows authentication
具有缓存凭据的登录会话表示经过身份验证的用户的网络安全上下文。当用户想要访问支持 Windows 身份验证的远程服务时,Windows 会自动透明地使用这些凭据
What runas /netonly and make_token do under the hood is creating an access token similar to the one of the current user (Yamcha) along with a logon session containing the credentials of the alternate user (Bulma). This enables the dual identity behaviour where the local identity remains the same, while the network identity changes to that of the alternate user.
runas /netonly 和 make_token 在后台所做的是创建一个类似于当前用户 (Yamcha) 的访问令牌,以及一个包含备用用户 (Bulma) 凭据的登录会话。这将启用双重标识行为,其中本地标识保持不变,而网络标识更改为备用用户的标识。
As stated before, the fact that runas netonly or make_token do not validate credentials has many benefits. For example we can use credentials for users who have been denied local access, and also for accounts that the local system does not know and cannot validate (e.g. a local user from other computer or an account from an untrusted domain). Additionally, we can create “sacrificial” logon sessions with invalid credentials, which allows us to manipulate Kerberos tickets without overwriting the ones stored in the original logon session.
However, this lack of validation can also result in unpleasant surprises, for example in the case of a company using an authenticated proxy. If we make a mistake when inserting credentials to make_token, or create sacrificial sessions carelessly, we can end up with locked accounts or losing our Beacon because it is no longer able to exit through the proxy!
Administrative Context or Not!?
行政背景与否!?
Raphael mentioned that, in order to use a token created by make_token, an administrative context was needed.
Raphael 提到,为了使用 make_token 创建的令牌,需要一个管理上下文。
“The problem with make_token, as much as steal_token, is it requires you to be in an administrator context before you can actually do anything with that token”
“make_token的问题在于,steal_token,它要求你先处于管理员上下文中,然后才能实际使用该令牌做任何事情”
Do we really need an administrative context? The truth is there are situations where this statement is not entirely accurate.
我们真的需要行政环境吗?事实是,在某些情况下,这种说法并不完全准确。
As far as we know, the make_token command uses the LogonUserA function (along with the LOGON32_LOGON_NEW_CREDENTIALS flag) to create a new access token similar to that of the user, but linked to a new logon session containing the alternate user’s credentials. The command does not stop there though, as LogonUserA only returns a handle to the new token; we have to do something with that token!
据我们所知,make_token 命令使用 LogonUserA 函数(以及 LOGON32_LOGON_NEW_CREDENTIALS 标志)创建一个类似于用户的新访问令牌,但链接到包含备用用户凭据的新登录会话。但是,该命令并不止于此,因为 LogonUserA 仅返回新令牌的句柄;我们必须用这个令牌做点什么!
Let’s suppose our goal is to create new processes with the context of the new token.
假设我们的目标是使用新令牌的上下文创建新流程。
Creating Processes with a Token
使用令牌创建进程
If we review the Windows API, we will spot two functions that support a token handle as an argument to create a new process:
如果我们查看 Windows API,我们将发现两个支持令牌句柄作为参数来创建新进程的函数:
Reading the documentation of these functions, however, will show the following statements:
但是,阅读这些函数的文档将显示以下语句:
“Typically, the process that calls the CreateProcessAsUser function must have the SE_INCREASE_QUOTA_NAME privilege and may require the SE_ASSIGNPRIMARYTOKEN_NAME privilege if the token is not assignable.”
“通常,调用 CreateProcessAsUser 函数的进程必须具有SE_INCREASE_QUOTA_NAME权限,如果令牌不可分配,则可能需要SE_ASSIGNPRIMARYTOKEN_NAME权限。”“The process that calls CreateProcessWithTokenW must have the SE_IMPERSONATE_NAME privilege.”
“调用 CreateProcessWithTokenW 的进程必须具有SE_IMPERSONATE_NAME权限。”
This is where Raphael’s statement makes sense. Even if we can create a token with a non-privileged user through LogonUserA, we will not be able to use that token to create new processes. To do so, Microsoft indicates we need administrative privileges such as SE_ASSIGNPRIMARYTOKEN_NAME, SE_INCREASE_QUOTA_NAME or SE_IMPERSONATE_NAME.
这就是拉斐尔的说法是有道理的。即使我们可以通过 LogonUserA 使用非特权用户创建令牌,我们也无法使用该令牌创建新进程。为此,Microsoft表示我们需要管理权限,例如 SE_ASSIGNPRIMARYTOKEN_NAME、SE_INCREASE_QUOTA_NAME 或 SE_IMPERSONATE_NAME。
When using make_token in a non-privileged context and attempting to create a process (e.g., shell dir \dc01.capsule.corp\C$), Beacon will silently fail and fall back to ignoring the token to create the process. That’s one of the reasons why sometimes it appears that the impersonation is not working properly.
在非特权上下文中使用 make_token 并尝试创建进程(例如,shell dir \dc01.capsule.corp\C$)时,Beacon 将静默失败并回退到忽略令牌以创建进程。这就是为什么有时模拟似乎无法正常工作的原因之一。
As a note, agents like Meterpreter do give more information about the failure:
As such, we could rephrase Raphael’s statement as follows:
“The problem with make_token is it requires you to be in an administrator context before you can actually create processes with that token”
“make_token的问题在于,它要求你先处于管理员上下文中,然后才能实际使用该令牌创建进程”
The perceptive reader may now wonder… What happens if I operate within my current process instead of creating new ones? Do I still need administrative privileges?
敏锐的读者现在可能会想知道……如果我在当前流程中操作而不是创建新流程,会发生什么?我还需要管理权限吗?
Access Tokens + Thread Impersonation
访问令牌 + 线程模拟
The Windows API provides functions like ImpersonateLoggedOnUser or SetThreadToken to allow a thread within a process to impersonate the security context provided by an access token.
Windows API 提供 ImpersonateLoggedOnUser 或 SetThreadToken 等函数,以允许进程中的线程模拟访问令牌提供的安全上下文。
In addition to keeping the token handle for future process creations, make_token also employs functions like these to acquire the token’s security context in the thread where Beacon is running. Do we need administrative privileges for this? Not at all.
除了保留令牌句柄以供将来创建进程外,make_token 还使用此类函数在运行 Beacon 的线程中获取令牌的安全上下文。我们是否需要管理权限?一点也不。
As can be seen in the image below, we meet point number three:
如下图所示,我们遇到了第三点:
This means that any command or tool executed from the thread where Beacon is running will benefit from the security context created by make_token, without requiring an administrative context. This includes many of the native commands, as well as capabilities implemented as Beacon Object Files (BOFs).
这意味着从运行 Beacon 的线程执行的任何命令或工具都将受益于 make_token 创建的安全上下文,而无需管理上下文。这包括许多本机命令,以及作为信标对象文件 (BOF) 实现的功能。
Closing Thoughts 结束语
Considering all the information above, we could do a more detailed description of make_token as follows:
考虑到上述所有信息,我们可以对make_token进行更详细的描述,如下所示:
The make_token command creates an access token similar to the one of the current user, along with a logon session containing the credentials specified as arguments. This enables a dual identity where nothing changes locally (we remain the same user), but in the network we will be represented by the credentials of the alternate user (note that make_token does not validate the credentials specified). Once the token is created, Beacon impersonates it to benefit from the new security context when running inline capabilities.
make_token 命令创建类似于当前用户的访问令牌,以及包含指定为参数的凭据的登录会话。这启用了双重身份,其中本地没有任何变化(我们仍然是同一用户),但在网络中,我们将由备用用户的凭据表示(请注意,make_token不会验证指定的凭据)。创建令牌后,Beacon 会模拟它,以便在运行内联功能时从新的安全上下文中受益。The token handle is also stored by Beacon to be used in new process creations, which requires an administrative context. If a process creation is attempted with an unprivileged user, Beacon will ignore the token and fall back to a regular process creation.
令牌句柄也由 Beacon 存储,以便在新流程创建中使用,这需要管理上下文。如果尝试使用非特权用户创建进程,Beacon 将忽略令牌并回退到常规进程创建。
As a final note, we would like to point out that in 2019 Raphael Mudge released a new version of his awesome Red Team Ops with Cobalt Strike course. In the eighth video, make_token was once again discussed, but this time showing a demo with an unprivileged user. While this demonstrated that running the command did not require an administrative context, it did not explain much more about it.
最后,我们想指出的是,在 2019 年,Raphael Mudge 发布了他令人敬畏的 Red Team Ops with Cobalt Strike 课程的新版本。在第八个视频中,再次讨论了make_token,但这次展示了一个非特权用户的演示。虽然这表明运行该命令不需要管理上下文,但它并没有对此进行更多解释。
We hope this article has answered any questions you may have had about make_token.
我们希望本文能回答您对make_token的任何问题。
Sources 来源
- https://www.cobaltstrike.com/blog/windows-access-tokens-and-alternate-credentials
- https://www.youtube.com/watch?v=9h4MWM9jtEI
- https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithlogonw
- https://learn.microsoft.com/es-es/windows/win32/api/winbase/nf-winbase-logonusera
- https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw
- https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
- https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-impersonateloggedonuser
- https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadtoken
- https://docs.microsoft.com/en-us/windows/desktop/secauthn/lsa-logon-sessions
- https://docs.microsoft.com/en-us/windows/desktop/secauthz/access-tokens
- https://attl4s.github.io/assets/pdf/Understanding_Windows_Lateral_Movements_2023.pdf
原文始发于Simone Salucci and Daniel Lopez Jimenez:Demystifying Cobalt Strike’s “make_token” Command