Autopsy  4.21.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
PathNormalizer.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2023 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 com.basistech.df.cybertriage.autopsy.malwarescan;
20 
21 import com.google.common.net.InetAddresses;
22 import java.net.InetAddress;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.Set;
28 import java.util.logging.Level;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import java.util.stream.Collectors;
32 import org.apache.commons.lang3.StringUtils;
34 import org.sleuthkit.datamodel.SleuthkitCase;
35 import org.sleuthkit.datamodel.TskCoreException;
36 
40 class PathNormalizer {
41 
42  private static final Logger LOGGER = Logger.getLogger(PathNormalizer.class.getName());
43 
44  private static final String ANONYMIZED_USERNAME = "<user>";
45  private static final String ANONYMIZED_IP = "<private_ip>";
46  private static final String ANONYMIZED_HOSTNAME = "<hostname>";
47  private static final String FORWARD_SLASH = "/";
48  private static final String BACK_SLASH = "\\";
49 
50  private static final Pattern USER_PATH_FORWARD_SLASH_REGEX = Pattern.compile("(?<!all )([/]{0,1}\\Qusers\\E/)(?!(public|Default|defaultAccount|All Users))([^/]+)(/){0,1}", Pattern.CASE_INSENSITIVE);
51  private static final Pattern USER_PATH_BACK_SLASH_REGEX = Pattern.compile("(?<!all )([\\\\]{0,1}\\Qusers\\E\\\\)(?!(public|Default|defaultAccount|All Users))([^\\\\]+)([\\\\]){0,1}", Pattern.CASE_INSENSITIVE);
52 
53  private static final Pattern USER_PATH_FORWARD_SLASH_REGEX_XP = Pattern.compile("([/]{0,1}\\Qdocuments and settings\\E/)(?!(Default User|All Users))([^/]+)(/){0,1}", Pattern.CASE_INSENSITIVE);
54  private static final Pattern USER_PATH_BACK_SLASH_REGEX_XP = Pattern.compile("([\\\\]{0,1}\\Qdocuments and settings\\E\\\\)(?!(Default User|All Users))([^\\\\]+)(\\\\){0,1}", Pattern.CASE_INSENSITIVE);
55 
56  private static final Pattern UNC_PATH_FORWARD_SLASH_PATTERN = Pattern.compile("(//)([^/]+)(/){0,1}");
57  private static final Pattern UNC_PATH_BACK_SLASH_PATTERN = Pattern.compile("(\\\\\\\\)([^\\\\]+)(\\\\){0,1}");
58 
59  private static final String USERNAME_REGEX_REPLACEMENT = "$1" + ANONYMIZED_USERNAME + "$4";
60 
61  private final SleuthkitCase skCase;
62 
63  PathNormalizer(SleuthkitCase skCase) {
64  this.skCase = skCase;
65  }
66 
67  protected List<String> getUsernames() {
68  try {
69  return this.skCase.getOsAccountManager().getOsAccounts().stream()
70  .filter(acct -> acct != null)
71  .map(acct -> acct.getLoginName().orElse(null))
72  .filter(StringUtils::isNotBlank)
73  .collect(Collectors.toList());
74  } catch (TskCoreException ex) {
75  LOGGER.log(Level.WARNING, "There was an error getting current os accounts", ex);
76  return Collections.emptyList();
77  }
78  }
79 
80  public String normalizePath(String inputString) {
81  if (StringUtils.isBlank(inputString)) {
82  return "";
83  }
84 
85  String anonymousString = anonymizeUserFromPathsWithForwardSlashes(inputString);
86  anonymousString = anonymizeUserFromPathsWithBackSlashes(anonymousString);
87  anonymousString = anonymizeServerFromUNCPath(anonymousString);
88 
89  return anonymousString;
90  }
91 
92  private String anonymizeUserFromPathsWithForwardSlashes(String stringWithUsername) {
93  String anonymousString = stringWithUsername;
94  anonymousString = regexReplace(anonymousString, USER_PATH_FORWARD_SLASH_REGEX_XP, USERNAME_REGEX_REPLACEMENT);
95  anonymousString = regexReplace(anonymousString, USER_PATH_FORWARD_SLASH_REGEX, USERNAME_REGEX_REPLACEMENT);
96  anonymousString = replaceFolder(anonymousString, getUsernames(), ANONYMIZED_USERNAME, FORWARD_SLASH);
97  return anonymousString;
98  }
99 
100  // Most paths in CyberTriage are normalized with forward slashes
101  // but there can still be strings containing paths that are not normalized such paths contained in arguments or event log payloads
102  private String anonymizeUserFromPathsWithBackSlashes(String stringWithUsername) {
103  String anonymousString = stringWithUsername;
104  anonymousString = regexReplace(anonymousString, USER_PATH_BACK_SLASH_REGEX_XP, USERNAME_REGEX_REPLACEMENT);
105  anonymousString = regexReplace(anonymousString, USER_PATH_BACK_SLASH_REGEX, USERNAME_REGEX_REPLACEMENT);
106  anonymousString = replaceFolder(anonymousString, getUsernames(), ANONYMIZED_USERNAME, BACK_SLASH);
107 
108  return anonymousString;
109  }
110 
111  private String anonymizeServerFromUNCPath(String inputString) {
112 
113  Set<String> serverNames = new HashSet<>();
114  String anonymousString = inputString.toLowerCase(Locale.ENGLISH);
115 
116  Matcher forwardSlashMatcher = UNC_PATH_FORWARD_SLASH_PATTERN.matcher(anonymousString);
117  while (forwardSlashMatcher.find()) {
118  String serverName = forwardSlashMatcher.group(2);
119  serverNames.add(serverName);
120  }
121 
122  Matcher backSlashMatcher = UNC_PATH_BACK_SLASH_PATTERN.matcher(anonymousString);
123  while (backSlashMatcher.find()) {
124  String serverName = backSlashMatcher.group(2);
125  serverNames.add(serverName);
126  }
127 
128  for (String serverName : serverNames) {
129 
130  if (StringUtils.isBlank(serverName)) {
131  continue;
132  }
133 
134  if (InetAddresses.isInetAddress(serverName) && isLocalIP(serverName)) {
135  anonymousString = replaceFolder(anonymousString, Collections.singletonList(serverName), ANONYMIZED_IP);
136  } else {
137  anonymousString = replaceFolder(anonymousString, Collections.singletonList(serverName), ANONYMIZED_HOSTNAME);
138  }
139 
140  }
141 
142  return anonymousString;
143  }
144 
145  private static String regexReplace(String orig, Pattern pattern, String regexReplacement) {
146  Matcher matcher = pattern.matcher(orig);
147  return matcher.replaceAll(regexReplacement);
148  }
149 
150  private static String replaceFolder(String orig, List<String> valuesToReplace, String replacementValue) {
151  String anonymized = orig;
152  anonymized = replaceFolder(anonymized, valuesToReplace, replacementValue, FORWARD_SLASH);
153  anonymized = replaceFolder(anonymized, valuesToReplace, replacementValue, BACK_SLASH);
154  return anonymized;
155  }
156 
157  private static String replaceFolder(String orig, List<String> valuesToReplace, String replacementValue, String folderDelimiter) {
158  if (orig == null || valuesToReplace == null) {
159  return orig;
160  }
161 
162  String anonymousString = orig;
163 
164  // ensure non-null
165  folderDelimiter = StringUtils.defaultString(folderDelimiter);
166  replacementValue = StringUtils.defaultString(replacementValue);
167 
168  // replace
169  for (String valueToReplace : valuesToReplace) {
170  if (StringUtils.isNotEmpty(valueToReplace)) {
171  anonymousString = StringUtils.replace(anonymousString,
172  folderDelimiter + valueToReplace + folderDelimiter,
173  folderDelimiter + replacementValue + folderDelimiter);
174  }
175  }
176 
177  return anonymousString;
178  }
179 
190  public static boolean isLocalIP(String ipAddress) {
191  try {
192  InetAddress a = InetAddresses.forString(ipAddress);
193  return a.isAnyLocalAddress() || a.isSiteLocalAddress()
194  || a.isLoopbackAddress() || a.isLinkLocalAddress();
195  } catch (IllegalArgumentException ex) {
196  LOGGER.log(Level.WARNING, "Invalid IP string", ex);
197  return false;
198  }
199  }
200 
201 }

Copyright © 2012-2022 Basis Technology. Generated on: Tue Feb 6 2024
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.