Sleuth Kit Java Bindings (JNI)  4.11.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-18", new WellKnownSidInfo(true, "S-1-5", NTAUTHORITY_REALM_NAME, "SYSTEM", "Local System Account"))
174  .put("S-1-5-19", new WellKnownSidInfo(true, "S-1-5", NTAUTHORITY_REALM_NAME, "LOCAL SERVICE", "Local Service Account"))
175  .put("S-1-5-20", new WellKnownSidInfo(true, "S-1-5", NTAUTHORITY_REALM_NAME, "NETWORK SERVICE", "Network Service Account"))
176  .build();
177 
178 
179  // These SID prefixes indicate well known windows accounts.
180  // - We can fill in the login names for these SID, as well as account user description.
181  private static final Map<String, WellKnownSidInfo> SPECIAL_SID_PREFIXES_MAP = ImmutableMap.<String, WellKnownSidInfo>builder()
182  .put("S-1-5-80", new WellKnownSidInfo(false, "S-1-5-80", "NT SERVICE", "", "NT Service Virtual Account"))
183  .put("S-1-5-82", new WellKnownSidInfo(false, "S-1-5-82", "IIS APPPOOL", "", "IIS AppPool Virtual Account"))
184  .put("S-1-5-83", new WellKnownSidInfo(false, "S-1-5-83", "NT VIRTUAL MACHINE", "", "Virtual Machine Virtual Account") )
185  .put("S-1-5-90", new WellKnownSidInfo(false, "S-1-5-90", "Window Manager", "", "Windows Manager Virtual Account"))
186  .put("S-1-5-94", new WellKnownSidInfo(false, "S-1-5-94", "WinRM Virtual Users", "", "Windows Remoting Virtual Account"))
187  .put("S-1-5-96", new WellKnownSidInfo(false, "S-1-5-96", "Font Driver Host", "", "Font Driver Host Virtual Account"))
188  .build();
189 
190 
191  // Looks for security identifier prefixes of the form S-<number>-<number>-<number>
192  // More information on security identifier architecture can be found at:
193  // https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/security-identifiers
194  // A number of accounts in the range S-1-5-80-* to S-1-5-111-* are special.
195  private static final Pattern WINDOWS_SPECIAL_ACCOUNT_PREFIX_REGEX = Pattern.compile("^[sS]\\-1\\-5\\-(\\d+)\\-");
196 
197 
198  // This map reverse maps some of the Well know account names (realm name &login name) to their well known SIDs.
199  private static final Table<String, String, String> SPECIAL_ACCOUNTS_TO_SID_MAP = HashBasedTable.create();
200  static {
201  SPECIAL_ACCOUNTS_TO_SID_MAP.put(NTAUTHORITY_REALM_NAME, "SYSTEM", "S-1-5-18");
202  SPECIAL_ACCOUNTS_TO_SID_MAP.put(NTAUTHORITY_REALM_NAME, "LOCAL SERVICE", "S-1-5-19");
203  SPECIAL_ACCOUNTS_TO_SID_MAP.put(NTAUTHORITY_REALM_NAME, "NETWORK SERVICE", "S-1-5-20");
204  }
205 
206  // A mapping of various well known realm names to their English names.
207  // We store only english names in the database for well known SIDs.
208  // Input names provided by client are first mapped to english before lookup or insert.
209  private static final Map<String, String> REALM_NAME_TO_ENGLISH_MAP = ImmutableMap.<String, String>builder()
210  .put("NT AUTHORITY", NTAUTHORITY_REALM_NAME) // to facilitate a quick hit on the english name
211  .put("NT-AUTORIT�T", NTAUTHORITY_REALM_NAME)
212  .put("AUTORITE NT", NTAUTHORITY_REALM_NAME)
213  .put("NT INSTANS", NTAUTHORITY_REALM_NAME)
214  .build();
215 
216  // A mapping of various well known realm names to their English names.
217  // We store only english names in the database for well known SIDs.
218  // Input names provided by client are first mapped to english before lookup or insert.
219  private static final Map<String, String> LOGINNAME_TO_ENGLISH_MAP = ImmutableMap.<String, String>builder()
220  .put("SYSTEM", "SYSTEM") // to facilitate a quick hit on the english name
221  .put("SYST�ME", "SYSTEM")
222 
223  .put("LOCAL SERVICE", "LOCAL SERVICE")
224  .put("LOKALER DIENST", "LOCAL SERVICE")
225  .put("SERVICE LOCAL", "LOCAL SERVICE")
226  .put("SERVIZIO LOCALE", "LOCAL SERVICE")
227  .put("SERVICIO LOC", "LOCAL SERVICE")
228 
229  .put("NETWORK SERVICE", "NETWORK SERVICE")
230  .put("NETZWERKDIENST", "NETWORK SERVICE")
231  .put("N�TVERKSTJ�NST", "NETWORK SERVICE")
232  .put("SERVICE R�SEAU", "NETWORK SERVICE")
233  .put("SERVIZIO DI RETE", "NETWORK SERVICE")
234  .put("SERVICIO DE RED", "NETWORK SERVICE")
235  .build();
236 
244  static boolean isWindowsWellKnownSid(String sid) {
245 
246  String tempSID = stripWindowsBackupPostfix(sid);
247  if (SPECIAL_SIDS_MAP.containsKey(tempSID)) {
248  return true;
249  }
250  for (String specialPrefix: SPECIAL_SID_PREFIXES_MAP.keySet()) {
251  if (tempSID.startsWith(specialPrefix)) {
252  return true;
253  }
254  }
255 
256  Matcher match = WINDOWS_SPECIAL_ACCOUNT_PREFIX_REGEX.matcher(tempSID);
257  if (match.find()) {
258  Integer domainIdentifier = Integer.valueOf(match.group(1));
259  // All the prefixes in the range S-1-5-80 to S-1-5-111 are special
260  if (domainIdentifier != null && domainIdentifier >= 80 && domainIdentifier <= 111) {
261  return true;
262  }
263  }
264 
265  return false;
266  }
267 
275  static boolean isWindowsWellKnownAccountName(String loginName, String realmName) {
276 
277  String resolvedRealmName = toWellknownEnglishRealmName(realmName);
278  String resolvedLoginName = toWellknownEnglishLoginName(loginName);
279  if (StringUtils.isBlank(resolvedRealmName) || StringUtils.isBlank(resolvedLoginName)) {
280  return false;
281  }
282 
283  return SPECIAL_ACCOUNTS_TO_SID_MAP.contains(resolvedRealmName.toUpperCase(), resolvedLoginName.toUpperCase());
284 
285  }
286 
295  private static String getWindowsWellKnownSidRealmAddr(String sid) throws TskCoreException {
296  String tempSID = stripWindowsBackupPostfix(sid);
297 
298  if (SPECIAL_SIDS_MAP.containsKey(tempSID)) {
299  return SPECIAL_SIDS_MAP.get(tempSID).getRealmAddr();
300  }
301 
302  for (Entry<String, WellKnownSidInfo> specialPrefixEntry : SPECIAL_SID_PREFIXES_MAP.entrySet()) {
303  if (tempSID.startsWith(specialPrefixEntry.getKey())) {
304  return specialPrefixEntry.getValue().getRealmAddr();
305  }
306  }
307 
308  Matcher match = WINDOWS_SPECIAL_ACCOUNT_PREFIX_REGEX.matcher(tempSID);
309  if (match.find()) {
310  Integer domainIdentifier = Integer.valueOf(match.group(1));
311  // All the prefixes in the range S-1-5-80 to S-1-5-111 are special
312  if (domainIdentifier != null && domainIdentifier >= 80 && domainIdentifier <= 111) {
313  String realmAddr = String.format("%s-%d", NTAUTHORITY_SID_PREFIX, domainIdentifier);
314  return realmAddr;
315  }
316  }
317 
318  return "";
319  }
327  private static WellKnownSidInfo getWindowsWellKnownInfo(String sid) {
328  String tempSID = stripWindowsBackupPostfix(sid);
329 
330  if (SPECIAL_SIDS_MAP.containsKey(tempSID)) {
331  return SPECIAL_SIDS_MAP.get(tempSID);
332  }
333  for (Entry<String, WellKnownSidInfo> specialPrefixEntry: SPECIAL_SID_PREFIXES_MAP.entrySet()) {
334  if (tempSID.startsWith(specialPrefixEntry.getKey())) {
335  return specialPrefixEntry.getValue();
336  }
337  }
338  return null;
339  }
340 
348  static String getWindowsWellKnownSidFullName(String sid) {
349  WellKnownSidInfo wellKnownSidInfo = getWindowsWellKnownInfo(sid);
350  return Objects.nonNull(wellKnownSidInfo) ? wellKnownSidInfo.getDescription() : "";
351  }
352 
360  static String getWindowsWellKnownSidRealmName(String sid) {
361 
362  if (StringUtils.isNotBlank(sid) && sid.equals(NTAUTHORITY_SID_PREFIX)) {
363  return NTAUTHORITY_REALM_NAME;
364  }
365 
366  WellKnownSidInfo wellKnownSidInfo = getWindowsWellKnownInfo(sid);
367  return Objects.nonNull(wellKnownSidInfo)
368  ? wellKnownSidInfo.getRealmName()
369  : null;
370  }
371 
379  static String getWindowsWellKnownSidLoginName(String sid) {
380 
381  WellKnownSidInfo wellKnownSidInfo = getWindowsWellKnownInfo(sid);
382  return Objects.nonNull(wellKnownSidInfo)
383  ? wellKnownSidInfo.getLoginName()
384  : null;
385  }
386 
387 
396  static String getWindowsWellKnownAccountSid( String loginName, String realmName) {
397 
398  String resolvedRealmName = toWellknownEnglishRealmName(realmName);
399  String resolvedLoginName = toWellknownEnglishLoginName(loginName);
400  if (StringUtils.isBlank(resolvedRealmName) || StringUtils.isBlank(resolvedLoginName)) {
401  return null;
402  }
403 
404  return SPECIAL_ACCOUNTS_TO_SID_MAP.get(resolvedRealmName.toUpperCase(), resolvedLoginName.toUpperCase());
405 
406  }
407 
416  static String toWellknownEnglishRealmName(String name) {
417  return StringUtils.isNotBlank(name)
418  ? REALM_NAME_TO_ENGLISH_MAP.getOrDefault(name.toUpperCase(), name)
419  : null;
420  }
421 
430  static String toWellknownEnglishLoginName(String name) {
431  return StringUtils.isNotBlank(name)
432  ? LOGINNAME_TO_ENGLISH_MAP.getOrDefault(name.toUpperCase(), name)
433  : null;
434  }
435 
445  static boolean isWindowsUserSid(String sid) {
446 
447  String tempSID = stripWindowsBackupPostfix(sid);
448 
449  if (GROUP_SIDS.contains(tempSID)) {
450  return false;
451  }
452 
453  for (String prefix: GROUP_SID_PREFIX) {
454  if (tempSID.startsWith(prefix)) {
455  return false;
456  }
457  }
458 
459  // check for domain groups - they have a domains specific identifier but have a fixed prefix and suffix
460  if (tempSID.startsWith(NTAUTHORITY_SID_PREFIX)) {
461  for (String suffix : DOMAIN_GROUP_SID_SUFFIX) {
462  if (tempSID.endsWith(suffix)) {
463  return false;
464  }
465  }
466  }
467 
468  return true;
469 
470  }
471 
485  public static String getWindowsRealmAddress(String sid) throws TskCoreException {
486 
487  String realmAddr;
488  String tempSID = stripWindowsBackupPostfix(sid);
489 
490  if ( isWindowsWellKnownSid(tempSID)) {
491  realmAddr = getWindowsWellKnownSidRealmAddr(sid);
492  } else {
493  // SIDs should have at least 4 components: S-1-A-S
494  // A: authority identifier
495  // S: one or more sub-authority identifiers (RIDs)
496  if (org.apache.commons.lang3.StringUtils.countMatches(tempSID, "-") < 3) {
497  throw new TskCoreException(String.format("Invalid SID %s for a host/domain", tempSID));
498  }
499  // get the sub authority SID
500  realmAddr = sid.substring(0, tempSID.lastIndexOf('-'));
501  }
502 
503  return realmAddr;
504  }
505 
514  private static String stripWindowsBackupPostfix(String sid) {
515  String tempSID = sid;
516 
517  if(tempSID.endsWith(SPECIAL_WINDOWS_BACK_UP_POSTFIX)) {
518  tempSID = tempSID.replace(SPECIAL_WINDOWS_BACK_UP_POSTFIX, "");
519  }
520 
521  return tempSID;
522  }
523 
524 }

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