Sleuth Kit Java Bindings (JNI)  4.12.1
Java bindings for using The Sleuth Kit
All Classes Namespaces Files Functions Variables Pages
WindowsAccountUtils.java
Go to the documentation of this file.
1 /*
2  * Sleuth Kit Data Model
3  *
4  * Copyright 2021-2022 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.datamodel;
20 
21 import com.google.common.collect.ImmutableMap;
22 import com.google.common.collect.ImmutableSet;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30 import org.apache.commons.lang3.StringUtils;
31 import com.google.common.collect.HashBasedTable;
32 import com.google.common.collect.Table;
33 import java.util.Locale;
34 
43 final class WindowsAccountUtils {
44 
45 
46  final static String SPECIAL_WINDOWS_BACK_UP_POSTFIX = ".bak";
47 
48  // Windows sometimes uses a special NULL sid, when a users actual SID is unknown.
49  // Our SID comparisons should ignore it, and treat it as a null/blank.
50  final static String WINDOWS_NULL_SID = "S-1-0-0";
51 
52  // Windows uses SIDs for groups as well as users.
53  // We dont want to create "User" account for group SIDs.
54  // The lists here help us identify and weed out group SIDs when creating accounts.
55  private static final Set<String> GROUP_SIDS = ImmutableSet.of(
56  "S-1-1-0", // Everyone
57  "S-1-2-0", // Local - anyone who has logged on locally
58  "S-1-2-1", // Console Logon
59 
60  "S-1-3-1", // Creator
61  "S-1-3-4", // Owner rights
62 
63  "S-1-5-1", // Dialup
64  "S-1-5-2", // Network
65  "S-1-5-3", // Batch
66  "S-1-5-4", // Interactive
67  "S-1-5-6", // Service
68  "S-1-5-7", // Anonymous
69  "S-1-5-9", // Enterprise Domain Controllers
70 
71  "S-1-5-11", // Authenticated Users
72  "S-1-5-12", // Restricted Code - not a group but not a user SID either
73  "S-1-5-13", // Terminal Server Users
74  "S-1-5-14", // Remote Interactive Logon
75 
76  "S-1-5-15", // This Organization
77 
78  "S-1-5-80-0", // All Services
79  "S-1-5-83-0", // NT Virtual Machine\Virtual Machines
80  "S-1-5-90-0" // Windows Manager\Windows Manager Group
81 
82  );
83 
84  // Any SIDs with the following prefixes are group SID and should be excluded.
85  private static final Set<String> GROUP_SID_PREFIX = ImmutableSet.of(
86  "S-1-5-32", // Builtin
87  "S-1-5-87" // Task ID prefix
88 
89  );
90 
91  // SIDS that begin with a domain SID prefix and have on of these
92  private static final String NTAUTHORITY_SID_PREFIX = "S-1-5";
93  private static final String NTAUTHORITY_REALM_NAME = "NT AUTHORITY";
94 
95 
96  private static final Set<String> DOMAIN_GROUP_SID_SUFFIX = ImmutableSet.of(
97  "-512", // Domain Admins
98  "-513", // Domain Users
99 
100  "-514", // Domain Guests
101  "-515", // Domain Computers
102  "-516", // Domain Controllers
103  "-517", // Cert Publishers
104 
105  "-518", // Schema Admins
106  "-519", // Enterprise Admins
107  "-520", // Group Policy Creator Owners
108 
109  "-526", // Key Admins
110  "-527", // Enterprise Key Admins
111 
112  "-533", // RAS and IAS Servers
113 
114  // Windows 2008 and later
115  "-498", // Enterprise Read-only Domain Controllers
116  "-521", // Read-only Domain Controllers
117  "-571", // Allowed RODC Password Replication Group
118  "-572", // Denied RODC Password Replication Group
119 
120  // Windows 2012 and later
121  "-522" // Cloneable Domain Controllers
122  );
123 
124 
129  public static class WellKnownSidInfo {
130 
131  WellKnownSidInfo(boolean isUserSID, String addr, String realmName, String loginName, String description) {
132  this.realmAddr = addr;
133  this.isUserSID = isUserSID;
134  this.realmName = realmName;
135  this.loginName = this.isUserSID ? loginName : "";
136  this.description = description;
137  }
138 
139  private final String realmAddr; // realm identifier - S-1-5-18
140  private final boolean isUserSID; // is this a realm SID or a user SID
141  private final String realmName; // realm name
142  private final String loginName; // user login name, may be empty
143  private final String description; // description
144 
145  public String getRealmAddr() {
146  return realmAddr;
147  }
148 
149  public boolean isIsUserSID() {
150  return isUserSID;
151  }
152 
153  public String getRealmName() {
154  return realmName;
155  }
156 
157  public String getLoginName() {
158  return loginName;
159  }
160 
161  public String getDescription() {
162  return description;
163  }
164 
165 
166  }
167 
168  // These windows SID indicate well known windows accounts.
169  // Well known SIDs and account are handled slightly differently from the regular accounts:
170  // - We can assume and fill in SID from given account name, and vice versa.
171  // - We map account names in foreign languages (some known set) to english names, for these well known accounts.
172  private static final Map<String, WellKnownSidInfo> SPECIAL_SIDS_MAP = ImmutableMap.<String, WellKnownSidInfo>builder()
173  .put("S-1-5-17", new WellKnownSidInfo(true, "S-1-5", NTAUTHORITY_REALM_NAME, "IUSR", "IIS Default Account"))
174  .put("S-1-5-18", new WellKnownSidInfo(true, "S-1-5", NTAUTHORITY_REALM_NAME, "SYSTEM", "Local System Account"))
175  .put("S-1-5-19", new WellKnownSidInfo(true, "S-1-5", NTAUTHORITY_REALM_NAME, "LOCAL SERVICE", "Local Service Account"))
176  .put("S-1-5-20", new WellKnownSidInfo(true, "S-1-5", NTAUTHORITY_REALM_NAME, "NETWORK SERVICE", "Network Service Account"))
177  .build();
178 
179 
180  // These SID prefixes indicate well known windows accounts.
181  // - We can fill in the login names for these SID, as well as account user description.
182  private static final Map<String, WellKnownSidInfo> SPECIAL_SID_PREFIXES_MAP = ImmutableMap.<String, WellKnownSidInfo>builder()
183  .put("S-1-5-80", new WellKnownSidInfo(false, "S-1-5-80", "NT SERVICE", "", "NT Service Virtual Account"))
184  .put("S-1-5-82", new WellKnownSidInfo(false, "S-1-5-82", "IIS APPPOOL", "", "IIS AppPool Virtual Account"))
185  .put("S-1-5-83", new WellKnownSidInfo(false, "S-1-5-83", "NT VIRTUAL MACHINE", "", "Virtual Machine Virtual Account") )
186  .put("S-1-5-90", new WellKnownSidInfo(false, "S-1-5-90", "Window Manager", "", "Windows Manager Virtual Account"))
187  .put("S-1-5-94", new WellKnownSidInfo(false, "S-1-5-94", "WinRM Virtual Users", "", "Windows Remoting Virtual Account"))
188  .put("S-1-5-96", new WellKnownSidInfo(false, "S-1-5-96", "Font Driver Host", "", "Font Driver Host Virtual Account"))
189  .build();
190 
191 
192  // Looks for security identifier prefixes of the form S-<number>-<number>-<number>
193  // More information on security identifier architecture can be found at:
194  // https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/security-identifiers
195  // A number of accounts in the range S-1-5-80-* to S-1-5-111-* are special.
196  private static final Pattern WINDOWS_SPECIAL_ACCOUNT_PREFIX_REGEX = Pattern.compile("^[sS]\\-1\\-5\\-(\\d+)\\-");
197 
198 
199  // This map reverse maps some of the Well know account names (realm name &login name) to their well known SIDs.
200  private static final Table<String, String, String> SPECIAL_ACCOUNTS_TO_SID_MAP = HashBasedTable.create();
201  static {
202  SPECIAL_ACCOUNTS_TO_SID_MAP.put(NTAUTHORITY_REALM_NAME, "SYSTEM", "S-1-5-18");
203  SPECIAL_ACCOUNTS_TO_SID_MAP.put(NTAUTHORITY_REALM_NAME, "LOCAL SERVICE", "S-1-5-19");
204  SPECIAL_ACCOUNTS_TO_SID_MAP.put(NTAUTHORITY_REALM_NAME, "NETWORK SERVICE", "S-1-5-20");
205  }
206 
207  // A mapping of various well known realm names to their English names.
208  // We store only english names in the database for well known SIDs.
209  // Input names provided by client are first mapped to english before lookup or insert.
210  private static final Map<String, String> REALM_NAME_TO_ENGLISH_MAP = ImmutableMap.<String, String>builder()
211  .put("NT AUTHORITY", NTAUTHORITY_REALM_NAME) // to facilitate a quick hit on the english name
212  .put("NT-AUTORIT�T", NTAUTHORITY_REALM_NAME)
213  .put("AUTORITE NT", NTAUTHORITY_REALM_NAME)
214  .put("NT INSTANS", NTAUTHORITY_REALM_NAME)
215  .build();
216 
217  // A mapping of various well known realm names to their English names.
218  // We store only english names in the database for well known SIDs.
219  // Input names provided by client are first mapped to english before lookup or insert.
220  private static final Map<String, String> LOGINNAME_TO_ENGLISH_MAP = ImmutableMap.<String, String>builder()
221  .put("SYSTEM", "SYSTEM") // to facilitate a quick hit on the english name
222  .put("SYST�ME", "SYSTEM")
223 
224  .put("LOCAL SERVICE", "LOCAL SERVICE")
225  .put("LOKALER DIENST", "LOCAL SERVICE")
226  .put("SERVICE LOCAL", "LOCAL SERVICE")
227  .put("SERVIZIO LOCALE", "LOCAL SERVICE")
228  .put("SERVICIO LOC", "LOCAL SERVICE")
229 
230  .put("NETWORK SERVICE", "NETWORK SERVICE")
231  .put("NETZWERKDIENST", "NETWORK SERVICE")
232  .put("N�TVERKSTJ�NST", "NETWORK SERVICE")
233  .put("SERVICE R�SEAU", "NETWORK SERVICE")
234  .put("SERVIZIO DI RETE", "NETWORK SERVICE")
235  .put("SERVICIO DE RED", "NETWORK SERVICE")
236  .build();
237 
245  static boolean isWindowsWellKnownSid(String sid) {
246 
247  String tempSID = stripWindowsBackupPostfix(sid);
248  if (SPECIAL_SIDS_MAP.containsKey(tempSID)) {
249  return true;
250  }
251  for (String specialPrefix: SPECIAL_SID_PREFIXES_MAP.keySet()) {
252  if (tempSID.startsWith(specialPrefix)) {
253  return true;
254  }
255  }
256 
257  Matcher match = WINDOWS_SPECIAL_ACCOUNT_PREFIX_REGEX.matcher(tempSID);
258  if (match.find()) {
259  Integer domainIdentifier = Integer.valueOf(match.group(1));
260  // All the prefixes in the range S-1-5-80 to S-1-5-111 are special
261  if (domainIdentifier != null && domainIdentifier >= 80 && domainIdentifier <= 111) {
262  return true;
263  }
264  }
265 
266  return false;
267  }
268 
276  static boolean isWindowsWellKnownAccountName(String loginName, String realmName) {
277 
278  String resolvedRealmName = toWellknownEnglishRealmName(realmName);
279  String resolvedLoginName = toWellknownEnglishLoginName(loginName);
280  if (StringUtils.isBlank(resolvedRealmName) || StringUtils.isBlank(resolvedLoginName)) {
281  return false;
282  }
283 
284  return SPECIAL_ACCOUNTS_TO_SID_MAP.contains(resolvedRealmName.toUpperCase(), resolvedLoginName.toUpperCase());
285 
286  }
287 
296  private static String getWindowsWellKnownSidRealmAddr(String sid) throws TskCoreException {
297  String tempSID = stripWindowsBackupPostfix(sid);
298 
299  if (SPECIAL_SIDS_MAP.containsKey(tempSID)) {
300  return SPECIAL_SIDS_MAP.get(tempSID).getRealmAddr();
301  }
302 
303  for (Entry<String, WellKnownSidInfo> specialPrefixEntry : SPECIAL_SID_PREFIXES_MAP.entrySet()) {
304  if (tempSID.startsWith(specialPrefixEntry.getKey())) {
305  return specialPrefixEntry.getValue().getRealmAddr();
306  }
307  }
308 
309  Matcher match = WINDOWS_SPECIAL_ACCOUNT_PREFIX_REGEX.matcher(tempSID);
310  if (match.find()) {
311  Integer domainIdentifier = Integer.valueOf(match.group(1));
312  // All the prefixes in the range S-1-5-80 to S-1-5-111 are special
313  if (domainIdentifier != null && domainIdentifier >= 80 && domainIdentifier <= 111) {
314  String realmAddr = String.format("%s-%d", NTAUTHORITY_SID_PREFIX, domainIdentifier);
315  return realmAddr;
316  }
317  }
318 
319  return "";
320  }
328  private static WellKnownSidInfo getWindowsWellKnownInfo(String sid) {
329  String tempSID = stripWindowsBackupPostfix(sid);
330 
331  if (SPECIAL_SIDS_MAP.containsKey(tempSID)) {
332  return SPECIAL_SIDS_MAP.get(tempSID);
333  }
334  for (Entry<String, WellKnownSidInfo> specialPrefixEntry: SPECIAL_SID_PREFIXES_MAP.entrySet()) {
335  if (tempSID.startsWith(specialPrefixEntry.getKey())) {
336  return specialPrefixEntry.getValue();
337  }
338  }
339  return null;
340  }
341 
349  static String getWindowsWellKnownSidFullName(String sid) {
350  WellKnownSidInfo wellKnownSidInfo = getWindowsWellKnownInfo(sid);
351  return Objects.nonNull(wellKnownSidInfo) ? wellKnownSidInfo.getDescription() : "";
352  }
353 
361  static String getWindowsWellKnownSidRealmName(String sid) {
362 
363  if (StringUtils.isNotBlank(sid) && sid.equals(NTAUTHORITY_SID_PREFIX)) {
364  return NTAUTHORITY_REALM_NAME;
365  }
366 
367  WellKnownSidInfo wellKnownSidInfo = getWindowsWellKnownInfo(sid);
368  return Objects.nonNull(wellKnownSidInfo)
369  ? wellKnownSidInfo.getRealmName()
370  : null;
371  }
372 
380  static String getWindowsWellKnownSidLoginName(String sid) {
381 
382  WellKnownSidInfo wellKnownSidInfo = getWindowsWellKnownInfo(sid);
383  return Objects.nonNull(wellKnownSidInfo)
384  ? wellKnownSidInfo.getLoginName()
385  : null;
386  }
387 
388 
397  static String getWindowsWellKnownAccountSid( String loginName, String realmName) {
398 
399  String resolvedRealmName = toWellknownEnglishRealmName(realmName);
400  String resolvedLoginName = toWellknownEnglishLoginName(loginName);
401  if (StringUtils.isBlank(resolvedRealmName) || StringUtils.isBlank(resolvedLoginName)) {
402  return null;
403  }
404 
405  return SPECIAL_ACCOUNTS_TO_SID_MAP.get(resolvedRealmName.toUpperCase(), resolvedLoginName.toUpperCase());
406 
407  }
408 
417  static String toWellknownEnglishRealmName(String name) {
418  return StringUtils.isNotBlank(name)
419  ? REALM_NAME_TO_ENGLISH_MAP.getOrDefault(name.toUpperCase(), name)
420  : null;
421  }
422 
431  static String toWellknownEnglishLoginName(String name) {
432  return StringUtils.isNotBlank(name)
433  ? LOGINNAME_TO_ENGLISH_MAP.getOrDefault(name.toUpperCase(), name)
434  : null;
435  }
436 
446  static boolean isWindowsUserSid(String sid) {
447 
448  String tempSID = stripWindowsBackupPostfix(sid);
449 
450  if (GROUP_SIDS.contains(tempSID)) {
451  return false;
452  }
453 
454  for (String prefix: GROUP_SID_PREFIX) {
455  if (tempSID.startsWith(prefix)) {
456  return false;
457  }
458  }
459 
460  // check for domain groups - they have a domains specific identifier but have a fixed prefix and suffix
461  if (tempSID.startsWith(NTAUTHORITY_SID_PREFIX)) {
462  for (String suffix : DOMAIN_GROUP_SID_SUFFIX) {
463  if (tempSID.endsWith(suffix)) {
464  return false;
465  }
466  }
467  }
468 
469  return true;
470 
471  }
472 
486  public static String getWindowsRealmAddress(String sid) throws TskCoreException {
487 
488  String realmAddr;
489  String tempSID = stripWindowsBackupPostfix(sid);
490 
491  if ( isWindowsWellKnownSid(tempSID)) {
492  realmAddr = getWindowsWellKnownSidRealmAddr(sid);
493  } else {
494  // SIDs should have at least 4 components: S-1-A-S
495  // A: authority identifier
496  // S: one or more sub-authority identifiers (RIDs)
497  if (org.apache.commons.lang3.StringUtils.countMatches(tempSID, "-") < 3) {
498  throw new TskCoreException(String.format("Invalid SID %s for a host/domain", tempSID));
499  }
500  // get the sub authority SID
501  realmAddr = sid.substring(0, tempSID.lastIndexOf('-'));
502  }
503 
504  return realmAddr;
505  }
506 
515  private static String stripWindowsBackupPostfix(String sid) {
516  String tempSID = sid;
517 
518  if(tempSID.endsWith(SPECIAL_WINDOWS_BACK_UP_POSTFIX)) {
519  tempSID = tempSID.replace(SPECIAL_WINDOWS_BACK_UP_POSTFIX, "");
520  }
521 
522  return tempSID;
523  }
524 
525 }

Copyright © 2011-2024 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.