diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java index f9499bb41f313e473483f3706ae6bce97af22d03..b74c490ecd301366fd34022928d843ff89295a45 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java @@ -19,11 +19,13 @@ package org.sleuthkit.datamodel; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Optional; +import java.util.Set; import java.util.logging.Logger; import org.sleuthkit.datamodel.OsAccountRealm.ScopeConfidence; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; @@ -34,7 +36,25 @@ * host with local accounts or a domain. */ public final class OsAccountRealmManager { - + + // Some windows SID indicate special account. + // These should be handled differently from regular user accounts. + private static final Set<String> SPECIAL_SIDS = ImmutableSet.of( + "S-1-5-18", // LOCAL_SYSTEM_ACCOUNT + "S-1-5-19", // LOCAL_SERVICE_ACCOUNT + "S-1-5-20" // NETWORK_SERVICE_ACCOUNT + ); + private static final Set<String> SPECIAL_SID_PREFIXES = ImmutableSet.of( + "S-1-5-80", // Virtual Service accounts + "S-1-5-82", // AppPoolIdentity Virtual accounts. + "S-1-5-83", // Virtual Machine Virtual Accounts. + "S-1-5-90", // Windows Manager Virtual Accounts. + "S-1-5-96" // Font Drive Host Virtual Accounts. + ); + + // Special Windows Accounts with short SIDS are given a special realm "address". + private final static String SPECIAL_WINDOWS_REALM_ADDR = "SPECIAL_WINDOWS_ACCOUNTS"; + private static final Logger LOGGER = Logger.getLogger(OsAccountRealmManager.class.getName()); private final SleuthkitCase db; @@ -91,7 +111,7 @@ public OsAccountRealm createWindowsRealm(String accountSid, String realmName, Ho case UNKNOWN: default: // check if the referring host already has a realm - boolean isHostRealmKnown = this.isHostRealmKnown(referringHost); + boolean isHostRealmKnown = isHostRealmKnown(referringHost); if (isHostRealmKnown) { scopeHost = null; // the realm does not scope to the referring host since it already has one. scopeConfidence = OsAccountRealm.ScopeConfidence.KNOWN; @@ -103,12 +123,16 @@ public OsAccountRealm createWindowsRealm(String accountSid, String realmName, Ho } - // RAMAN TBD: can the SID be parsed in some way to determine local vs domain ?? - - // get subAuthority sid + // get windows realm address from sid String realmAddr = null; if (!Strings.isNullOrEmpty(accountSid)) { - realmAddr = getWindowsSubAuthorityId(accountSid); + realmAddr = getWindowsRealmAddress(accountSid); + + // if the account is special windows account, create a local realm for it. + if (realmAddr.equals(SPECIAL_WINDOWS_REALM_ADDR)) { + scopeHost = referringHost; + scopeConfidence = OsAccountRealm.ScopeConfidence.KNOWN; + } } String signature = makeRealmSignature(realmAddr, realmName, scopeHost); @@ -174,8 +198,8 @@ Optional<OsAccountRealm> getWindowsRealm(String accountSid, String realmName, Ho // If a accountSID is provided , search for realm by addr. if (!Strings.isNullOrEmpty(accountSid)) { // get realm addr from the account SID. - String subAuthorityId = getWindowsSubAuthorityId(accountSid); - return this.getRealmByAddr(subAuthorityId, referringHost, connection); + String realmAddr = getWindowsRealmAddress(accountSid); + return this.getRealmByAddr(realmAddr, referringHost, connection); } // No realm addr, Search by name @@ -367,9 +391,11 @@ Optional<OsAccountRealm> getRealmByName(String realmName, Host host, CaseDbConne */ private boolean isHostRealmKnown(Host host) throws TskCoreException { + // check if this host has a local known realm aleady, other than the special windows realm. String queryString = REALM_QUERY_STRING + " WHERE realms.scope_host_id = " + host.getId() - + " AND realms.scope_confidence = " + OsAccountRealm.ScopeConfidence.KNOWN.getId(); + + " AND realms.scope_confidence = " + OsAccountRealm.ScopeConfidence.KNOWN.getId() + + " AND LOWER(realms.realm_addr) <> LOWER('"+ SPECIAL_WINDOWS_REALM_ADDR + "') "; try (CaseDbConnection connection = this.db.getConnection(); Statement s = connection.createStatement(); @@ -509,23 +535,52 @@ private OsAccountRealm createRealm(String realmName, String realmAddr, String si } /** - * Gets the sub authority id from the given SID. + * Get the windows realm address from the given SID. + * + * For all regular account SIDs, the realm address is the sub-authority SID. + * For special Windows account the realm address is a special address. * * @param sid SID * - * @return Sub-authority id string. + * @return Realm address for the SID. */ - private String getWindowsSubAuthorityId(String sid) { + private String getWindowsRealmAddress(String sid) { - // RAMAN TBD: this fails for short WellKnown SIDs - if (org.apache.commons.lang3.StringUtils.countMatches(sid, "-") < 5 ) { - throw new IllegalArgumentException(String.format("Invalid SID %s for a host/domain", sid)); - } - String subAuthorityId = sid.substring(0, sid.lastIndexOf('-')); + String realmAddr; - return subAuthorityId; + if (isWindowsSpecialSid(sid)) { + realmAddr = SPECIAL_WINDOWS_REALM_ADDR; + } else { + // regular SIDs should have at least 5 components: S-1-x-y-z + if (org.apache.commons.lang3.StringUtils.countMatches(sid, "-") < 4) { + throw new IllegalArgumentException(String.format("Invalid SID %s for a host/domain", sid)); + } + // get the sub authority SID + realmAddr = sid.substring(0, sid.lastIndexOf('-')); + } + + return realmAddr; } + /** + * Checks if the given SID is a special Windows SID. + * + * @param sid SID to check. + * + * @return True if the SID is a Windows special SID, false otherwise + */ + private boolean isWindowsSpecialSid(String sid) { + if (SPECIAL_SIDS.contains(sid)) { + return true; + } + for (String specialPrefix: SPECIAL_SID_PREFIXES) { + if (sid.startsWith(specialPrefix)) { + return true; + } + } + return false; + } + /** * Makes a realm signature based on given realm address, name scope host. * diff --git a/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java b/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java index 77ee9242c492f3d42dc6f43b15a3ce6b313ff1a9..4f8a90cd9a3c453a9f0d8a9f1d7431b28567113e 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TskCaseDbBridge.java @@ -369,8 +369,7 @@ private long addBatchedFilesToDb() { while (it.hasNext()) { FileInfo fileInfo = it.next(); String ownerUid = fileInfo.ownerUid; - if ((Strings.isNullOrEmpty(fileInfo.ownerUid) == false) - && (org.apache.commons.lang3.StringUtils.countMatches(ownerUid, "-") >= 5 )) { // RAMAN TBD: we dont know yet how to handle short Well known SIDs. + if (Strings.isNullOrEmpty(fileInfo.ownerUid) == false) { // first check the owner id is in the map, if found, then continue if (this.ownerIdToAccountMap.containsKey(ownerUid)) { continue; @@ -403,8 +402,7 @@ private long addBatchedFilesToDb() { } Long ownerAccountObjId = OsAccount.NO_ACCOUNT; - if ((Strings.isNullOrEmpty(fileInfo.ownerUid) == false) - && (org.apache.commons.lang3.StringUtils.countMatches(fileInfo.ownerUid, "-") >= 5 ) ){ // RAMAN TBD: we dont know yet how to handle short Well known SIDs. + if (Strings.isNullOrEmpty(fileInfo.ownerUid) == false) { if (ownerIdToAccountMap.containsKey(fileInfo.ownerUid)) { ownerAccountObjId = ownerIdToAccountMap.get(fileInfo.ownerUid).getId(); } else { diff --git a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java index a27f34feef6925ed3a51bc9e2164690a6da1d0f4..8944a191339498d54d20b622ef961a0929fceffe 100644 --- a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java +++ b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java @@ -148,8 +148,8 @@ public void osAccountRealmTests() throws TskCoreException { Host host1 = caseDB.getHostManager().createHost(HOSTNAME1); String realmName1 = "basis"; - String realmSID1 = "S-1-5-18-1111111111-2222222222-3333333333"; - String realmAddr1 = "S-1-5-18-1111111111-2222222222"; + String realmSID1 = "S-1-5-21-1111111111-2222222222-3333333333"; + String realmAddr1 = "S-1-5-21-1111111111-2222222222"; OsAccountRealm domainRealm1 = caseDB.getOsAccountRealmManager().createWindowsRealm(realmSID1, realmName1, host1, OsAccountRealm.RealmScope.DOMAIN); @@ -290,4 +290,125 @@ public void basicOsAccountTests() throws TskCoreException { } } + + + @Test + public void windowsSpecialAccountTests() throws TskCoreException { + + try { + + String SPECIAL_WINDOWS_REALM_ADDR = "SPECIAL_WINDOWS_ACCOUNTS"; + + + + // TEST: create accounts with a "short" sid + { + String hostname1 = "host111"; + Host host1 = caseDB.getHostManager().createHost(hostname1); + + String realmName1 = "realmName111"; + String sid1 = "S-1-5-32-544"; // builtin Administrators + String sid2 = "S-1-5-32-545"; // builtin Users + String sid3 = "S-1-5-32-546"; // builtin Guests + String realmAddr1 = "S-1-5-32"; + + OsAccount osAccount1 = caseDB.getOsAccountManager().createWindowsAccount(sid1, null, realmName1, host1, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount osAccount2 = caseDB.getOsAccountManager().createWindowsAccount(sid2, null, realmName1, host1, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount osAccount3 = caseDB.getOsAccountManager().createWindowsAccount(sid3, null, realmName1, host1, OsAccountRealm.RealmScope.UNKNOWN); + + assertEquals(osAccount1.getRealm().getRealmName().orElse("").equalsIgnoreCase(realmName1), true); + assertEquals(osAccount1.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(realmAddr1), true); + assertEquals(osAccount1.isAdmin(), false); + assertEquals(osAccount1.getUniqueIdWithinRealm().orElse("").equalsIgnoreCase(sid1), true); + + assertEquals(osAccount2.getRealm().getRealmName().orElse("").equalsIgnoreCase(realmName1), true); + assertEquals(osAccount2.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(realmAddr1), true); + assertEquals(osAccount2.isAdmin(), false); + assertEquals(osAccount2.getUniqueIdWithinRealm().orElse("").equalsIgnoreCase(sid2), true); + + assertEquals(osAccount3.getRealm().getRealmName().orElse("").equalsIgnoreCase(realmName1), true); + assertEquals(osAccount3.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(realmAddr1), true); + assertEquals(osAccount3.isAdmin(), false); + assertEquals(osAccount3.getUniqueIdWithinRealm().orElse("").equalsIgnoreCase(sid3), true); + } + + + + + // TEST create accounts with special SIDs on host2 + { + String hostname2 = "host222"; + Host host2 = caseDB.getHostManager().createHost(hostname2); + + String specialSid1 = "S-1-5-18"; + String specialSid2 = "S-1-5-19"; + String specialSid3 = "S-1-5-20"; + + OsAccount specialAccount1 = caseDB.getOsAccountManager().createWindowsAccount(specialSid1, null, null, host2, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount2 = caseDB.getOsAccountManager().createWindowsAccount(specialSid2, null, null, host2, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount3 = caseDB.getOsAccountManager().createWindowsAccount(specialSid3, null, null, host2, OsAccountRealm.RealmScope.UNKNOWN); + + assertEquals(specialAccount1.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(specialAccount2.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(specialAccount3.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + } + + + // TEST create accounts with special SIDs on host3 - should create their own realm + { + String hostname3 = "host333"; + Host host3 = caseDB.getHostManager().createHost(hostname3); + + String specialSid1 = "S-1-5-18"; + String specialSid2 = "S-1-5-19"; + String specialSid3 = "S-1-5-20"; + + OsAccount specialAccount1 = caseDB.getOsAccountManager().createWindowsAccount(specialSid1, null, null, host3, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount2 = caseDB.getOsAccountManager().createWindowsAccount(specialSid2, null, null, host3, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount3 = caseDB.getOsAccountManager().createWindowsAccount(specialSid3, null, null, host3, OsAccountRealm.RealmScope.UNKNOWN); + + assertEquals(specialAccount1.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(specialAccount2.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(specialAccount3.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + + // verify a new local realm with host3 was created for these account even they've been seen previously on another host + assertEquals(specialAccount1.getRealm().getScopeHost().orElse(null).getName().equalsIgnoreCase(hostname3), true); + assertEquals(specialAccount1.getRealm().getScopeHost().orElse(null).getName().equalsIgnoreCase(hostname3), true); + assertEquals(specialAccount1.getRealm().getScopeHost().orElse(null).getName().equalsIgnoreCase(hostname3), true); + } + + + // Test some other special account. + { + String hostname4 = "host444"; + Host host4 = caseDB.getHostManager().createHost(hostname4); + + String specialSid1 = "S-1-5-80-3696737894-3623014651-202832235-645492566-13622391"; + String specialSid2 = "S-1-5-82-4003674586-223046494-4022293810-2417516693-151509167"; + String specialSid3 = "S-1-5-90-0-2"; + String specialSid4 = "S-1-5-96-0-3"; + + + OsAccount specialAccount1 = caseDB.getOsAccountManager().createWindowsAccount(specialSid1, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount2 = caseDB.getOsAccountManager().createWindowsAccount(specialSid2, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount3 = caseDB.getOsAccountManager().createWindowsAccount(specialSid3, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount4 = caseDB.getOsAccountManager().createWindowsAccount(specialSid4, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); + + + assertEquals(specialAccount1.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(specialAccount2.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(specialAccount3.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(specialAccount4.getRealm().getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + + + } + + } + + finally { + + } + + } + }