def testSystemAccountAnomaly(self): passwd = [ "root:x:0:0::/root:/bin/sash", "miss:x:1000:100:Missing:/home/miss:/bin/bash", "bad1:x:0:1001:Bad 1:/home/bad1:/bin/bash", "bad2:x:1002:0:Bad 2:/home/bad2:/bin/bash" ] shadow = [ "root:{UNSET}:16000:0:99999:7:::", "ok:{SHA512}:16000:0:99999:7:::", "bad1::16333:0:99999:7:::", "bad2:{DES}:16333:0:99999:7:::" ] group = [ "root:x:0:root", "miss:x:1000:miss", "bad1:x:1001:bad1", "bad2:x:1002:bad2" ] gshadow = ["root:::root", "miss:::miss", "bad1:::bad1", "bad2:::bad2"] stats, files = self._GenFiles(passwd, shadow, group, gshadow) no_grp = { "symptom": "Accounts with invalid gid.", "finding": ["gid 100 assigned without /etc/groups entry: miss"], "type": "PARSER_ANOMALY" } uid = { "symptom": "Accounts with shared uid.", "finding": ["uid 0 assigned to multiple accounts: bad1,root"], "type": "PARSER_ANOMALY" } gid = { "symptom": "Privileged group with unusual members.", "finding": ["Accounts in 'root' group: bad2"], "type": "PARSER_ANOMALY" } no_match = { "symptom": "Mismatched passwd and shadow files.", "finding": [ "Present in passwd, missing in shadow: miss", "Present in shadow, missing in passwd: ok" ], "type": "PARSER_ANOMALY" } expected = [ rdf_anomaly.Anomaly(**no_grp), rdf_anomaly.Anomaly(**uid), rdf_anomaly.Anomaly(**gid), rdf_anomaly.Anomaly(**no_match) ] parser = linux_file_parser.LinuxSystemPasswdParser() rdfs = parser.ParseMultiple(stats, files, None) results = [r for r in rdfs if isinstance(r, rdf_anomaly.Anomaly)] self.assertEqual(len(expected), len(results)) for expect, result in zip(expected, results): self.assertEqual(expect.symptom, result.symptom) # Expand out repeated field helper. self.assertItemsEqual(list(expect.finding), list(result.finding)) self.assertEqual(expect.type, result.type)
def CheckCryptResults(self, passwd, shadow, group, gshadow, algo, usr, grp): stats, files = self._GenFiles(passwd, shadow, group, gshadow) parser = linux_file_parser.LinuxSystemPasswdParser() results = list(parser.ParseMultiple(stats, files, None)) usrs = [r for r in results if isinstance(r, rdfvalue.KnowledgeBaseUser)] grps = [r for r in results if isinstance(r, rdfvalue.Group)] self.assertEqual(1, len(usrs), "Different number of usr %s results" % algo) self.assertEqual(1, len(grps), "Different number of grp %s results" % algo) self.CheckExpectedUser(algo, usr, usrs[0]) self.CheckExpectedGroup(algo, grp, grps[0])
def _GenResults(self): host_data = self.SetKnowledgeBase() login = { "/etc/passwd": """ nopasswd:x:1000:1000::/home/nopasswd:/bin/bash md5:x:1001:1001::/home/md5:/bin/bash undying:x:1002:1002::/home/undying:/bin/bash disabled:x:1003:1003::/home/disabled:/bin/bash +nisuser:acr.7pt3dpA5s::::::/bin/zsh""", "/etc/shadow": """ nopasswd::16000:0:365:7::: md5:$1$rootrootrootrootrootro:16000:::::: undying:$6$saltsalt${0}:16000:0:99999:7::: disabled:!:16000:0:99999:7:::""".format("r" * 86), "/etc/group": """ nopasswd:x:1000:nopasswd +::: md5:x:1001:md5 undying:x:1002:undying disabled:x:1003:disabled""", "/etc/gshadow": """ nopasswd:::nopasswd md5:::md5 undying:::undying disabled:::disabled""" } perms = { "/etc/passwd": (0, 0, 0o100666), # Anomalous write perm. "/etc/group": (1, 0, 0o100644), # Anomalous owner. "/etc/shadow": (0, 0, 0o100444), # Anomalous read perm. "/etc/gshadow": (0, 1, 0o100400) } # Anomalous group. stats = [] files = [] for path, lines in login.items(): p = rdf_paths.PathSpec(path=path, pathtype="OS") st_uid, st_gid, st_mode = perms.get(path) stats.append( rdf_client.StatEntry(pathspec=p, st_uid=st_uid, st_gid=st_gid, st_mode=st_mode)) files.append(StringIO.StringIO(lines)) parser = linux_file_parser.LinuxSystemPasswdParser() rdfs = list(parser.ParseMultiple(stats, files, None)) host_data["LoginPolicyConfiguration"] = self.SetArtifactData( anomaly=[a for a in rdfs if isinstance(a, rdf_anomaly.Anomaly)], parsed=[r for r in rdfs if not isinstance(r, rdf_anomaly.Anomaly)], raw=stats) return self.RunChecks(host_data)
def testNoAnomaliesWhenEverythingIsFine(self): passwd = ["ok_1:x:1000:1000::/home/ok_1:/bin/bash", "ok_2:x:1001:1001::/home/ok_2:/bin/bash"] shadow = ["ok_1:{SHA256}:16000:0:99999:7:::", "ok_2:{SHA512}:16000:0:99999:7:::"] group = ["ok_1:x:1000:ok_1", "ok_2:x:1001:ok_2"] gshadow = ["ok_1:::ok_1", "ok_2:::ok_2"] stats, files = self._GenFiles(passwd, shadow, group, gshadow) parser = linux_file_parser.LinuxSystemPasswdParser() rdfs = parser.ParseMultiple(stats, files, None) results = [r for r in rdfs if isinstance(r, rdfvalue.Anomaly)] self.assertFalse(results)
def _GenResults(self): parser = linux_file_parser.LinuxSystemPasswdParser() if self.results is None: host_data = self.SetKnowledgeBase() login = { "/etc/passwd": """ nopasswd:x:1000:1000::/home/nopasswd:/bin/bash md5:x:1001:1001::/home/md5:/bin/bash undying:x:1002:1002::/home/undying:/bin/bash disabled:x:1003:1003::/home/disabled:/bin/bash +nisuser:acr.7pt3dpA5s::::::/bin/zsh""", "/etc/shadow": """ nopasswd::16000:0:365:7::: md5:$1$rootrootrootrootrootro:16000:0:365:7::: undying:$6$saltsalt${0}:16000:0:99999:7::: disabled:!:16000:0:99999:7:::""".format("r" * 86), "/etc/group": """ nopasswd:x:1000:nopasswd +::: md5:x:1001:md5 undying:x:1002:undying disabled:x:1003:disabled""", "/etc/gshadow": """ nopasswd:::nopasswd md5:::md5 undying:::undying disabled:::disabled""" } modes = { "/etc/passwd": { "st_mode": 0o100666 }, # Bad write perm. "/etc/group": { "st_uid": 1 }, # Bad owner. "/etc/shadow": { "st_mode": 0o100444 }, # Bad read perm. "/etc/gshadow": { "st_gid": 1, "st_mode": 0o100400 } } # Bad group. host_data = self.GenFileData("LoginPolicyConfiguration", login, parser, modes) return self.RunChecks(host_data)
def _GenResults(self): if self.results is None: host_data = self.SetKnowledgeBase() login = { "/etc/passwd": """ nopasswd:x:1000:1000::/home/nopasswd:/bin/bash md5:x:1001:1001::/home/md5:/bin/bash undying:x:1002:1002::/home/undying:/bin/bash +nisuser:acr.7pt3dpA5s::::::/bin/zsh""", "/etc/shadow": """ nopasswd::16000:0:365:7::: md5:$1$rootrootrootrootrootro:16000:0:365:7::: undying:$6$saltsalt${0}:16000:0:99999:7:::""".format("r" * 86), "/etc/group": """ nopasswd:x:1000:nopasswd +::: md5:x:1001:md5 undying:x:1002:undying""", "/etc/gshadow": """ nopasswd:::nopasswd md5:::md5 undying:::undying""" } stats = [] files = [] for path, lines in login.items(): p = rdfvalue.PathSpec(path=path) stats.append(rdfvalue.StatEntry(pathspec=p)) files.append(StringIO.StringIO(lines)) parser = linux_file_parser.LinuxSystemPasswdParser() rdfs = list(parser.ParseMultiple(stats, files, None)) host_data["LoginPolicyConfiguration"] = rdfs return self.RunChecks(host_data)