【T1543.003】利用 ACL 隐藏恶意 Windows 服务
看到一篇文章讲述了通过设置 DACL 隐藏 Windows 服务信息,便做一下总结。
技术原理
系统中每个 Service 在 Windows 中都属于一种对象(Object),用户在访问对象时的权限会被和对象绑定的安全描述信息(Security Descriptors)所限定,例如 修改、删除、读写等权限。
既然 Service 也是一种对象,那么也可以通过某种方式对服务的安全描述信息进行适当的修改,使某一类用户无法查看、修改该服务的信息以及停止删除服务。
安全描述信息结构与设置方式
经 MSDN 描述可知,一个安全描述信息结构体包括如下信息:
- SIDs: 该对象的拥有者以及所属组信息
- DACL: 用于允许、拒绝指定用户/组对该对象的访问权限
- SACL: 为试图访问对象的访问类型生成审计日志
- 一组控制位: 用于限定安全描述符或其单个成员的含义。
对应的结构体定义 SECURITY_DESCRIPTOR :
typedef struct _SECURITY_DESCRIPTOR {
BYTE Revision;
BYTE Sbz1;
SECURITY_DESCRIPTOR_CONTROL Control;
PSID Owner;
PSID Group;
PACL Sacl;
PACL Dacl;
} SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
但是程序或用户并不能直接控制这些信息,必须通过 Windows 提供的 API 进行设置或查询。
这就导致由于对象种类的不同,设置这些安全描述信息的方式上可能各有不同:
- 有的可以直接通过 UI 界面进行设置,例如文件对象。
- 有的需要通过 Windows 内置的命令进行设置,例如服务对象。
- 有的则必须使用系统 API 完成相关工作,例如进程、线程。
对于这些安全描述信息,Windows 单独提供了一种简单的字符串表述形式,官方称其为 “安全描述符定义语言”(SDDL),下面着重总结一下 SDDL 的东西。
安全描述符定义语言(SDDL)
参考 MSDN 可知,SDDL 和 SECURITY_DESCRIPTOR 结构体之间的转换是通过两个 API 进行互转的:
- ConvertSecurityDescriptorToStringSecurityDescriptor
- ConvertStringSecurityDescriptorToSecurityDescriptor
所以,这为安全描述符的设置提供了很大的便利,下面先了解一些基本概念。
基本概念
- Securable Object: 安全对象,是拥有 SD (安全描述符)的 Windows 对象,所有被命名的 Windows 对象都是安全对象,但是一些没有命名的对象也是安全对象,如:进程和线程,也有安全描述符 SD。
- SID: 每个用户/组 SID, 用于标识用户/组。
- ACL: 访问控制链表,该链表其实有两个表组成:DACL 和 SACL。
- DACL: 自由访问控制链表,用于允许、拒绝指定用户/组对该对象的访问权限。
- SACL: 系统访问控制链表,为试图访问对象的访问类型生成审计日志。
- ACE: 访问控制项,是构成访问控制链表的最小单元。
- SDDL: 安全描述符定义语言,一种利用字符串表示安全描述信息的格式语言。
ACE
ACE(访问控制项)是构成 DACL(自由访问控制链表)和 SACL(系统访问控制链表)的最小存储单元,它的格式如下:
ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid;(resource_attribute)
为空代表不限制,常见的策略组合可能是这样的:
ace_type;;rights;;;account_sid;
ace_type
ace_type 有很多,参见 MSDN,该字段可以表示对这条规则应用的策略类型,例如:
"A" SDDL_ACCESS_ALLOWED ACCESS_ALLOWED_ACE_TYPE
"D" SDDL_ACCESS_DENIED ACCESS_DENIED_ACE_TYPE
rights
根据 MSDN 可看出,针对不同类型的对象,权限常量的名字不是很统一,Wayne Martin 在他的文章中给出了一部分 ADS、SCM、Service、value、SDDL 的映射关系:
"CC" ADS_RIGHT_DS_CREATE_CHILD = 0x1, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG
"DC" ADS_RIGHT_DS_DELETE_CHILD = 0x2, SC_MANAGER_CREATE_SERVICE, SERVICE_CHANGE_CONFIG
"LC" ADS_RIGHT_ACTRL_DS_LIST = 0x4, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_STATUS
"SW" ADS_RIGHT_DS_SELF = 0x8, SC_MANAGER_LOCK, SERVICE_ENUMERATE_DEPENDENTS
"RP" ADS_RIGHT_DS_READ_PROP = 0x10, SC_MANAGER_QUERY_LOCK_STATUS, SERVICE_START,
"WP" ADS_RIGHT_DS_WRITE_PROP = 0x20, SC_MANAGER_MODIFY_BOOT_CONFIG, SERVICE_STOP
"DT" ADS_RIGHT_DS_DELETE_TREE = 0x40, SERVICE_PAUSE_CONTINUE
"LO" ADS_RIGHT_DS_LIST_OBJECT = 0x80, SERVICE_INTERROGATE
"CR" ADS_RIGHT_DS_CONTROL_ACCESS = 0x100 SERVICE_USER_DEFINED_CONTROL
"RC" READ_CONTROL = 0x20000 READ_CONTROL
"SD" ADS_RIGHT_DELETE = 0x10000 DELETE
SCM 和 Servcie 的权限描述符,来自 Service Security and Access Rights - MSDN :
SERVICE_QUERY_CONFIG (0x0001) Required to call the QueryServiceConfig and QueryServiceConfig2 functions to query the service configuration.
SERVICE_CHANGE_CONFIG (0x0002) Required to call the ChangeServiceConfig or ChangeServiceConfig2 function to change the service configuration. Because this grants the caller the right to change the executable file that the system runs, it should be granted only to administrators.
SERVICE_QUERY_STATUS (0x0004) Required to call the QueryServiceStatusEx function to ask the service control manager about the status of the service.
SERVICE_ENUMERATE_DEPENDENTS (0x0008) Required to call the EnumDependentServices function to enumerate all the services dependent on the service.
SERVICE_START (0x0010) Required to call the StartService function to start the service.
SERVICE_STOP (0x0020) Required to call the ControlService function to stop the service.
SERVICE_PAUSE_CONTINUE (0x0040) Required to call the ControlService function to pause or continue the service.
SERVICE_INTERROGATE (0x0080) Required to call the ControlService function to ask the service to report its status immediately.
SERVICE_USER_DEFINED_CONTROL(0x0100) Required to call the ControlService function to specify a user-defined control code.
SERVICE_ALL_ACCESS (0xF01FF) Includes STANDARD_RIGHTS_REQUIRED in addition to all access rights in this table.
READ_CONTROL Required to call the QueryServiceObjectSecurity function to query the security descriptor of the service object.
SC_MANAGER_CONNECT (0x0001) Required to connect to the service control manager.
SC_MANAGER_CREATE_SERVICE (0x0002) Required to call the CreateService function to create a service object and add it to the database.
SC_MANAGER_ENUMERATE_SERVICE (0x0004) Required to call the EnumServicesStatusEx function to list the services that are in the database.
SC_MANAGER_LOCK (0x0008) Required to call the LockServiceDatabase function to acquire a lock on the database.
SC_MANAGER_QUERY_LOCK_STATUS (0x0010)
SC_MANAGER_MODIFY_BOOT_CONFIG (0x0020) Required to call the NotifyBootConfigStatus function.
SC_MANAGER_ALL_ACCESS (0xF003F) Includes STANDARD_RIGHTS_REQUIRED, in addition to all access rights in this table.
SDDL 和 ADS 关系映射,来自 ACE Strings - MSDN :
"RC" SDDL_READ_CONTROL READ_CONTROL
"RP" SDDL_READ_PROPERTY ADS_RIGHT_DS_READ_PROP
"WP" SDDL_WRITE_PROPERTY ADS_RIGHT_DS_WRITE_PROP
"CC" SDDL_CREATE_CHILD ADS_RIGHT_DS_CREATE_CHILD
"DC" SDDL_DELETE_CHILD ADS_RIGHT_DS_DELETE_CHILD
"LC" SDDL_LIST_CHILDREN ADS_RIGHT_ACTRL_DS_LIST
"SW" SDDL_SELF_WRITE ADS_RIGHT_DS_SELF
"LO" SDDL_LIST_OBJECT ADS_RIGHT_DS_LIST_OBJECT
"DT" SDDL_DELETE_TREE ADS_RIGHT_DS_DELETE_TREE
"CR" SDDL_CONTROL_ACCESS ADS_RIGHT_DS_CONTROL_ACCESS
ADS 的枚举项值,来自 ads_rights_enum - systemmanager:
typedef enum
{
ADS_RIGHT_DELETE = 0x10000,
ADS_RIGHT_READ_CONTROL = 0x20000,
ADS_RIGHT_WRITE_DAC = 0x40000,
ADS_RIGHT_WRITE_OWNER = 0x80000,
ADS_RIGHT_SYNCHRONIZE = 0x100000,
ADS_RIGHT_ACCESS_SYSTEM_SECURITY = 0x1000000,
ADS_RIGHT_GENERIC_READ = 0x80000000,
ADS_RIGHT_GENERIC_WRITE = 0x40000000,
ADS_RIGHT_GENERIC_EXECUTE = 0x20000000,
ADS_RIGHT_GENERIC_ALL = 0x10000000,
ADS_RIGHT_DS_CREATE_CHILD = 0x1,
ADS_RIGHT_DS_DELETE_CHILD = 0x2,
ADS_RIGHT_ACTRL_DS_LIST = 0x4,
ADS_RIGHT_DS_SELF = 0x8,
ADS_RIGHT_DS_READ_PROP = 0x10,
ADS_RIGHT_DS_WRITE_PROP = 0x20,
ADS_RIGHT_DS_DELETE_TREE = 0x40,
ADS_RIGHT_DS_LIST_OBJECT = 0x80,
ADS_RIGHT_DS_CONTROL_ACCESS = 0x100
} ADS_RIGHTS_ENUM;
account_sid
SID 用于标识 拥有者 或 所属组,在 ACE 中的 account_sid 可以是一个 SID(S-R-I-S-S)或者在 Sddl.h 中定义的字符串常量,这些字符串常量又称 “周知 SID”:
"AN" SDDL_ANONYMOUS Anonymous logon. The corresponding RID is SECURITY_ANONYMOUS_LOGON_RID.
"AO" SDDL_ACCOUNT_OPERATORS Account operators. The corresponding RID is DOMAIN_ALIAS_RID_ACCOUNT_OPS.
"AU" SDDL_AUTHENTICATED_USERS Authenticated users. The corresponding RID is SECURITY_AUTHENTICATED_USER_RID.
"BA" SDDL_BUILTIN_ADMINISTRATORS Built-in administrators. The corresponding RID is DOMAIN_ALIAS_RID_ADMINS.
"BG" SDDL_BUILTIN_GUESTS Built-in guests. The corresponding RID is DOMAIN_ALIAS_RID_GUESTS.
"BO" SDDL_BACKUP_OPERATORS Backup operators. The corresponding RID is DOMAIN_ALIAS_RID_BACKUP_OPS.
"BU" SDDL_BUILTIN_USERS Built-in users. The corresponding RID is DOMAIN_ALIAS_RID_USERS.
"CA" SDDL_CERT_SERV_ADMINISTRATORS Certificate publishers. The corresponding RID is DOMAIN_GROUP_RID_CERT_ADMINS.
"CD" SDDL_CERTSVC_DCOM_ACCESS Users who can connect to certification authorities using Distributed Component Object Model (DCOM). The corresponding RID is DOMAIN_ALIAS_RID_CERTSVC_DCOM_ACCESS_GROUP.
"CG" SDDL_CREATOR_GROUP Creator group. The corresponding RID is SECURITY_CREATOR_GROUP_RID.
"CO" SDDL_CREATOR_OWNER Creator owner. The corresponding RID is SECURITY_CREATOR_OWNER_RID.
"DA" SDDL_DOMAIN_ADMINISTRATORS Domain administrators. The corresponding RID is DOMAIN_GROUP_RID_ADMINS.
"DC" SDDL_DOMAIN_COMPUTERS Domain computers. The corresponding RID is DOMAIN_GROUP_RID_COMPUTERS.
"DD" SDDL_DOMAIN_DOMAIN_CONTROLLERS Domain controllers. The corresponding RID is DOMAIN_GROUP_RID_CONTROLLERS.
"DG" SDDL_DOMAIN_GUESTS Domain guests. The corresponding RID is DOMAIN_GROUP_RID_GUESTS.
"DU" SDDL_DOMAIN_USERS Domain users. The corresponding RID is DOMAIN_GROUP_RID_USERS.
"EA" SDDL_ENTERPRISE_ADMINS Enterprise administrators. The corresponding RID is DOMAIN_GROUP_RID_ENTERPRISE_ADMINS.
"ED" SDDL_ENTERPRISE_DOMAIN_CONTROLLERS Enterprise domain controllers. The corresponding RID is SECURITY_SERVER_LOGON_RID.
"HI" SDDL_ML_HIGH High integrity level. The corresponding RID is SECURITY_MANDATORY_HIGH_RID.
"IU" SDDL_INTERACTIVE Interactively logged-on user. This is a group identifier added to the token of a process when it was logged on interactively. The corresponding logon type is LOGON32_LOGON_INTERACTIVE. The corresponding RID is SECURITY_INTERACTIVE_RID.
"LA" SDDL_LOCAL_ADMIN Local administrator. The corresponding RID is DOMAIN_USER_RID_ADMIN.
"LG" SDDL_LOCAL_GUEST Local guest. The corresponding RID is DOMAIN_USER_RID_GUEST.
"LS" SDDL_LOCAL_SERVICE Local service account. The corresponding RID is SECURITY_LOCAL_SERVICE_RID.
"LW" SDDL_ML_LOW Low integrity level. The corresponding RID is SECURITY_MANDATORY_LOW_RID.
"ME" SDDL_MLMEDIUM Medium integrity level. The corresponding RID is SECURITY_MANDATORY_MEDIUM_RID.
"MU" SDDL_PERFMON_USERS Performance Monitor users.
"NO" SDDL_NETWORK_CONFIGURATION_OPS Network configuration operators. The corresponding RID is DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS.
"NS" SDDL_NETWORK_SERVICE Network service account. The corresponding RID is SECURITY_NETWORK_SERVICE_RID.
"NU" SDDL_NETWORK Network logon user. This is a group identifier added to the token of a process when it was logged on across a network. The corresponding logon type is LOGON32_LOGON_NETWORK. The corresponding RID is SECURITY_NETWORK_RID.
"PA" SDDL_GROUP_POLICY_ADMINS Group Policy administrators. The corresponding RID is DOMAIN_GROUP_RID_POLICY_ADMINS.
"PO" SDDL_PRINTER_OPERATORS Printer operators. The corresponding RID is DOMAIN_ALIAS_RID_PRINT_OPS.
"PS" SDDL_PERSONAL_SELF Principal self. The corresponding RID is SECURITY_PRINCIPAL_SELF_RID.
"PU" SDDL_POWER_USERS Power users. The corresponding RID is DOMAIN_ALIAS_RID_POWER_USERS.
"RC" SDDL_RESTRICTED_CODE Restricted code. This is a restricted token created using the CreateRestrictedToken function. The corresponding RID is SECURITY_RESTRICTED_CODE_RID.
"RD" SDDL_REMOTE_DESKTOP Terminal server users. The corresponding RID is DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS.
"RE" SDDL_REPLICATOR Replicator. The corresponding RID is DOMAIN_ALIAS_RID_REPLICATOR.
"RO" SDDL_ENTERPRISE_RO_DCs Enterprise Read-only domain controllers. The corresponding RID is DOMAIN_GROUP_RID_ENTERPRISE_READONLY_DOMAIN_CONTROLLERS.
"RS" SDDL_RAS_SERVERS RAS servers group. The corresponding RID is DOMAIN_ALIAS_RID_RAS_SERVERS.
"RU" SDDL_ALIAS_PREW2KCOMPACC Alias to grant permissions to accounts that use applications compatible with operating systems previous to Windows 2000. The corresponding RID is DOMAIN_ALIAS_RID_PREW2KCOMPACCESS.
"SA" SDDL_SCHEMA_ADMINISTRATORS Schema administrators. The corresponding RID is DOMAIN_GROUP_RID_SCHEMA_ADMINS.
"SI" SDDL_ML_SYSTEM System integrity level. The corresponding RID is SECURITY_MANDATORY_SYSTEM_RID.
"SO" SDDL_SERVER_OPERATORS Server operators. The corresponding RID is DOMAIN_ALIAS_RID_SYSTEM_OPS.
"SU" SDDL_SERVICE Service logon user. This is a group identifier added to the token of a process when it was logged as a service. The corresponding logon type is LOGON32_LOGON_SERVICE. The corresponding RID is SECURITY_SERVICE_RID.
"SY" SDDL_LOCAL_SYSTEM Local system. The corresponding RID is SECURITY_LOCAL_SYSTEM_RID.
"WD" SDDL_EVERYONE Everyone. The corresponding RID is SECURITY_WORLD_RID.
ACL 继承
暂略。
利用 SDDL 设置隐藏服务
对于正常添加的普通服务,使用 powershell 或 sc.exe 可以直接查看该服务的信息:
PS C:\WINDOWS\system32> Get-Service -Name SWCUEngine
Status Name DisplayName
------ ---- -----------
Running SWCUEngine SWCUEngine
通过修改服务的 SD 之后,查询该服务信息将报错 ”ObjectNotFound“,以此达到隐藏的目的:
PS C:\WINDOWS\system32> & $env:SystemRoot\System32\sc.exe sdset SWCUEngine "D:(D;;DCLCWPDTSD;;;IU)(D;;DCLCWPDTSD;;;SU)(D;;DCLCWPDTSD;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
[SC] SetServiceObjectSecurity SUCCESS
PS C:\WINDOWS\system32> Get-Service -Name SWCUEngine
Get-Service : Cannot find any service with service name 'SWCUEngine'.
At line:1 char:1
+ Get-Service -Name SWCUEngine
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (SWCUEngine:String) [Get-Service], ServiceCommandException
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
看一下其中设置的 ACE,“D:”表示 DACL 表,多个 ACE 之间以小括号分割。
“D;;DCLCWPDTSD;;;IU” 代表 拒绝交互式用户以下权限:
- DC: SERVICE_CHANGE_CONFIG 修改服务配置
- LC: SERVICE_QUERY_STATUS 查询服务状态
- WP: SERVICE_STOP 停止服务
- DT: SERVICE_PAUSE_CONTINUE 暂停和启动服务
- SD: DELETE 删除服务
“S:”表示 SACL 表,“AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD”表示任何人对该服务的失败操作都进行审计记录。
隐藏后,无论是 services.exe、Get-Service、sc query 或任何其它关于服务的控制工具均无法检索出对应信息,效果如下:
#以下三种查询服务信息的手段均无信息
PS C:\WINDOWS\system32> Get-Service | Select-Object Name | Select-String -Pattern 'SWCUEngine'
PS C:\WINDOWS\system32> Get-WmiObject Win32_Service | Select-String -Pattern 'SWCUEngine'
PS C:\WINDOWS\system32> & $env:SystemRoot\System32\sc.exe query | Select-String -Pattern 'SWCUEngine'
PS C:\WINDOWS\system32
如果蓝队知道恶意服务的名称的话,可以通过停止该服务的回显进行判断,例如:
# 停止不存在的JoshNoSuchService服务得到 InvalidOperationException 异常
PS C:\WINDOWS\system32> Set-Service -Name JoshNoSuchService -Status Stopped
Set-Service : Service JoshNoSuchService was not found on computer '.'.
At line:1 char:1
+ Set-Service -Name JoshNoSuchService -Status Stopped
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (.:String) [Set-Service], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.SetServiceCommand
# 停止存在但被隐藏的SWCUEngine服务得到ServiceCommandException异常
PS C:\WINDOWS\system32> Set-Service -Name SWCUEngine -Status Stopped
Set-Service : Service 'SWCUEngine (SWCUEngine)' cannot be configured due to the following error: Access is denied
At line:1 char:1
+ Set-Service -Name SWCUEngine -Status Stopped
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (System.ServiceProcess.ServiceController:ServiceController) [Set-Service],
ServiceCommandException
+ FullyQualifiedErrorId : CouldNotSetService,Microsoft.PowerShell.Commands.SetServiceCommand
判断存在后,取消隐藏的方式也很简单:
# 使用sc.exe修改目标服务的SDDL语法实现取消隐藏
PS C:\WINDOWS\system32> & $env:SystemRoot\System32\sc.exe sdset SWCUEngine "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
[SC] SetServiceObjectSecurity SUCCESS
# 现在使用 Get-Serice 指令可以查看到对应的服务信息
PS C:\WINDOWS\system32> Get-Service -Name 'SWCUEngine'
Status Name DisplayName
------ ---- -----------
Running SWCUEngine SWCUEngine
引用
- https://xz.aliyun.com/t/8420
- https://www.sans.org/blog/red-team-tactics-hiding-windows-services/
- http://waynes-world-it.blogspot.com/2009/10/service-control-manager-security-for.html
- https://docs.microsoft.com/en-us/windows/win32/services/service-security-and-access-rights
- https://docs.microsoft.com/zh-tw/windows/win32/secauthz/sid-strings
- https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings
- https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptora
- https://www.installsetupconfig.com/win32programming/accesscontrollistacl4.pdf