Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
12 changes: 8 additions & 4 deletions dissect/target/plugins/apps/shell/powershell.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
[
("datetime", "mtime"),
("string", "command"),
("varint", "order"),
("path", "source"),
],
)
Expand Down Expand Up @@ -52,18 +53,21 @@ def powershell_history(self) -> Iterator[ConsoleHostHistoryRecord]:
- https://learn.microsoft.com/en-us/powershell/module/psreadline/about/about_psreadline?view=powershell-7.3#command-history
""" # noqa E501

for user, _path in self._history:
file_mtime = _path.stat().st_mtime
for user, path in self._history:
file_mtime = path.stat().st_mtime

for line in _path.open("r"):
i = -1
for line in path.open("r"):
line = line.strip()
if not line:
continue
i += 1

yield ConsoleHostHistoryRecord(
mtime=file_mtime,
command=line,
source=_path,
order=i,
source=path,
_target=self.target,
_user=user,
)
11 changes: 9 additions & 2 deletions dissect/target/plugins/os/unix/bsd/citrix/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ def commandhistory(self) -> Iterator[CommandHistoryRecord]:

def parse_netscaler_bash_history(self, path: TargetPath) -> Iterator[CommandHistoryRecord]:
"""Parse bash.log* contents."""

i = -1
for ts, line in year_rollover_helper(path, RE_CITRIX_NETSCALER_BASH_HISTORY_DATE, "%b %d %H:%M:%S "):
line = line.strip()
if not line:
Expand All @@ -94,13 +96,15 @@ def parse_netscaler_bash_history(self, path: TargetPath) -> Iterator[CommandHist
if not match:
continue

i += 1
group = match.groupdict()
command = group.get("command")
user = self._find_user_by_name(group.get("username"))

yield CommandHistoryRecord(
ts=ts,
command=command,
order=-i,
shell="citrix-netscaler-bash",
source=path,
_target=self.target,
Expand All @@ -115,16 +119,19 @@ def parse_netscaler_cli_history(
The only difference compared to generic bash history files is that the first line will start with
``_HiStOrY_V2_``, which we will skip.
"""
for idx, line in enumerate(history_file.open("rt")):
i = -1
for line in history_file.open("rt"):
if not (line := line.strip()):
continue

if idx == 0 and line == "_HiStOrY_V2_":
if i == -1 and line == "_HiStOrY_V2_":
continue

