Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix UTMP misinterpretation of IPv6 addresses #292

Merged
merged 22 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
26 changes: 9 additions & 17 deletions dissect/target/plugins/os/unix/log/btmp.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import socket
import struct

from dissect.util.ts import from_unix

from dissect.target.helpers.record import TargetRecordDescriptor
from dissect.target.plugin import Plugin, export
from dissect.target.plugins.os.unix.log.utmp import UtmpFile, utmp
from dissect.target.plugins.os.unix.log.utmp import UtmpFile

BtmpRecord = TargetRecordDescriptor(
"linux/log/btmp",
Expand Down Expand Up @@ -45,19 +40,16 @@ def btmp(self):
btmp = UtmpFile(self.target.fs.open(btmp_path), compressed=True)
else:
btmp = UtmpFile(self.target.fs.open(btmp_path))
r_type = ""
for entry in btmp:
if entry.ut_type in utmp.Type.reverse:
r_type = utmp.Type.reverse[entry.ut_type]

for entry in btmp:
yield BtmpRecord(
ts=from_unix(entry.ut_tv.tv_sec),
ut_type=r_type,
ts=entry.ts,
ut_type=entry.ut_type,
ut_pid=entry.ut_pid,
ut_user=entry.ut_user.decode().strip("\x00"),
ut_line=entry.ut_line.decode().strip("\x00"),
ut_id=entry.ut_id.decode().strip("\x00"),
ut_host=entry.ut_host.decode().strip("\x00"),
ut_addr=socket.inet_ntoa(struct.pack("<i", entry.ut_addr_v6[0])),
ut_user=entry.ut_user,
ut_line=entry.ut_line,
ut_id=entry.ut_id,
ut_host=entry.ut_host,
ut_addr=entry.ut_addr,
_target=self.target,
)
48 changes: 45 additions & 3 deletions dissect/target/plugins/os/unix/log/utmp.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import gzip
import ipaddress
import struct
from collections import namedtuple

from dissect import cstruct
from dissect.util.stream import BufferedStream
from dissect.util.ts import from_unix

c_utmp = """
#define UT_LINESIZE 32
Expand Down Expand Up @@ -43,14 +47,28 @@
struct exit_status ut_exit;
long ut_session;
struct timeval ut_tv;
int32_t ut_addr_v6[4];
int32_t ut_addr_v6[4]; // Internet address of remote host; IPv4 address uses just ut_addr_v6[0]
char __unused[20];
};
"""
""" # noqa: E501

utmp = cstruct.cstruct()
utmp.load(c_utmp)

UTMP_ENTRY = namedtuple(
"UTMPRecord",
[
"ts",
"ut_type",
"ut_user",
"ut_pid",
"ut_line",
"ut_id",
"ut_host",
"ut_addr",
],
)


class UtmpFile:
"""utmp maintains a full accounting of the current status of the system"""
Expand All @@ -68,6 +86,30 @@ def __iter__(self):

while True:
try:
yield utmp.entry(byte_stream)
entry = utmp.entry(byte_stream)

r_type = ""
if entry.ut_type in utmp.Type.reverse:
r_type = utmp.Type.reverse[entry.ut_type]

# UTMP misuses the field ut_addr_v6 for IPv4 and IPv6 addresses, because of this
pyrco marked this conversation as resolved.
Show resolved Hide resolved
# if the last 12 bytes are zero the value is read as an IPv4-address.
if entry.ut_addr_v6[1:] == [0, 0, 0]:
ut_addr = struct.pack("<i", entry.ut_addr_v6[0])
else:
ut_addr = struct.pack("<4i", *entry.ut_addr_v6)
Zawadidone marked this conversation as resolved.
Show resolved Hide resolved

utmp_entry = UTMP_ENTRY(
ts=from_unix(entry.ut_tv.tv_sec),
ut_type=r_type,
ut_pid=entry.ut_pid,
ut_user=entry.ut_user.decode().strip("\x00"),
ut_line=entry.ut_line.decode().strip("\x00"),
ut_id=entry.ut_id.decode().strip("\x00"),
ut_host=entry.ut_host.decode().strip("\x00"),
ut_addr=ipaddress.ip_address(ut_addr),
Zawadidone marked this conversation as resolved.
Show resolved Hide resolved
)

yield utmp_entry
except EOFError:
break
26 changes: 9 additions & 17 deletions dissect/target/plugins/os/unix/log/wtmp.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import socket
import struct

from dissect.util.ts import from_unix

from dissect.target.helpers.record import TargetRecordDescriptor
from dissect.target.plugin import Plugin, export
from dissect.target.plugins.os.unix.log.utmp import UtmpFile, utmp
from dissect.target.plugins.os.unix.log.utmp import UtmpFile

WtmpRecord = TargetRecordDescriptor(
"linux/log/wtmp",
Expand Down Expand Up @@ -44,19 +39,16 @@ def wtmp(self):
wtmp = UtmpFile(self.target.fs.open(wtmp_path), compressed=True)
else:
wtmp = UtmpFile(self.target.fs.open(wtmp_path))
r_type = ""
for entry in wtmp:
if entry.ut_type in utmp.Type.reverse:
r_type = utmp.Type.reverse[entry.ut_type]

for entry in wtmp:
yield WtmpRecord(
ts=from_unix(entry.ut_tv.tv_sec),
ut_type=r_type,
ts=entry.ts,
ut_type=entry.ut_type,
ut_pid=entry.ut_pid,
ut_user=entry.ut_user.decode().strip("\x00"),
ut_line=entry.ut_line.decode().strip("\x00"),
ut_id=entry.ut_id.decode().strip("\x00"),
ut_host=entry.ut_host.decode().strip("\x00"),
ut_addr=socket.inet_ntoa(struct.pack("<i", entry.ut_addr_v6[0])),
ut_user=entry.ut_user,
ut_line=entry.ut_line,
ut_id=entry.ut_id,
ut_host=entry.ut_host,
ut_addr=entry.ut_addr,
_target=self.target,
)