Sleuth Kit Java Bindings (JNI) 4.14.0
Java bindings for using The Sleuth Kit
Loading...
Searching...
No Matches
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 */
19package org.sleuthkit.datamodel;
20
21import com.google.common.collect.ImmutableMap;
22import com.google.common.collect.ImmutableSet;
23import java.util.Map;
24import java.util.Map.Entry;
25import java.util.Objects;
26import java.util.Optional;
27import java.util.Set;
28import java.util.regex.Matcher;
29import java.util.regex.Pattern;
30import org.apache.commons.lang3.StringUtils;
31import com.google.common.collect.HashBasedTable;
32import com.google.common.collect.Table;
33import java.util.Locale;
34
43final 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.