i += 1
yield CommandHistoryRecord(
ts=None,
command=line,
order=i,
shell="citrix-netscaler-cli",
source=history_file,
_target=self.target,
Expand Down
10 changes: 9 additions & 1 deletion dissect/target/plugins/os/unix/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
[
("datetime", "ts"),
("string", "command"),
("varint", "order"),
("string", "shell"),
("path", "source"),
],
Expand Down Expand Up @@ -95,6 +96,7 @@ def parse_generic_history(self, file, user: UnixUserRecord, shell: str) -> Itera
"""
next_cmd_ts = None

i = -1
for line in file.open("rt", errors="replace"):
ts = None
line = line.strip()
Expand All @@ -110,9 +112,11 @@ def parse_generic_history(self, file, user: UnixUserRecord, shell: str) -> Itera
ts = next_cmd_ts
next_cmd_ts = None

i += 1
yield CommandHistoryRecord(
ts=ts,
command=line,
order=i,
shell=shell,
source=file,
_target=self.target,
Expand All @@ -133,6 +137,7 @@ def parse_zsh_history(self, file, user: UnixUserRecord) -> Iterator[CommandHisto
Resources:
- https://sourceforge.net/p/zsh/code/ci/master/tree/Src/hist.c
"""
i = -1
for line in file.open("rt", errors="replace"):
line = line.strip()

Expand All @@ -146,9 +151,11 @@ def parse_zsh_history(self, file, user: UnixUserRecord) -> Iterator[CommandHisto
ts = None
command = line

i += 1
yield CommandHistoryRecord(
ts=ts,
command=command,
order=i,
shell="zsh",
source=file,
_target=self.target,
Expand Down Expand Up @@ -182,10 +189,11 @@ def parse_fish_history(self, history_file: TargetPath, user: UnixUserRecord) ->
with history_file.open("r") as h_file:
history_data = h_file.read()

for command, ts in RE_FISH.findall(history_data):
for i, (command, ts) in enumerate(RE_FISH.findall(history_data)):
yield CommandHistoryRecord(
ts=from_unix(int(ts)),
command=command,
order=i,
shell="fish",
source=history_file,
_target=self.target,
Expand Down
1 change: 1 addition & 0 deletions tests/plugins/apps/shell/test_powershell.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ def test_plugins_os_windows_powershell(target, fs, target_file, request):

assert len(records) == 4
assert records[0].command == 'Write-Host "Hello World!"'
assert records[0].order == 0
assert str(records[0].source) == target_file
6 changes: 6 additions & 0 deletions tests/plugins/os/unix/bsd/citrix/test_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ def test_netscaler_bash_history_absolute_path_glob(target_citrix: Target, fs_bsd
# year from the file's mtime, which in this mocked version is epoch 0 (thus the year 1970)
assert results[1].ts == datetime.datetime(1970, 8, 9, 12, 56, 0, tzinfo=datetime.timezone.utc)
assert results[1].command == "find . -name '*ci.php*'"
assert results[1].order == -1
assert results[1].shell == "citrix-netscaler-bash"
assert results[1].source.as_posix() == "/var/log/bash.log"

assert results[0].ts == datetime.datetime(1970, 8, 10, 11, 57, 39, tzinfo=datetime.timezone.utc)
assert results[0].command == 'debug "hello world"'
assert results[0].order == 0
assert results[0].shell == "citrix-netscaler-bash"
assert results[0].source.as_posix() == "/var/log/bash.log"

Expand All @@ -54,11 +56,13 @@ def test_netscaler_commandhistory_decompress(target_citrix: Target, fs_bsd: Virt
# Due to the usage of year_rollover_history, the results are returned back-to-front
assert results[1].ts == datetime.datetime(1970, 8, 9, 12, 56, 0, tzinfo=datetime.timezone.utc)
assert results[1].command == "find . -name '*ci.php*'"
assert results[1].order == -1
assert results[1].shell == "citrix-netscaler-bash"
assert results[1].source.as_posix() == "/var/log/bash.log.0.gz"

assert results[0].ts == datetime.datetime(1970, 8, 10, 11, 57, 39, tzinfo=datetime.timezone.utc)
assert results[0].command == 'debug "hello world"'
assert results[0].order == 0
assert results[0].shell == "citrix-netscaler-bash"
assert results[0].source.as_posix() == "/var/log/bash.log.0.gz"

Expand All @@ -79,10 +83,12 @@ def test_netscaler_cli_history(target_citrix: Target, fs_bsd: VirtualFilesystem)

assert not results[0].ts
assert results[0].command == "help"
assert results[0].order == 0
assert results[0].shell == "citrix-netscaler-cli"
assert results[0].source.as_posix() == "/var/nstmp/user/.nscli_history"

assert not results[1].ts
assert results[1].command == "shell"
assert results[1].order == 1
assert results[1].shell == "citrix-netscaler-cli"
assert results[1].source.as_posix() == "/var/nstmp/user/.nscli_history"
13 changes: 13 additions & 0 deletions tests/plugins/os/unix/test_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,25 @@ def test_commandhistory_with_timestamps(target_unix_users, fs_unix):

assert results[0].ts == dt("2022-03-29T23:58:59Z")
assert results[0].command == 'echo "this is a test"'
assert results[0].order == 0
assert results[0].shell == "bash"
assert results[0].source.as_posix() == "/root/.bash_history"

assert results[1].ts is None
assert results[1].command == 'echo "O no. A line without timestamp"'
assert results[1].order == 1
assert results[1].shell == "bash"
assert results[1].source.as_posix() == "/root/.bash_history"

assert results[2].ts == dt("2022-07-23T12:14:28Z")
assert results[2].command == "exit"
assert results[2].order == 2
assert results[2].shell == "bash"
assert results[2].source.as_posix() == "/root/.bash_history"

assert results[3].ts is None
assert results[3].command == 'echo "this for user 2"'
assert results[3].order == 0
assert results[3].shell == "bash"
assert results[3].source.as_posix() == "/home/user/.bash_history"

Expand All @@ -77,11 +81,13 @@ def test_commandhistory_without_timestamps(target_unix_users, fs_unix):

assert results[0].ts is None
assert results[0].command == 'echo "Test if basic commandhistory works" > /dev/null'
assert results[0].order == 0
assert results[0].shell == "zsh"
assert results[0].source.as_posix() == "/root/.zsh_history"

assert results[1].ts is None
assert results[1].command == "exit"
assert results[1].order == 1
assert results[1].shell == "zsh"
assert results[1].source.as_posix() == "/root/.zsh_history"

Expand All @@ -105,11 +111,13 @@ def test_commandhistory_zsh_history(target_unix_users, fs_unix):

assert results[0].ts == from_unix(1673860722)
assert results[0].command == "sudo apt install sl"
assert results[0].order == 0
assert results[0].shell == "zsh"
assert results[0].source.as_posix() == "/root/.zsh_history"

assert not results[1].ts
assert results[1].command == 'echo "Whoops no timestamps"'
assert results[1].order == 1
assert results[1].shell == "zsh"
assert results[1].source.as_posix() == "/root/.zsh_history"

Expand Down Expand Up @@ -138,16 +146,19 @@ def test_commandhistory_fish_history(target_unix_users, fs_unix):

assert results[0].ts == from_unix(1688642435)
assert results[0].command == "ls"
assert results[0].order == 0
assert results[0].shell == "fish"
assert results[0].source.as_posix() == "/root/.local/share/fish/fish_history"

assert results[1].ts == from_unix(1688642441)
assert results[1].command == "cd home/"
assert results[1].order == 1
assert results[1].shell == "fish"
assert results[1].source.as_posix() == "/root/.local/share/fish/fish_history"

assert results[2].ts == from_unix(1688986629)
assert results[2].command == 'echo "test: test"'
assert results[2].order == 2
assert results[2].shell == "fish"
assert results[2].source.as_posix() == "/root/.local/share/fish/fish_history"

Expand Down Expand Up @@ -214,6 +225,7 @@ def test_commandhistory_database_history(target_unix_users, fs_unix, db_type, db
for i, line in enumerate(history_lines):
assert results[i].ts is None
assert results[i].command == line
assert results[i].order == i
assert results[i].shell == db_type
assert results[i].source.as_posix() == f"/root/{db_file}"

Expand All @@ -233,5 +245,6 @@ def test_commandhistory_is_directory(target_unix_users: Target, fs_unix: Virtual

assert results[0].ts is None
assert results[0].command == "test"
assert results[0].order == 0
assert results[0].shell == "zsh"
assert results[0].source.as_posix() == "/root/.zsh_history"