Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions plaso/parsers/text_plugins/syslog.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,35 @@ class BaseSyslogTextPlugin(interface.TextPlugin):

_CRON_MESSAGE = pyparsing.Group(_CRON_TASK_RUN).set_results_name("task_run")

# OpenSSH 9.8 split the server into a listener binary, sshd, and a per-session
# binary, sshd-session, which writes the authentication messages.
_SSHD_REPORTERS = frozenset(["sshd", "sshd-session"])

_SSHD_AUTHENTICATION_METHOD = pyparsing.Keyword("password") | pyparsing.Keyword(
"publickey"
)

_SSHD_FINGER_PRINT = pyparsing.Combine(
pyparsing.Literal("RSA ") + pyparsing.Word(":" + pyparsing.hexnums)
# A key fingerprint consists of the key type followed by the digest, where the
# digest is either the hash name and a base64 value, such as
# "ED25519 SHA256:5xyQ+PG1Z3CIiShclJ2iNya5TOdKDgE/HrOXr21IdOo", or the older
# colon separated hexadecimal form, such as "RSA 00:aa:bb:cc". The default of
# the sshd_config FingerprintHash option is sha256.
# Note that the hexadecimal form is matched first, since the hash name and
# base64 pattern would otherwise match "00:aa" of "00:aa:bb:cc" and leave the
# remainder of the value unparsed.
_SSHD_FINGER_PRINT = pyparsing.Regex(
r"[A-Za-z0-9-]+ "
r"(?:[0-9a-fA-F]{2}(?::[0-9a-fA-F]{2})+|[A-Za-z0-9]+:[A-Za-z0-9+/=]+)"
).set_results_name("fingerprint")

_SSH_USERNAME = pyparsing.Word(pyparsing.alphanums).set_results_name("username")
# A user name is determined by the text that precedes " from " since sshd
# logs the user name as provided by the client, which is not limited to the
# characters that useradd would accept.
_SSH_USERNAME = (
pyparsing.SkipTo(pyparsing.Literal("from"))
.set_parse_action(lambda tokens: tokens[0].strip())
.set_results_name("username")
)

_SSH_IP_ADDRESS = (
pyparsing.pyparsing_common.ipv4_address
Expand All @@ -181,6 +201,7 @@ class BaseSyslogTextPlugin(interface.TextPlugin):
+ _SSH_IP_ADDRESS.set_results_name("ip_address")
+ pyparsing.Literal("port")
+ _SSH_PORT
+ pyparsing.Optional(pyparsing.Literal("ssh2").set_results_name("protocol"))
+ pyparsing.StringEnd()
)

Expand Down Expand Up @@ -548,7 +569,7 @@ def _ParseRecord(self, parser_mediator, key, structure):
event_data = None
if reporter == "CRON":
event_data = self._ParseCronMessageBody(message_body)
elif reporter == "sshd":
elif reporter in self._SSHD_REPORTERS:
event_data = self._ParseSshdMessageBody(message_body)

if not event_data:
Expand Down Expand Up @@ -833,7 +854,7 @@ def _ParseRecord(self, parser_mediator, key, structure):
event_data = None
if reporter == "CRON":
event_data = self._ParseCronMessageBody(message_body)
elif reporter == "sshd":
elif reporter in self._SSHD_REPORTERS:
event_data = self._ParseSshdMessageBody(message_body)

if not event_data:
Expand Down
8 changes: 8 additions & 0 deletions test_data/syslog/syslog_sshd_session.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Aug 2 11:41:04 localhost sshd-session[2470]: Failed password for svc-backup from 192.168.1.62 port 60203 ssh2
Aug 2 11:41:08 localhost sshd-session[2473]: Failed password for john.doe from 192.168.1.62 port 60204 ssh2
Aug 2 11:42:13 localhost sshd-session[2600]: Accepted password for svc-backup from 192.168.1.62 port 60214 ssh2
Aug 2 11:42:15 localhost sshd-session[2655]: Accepted password for john.doe from 192.168.1.62 port 60215 ssh2
Aug 2 11:42:16 localhost sshd-session[2711]: Accepted password for test_user from 192.168.1.62 port 60217 ssh2
Aug 2 11:42:18 localhost sshd-session[2789]: Received disconnect from 192.168.1.62 port 60218:11: disconnected by user
Aug 2 11:42:18 localhost sshd-session[2789]: Disconnected from user root 192.168.1.62 port 60218
Aug 2 11:42:48 localhost sshd-session[2842]: Accepted publickey for root from 192.168.1.62 port 60220 ssh2: ED25519 SHA256:a79QfkCiaM8pEpw/wmP0Qfkl3ttsHxPlSKgqilMv9K8
111 changes: 111 additions & 0 deletions tests/parsers/text_plugins/syslog.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,117 @@ def testProcessSshd(self):
event_data = storage_writer.GetAttributeContainerByIndex("event_data", 4)
self.CheckEventData(event_data, expected_event_values)

expected_event_values = {
"authentication_method": "password",
"data_type": "syslog:ssh:failed_connection",
"ip_address": "188.124.3.41",
"last_written_time": "0000-03-11T22:55:32",
"port": "32889",
"protocol": "ssh2",
"username": "root",
}
event_data = storage_writer.GetAttributeContainerByIndex("event_data", 5)
self.CheckEventData(event_data, expected_event_values)

expected_event_values = {
"authentication_method": "publickey",
"data_type": "syslog:ssh:login",
"fingerprint": ("RSA SHA256:5xyQ+PG1Z3CIiShclJ2iNya5TOdKDgE/HrOXr21IdOo"),
"ip_address": "192.0.2.60",
"last_written_time": "0000-03-11T22:55:35",
"port": "59915",
"username": "fred",
}
event_data = storage_writer.GetAttributeContainerByIndex("event_data", 8)
self.CheckEventData(event_data, expected_event_values)

def testProcessSshdSession(self):
"""Tests the Process function with a sshd-session syslog file."""
plugin = syslog.TraditionalSyslogTextPlugin()
storage_writer = self._ParseTextFileWithPlugin(
["syslog", "syslog_sshd_session.log"], plugin
)
number_of_event_data = storage_writer.GetNumberOfAttributeContainers(
"event_data"
)
self.assertEqual(number_of_event_data, 8)

number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
"extraction_warning"
)
self.assertEqual(number_of_warnings, 0)

number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
"recovery_warning"
)
self.assertEqual(number_of_warnings, 0)

expected_event_values = {
"authentication_method": "password",
"data_type": "syslog:ssh:failed_connection",
"ip_address": "192.168.1.62",
"last_written_time": "0000-08-02T11:41:04",
"port": "60203",
"protocol": "ssh2",
"reporter": "sshd-session",
"username": "svc-backup",
}
event_data = storage_writer.GetAttributeContainerByIndex("event_data", 0)
self.CheckEventData(event_data, expected_event_values)

expected_event_values = {
"data_type": "syslog:ssh:failed_connection",
"last_written_time": "0000-08-02T11:41:08",
"reporter": "sshd-session",
"username": "john.doe",
}
event_data = storage_writer.GetAttributeContainerByIndex("event_data", 1)
self.CheckEventData(event_data, expected_event_values)

expected_event_values = {
"data_type": "syslog:ssh:login",
"last_written_time": "0000-08-02T11:42:13",
"reporter": "sshd-session",
"username": "svc-backup",
}
event_data = storage_writer.GetAttributeContainerByIndex("event_data", 2)
self.CheckEventData(event_data, expected_event_values)

expected_event_values = {
"data_type": "syslog:ssh:login",
"last_written_time": "0000-08-02T11:42:16",
"reporter": "sshd-session",
"username": "test_user",
}
event_data = storage_writer.GetAttributeContainerByIndex("event_data", 4)
self.CheckEventData(event_data, expected_event_values)

# A message that the sshd structures do not define, which is retained as
# a syslog:line.
expected_event_values = {
"data_type": "syslog:line",
"last_written_time": "0000-08-02T11:42:18",
"reporter": "sshd-session",
}
event_data = storage_writer.GetAttributeContainerByIndex("event_data", 5)
self.CheckEventData(event_data, expected_event_values)

expected_event_values = {
"authentication_method": "publickey",
"data_type": "syslog:ssh:login",
"fingerprint": (
"ED25519 SHA256:a79QfkCiaM8pEpw/wmP0Qfkl3ttsHxPlSKgqilMv9K8"
),
"ip_address": "192.168.1.62",
"last_written_time": "0000-08-02T11:42:48",
"port": "60220",
"protocol": "ssh2",
"reporter": "sshd-session",
"username": "root",
}
event_data = storage_writer.GetAttributeContainerByIndex("event_data", 7)
self.CheckEventData(event_data, expected_event_values)


if __name__ == "__main__":
unittest.main()
Loading