From cedbab94fcff83bf16629123aea93d99ea7ffcd7 Mon Sep 17 00:00:00 2001 From: Mike Cohen Date: Sun, 19 Jan 2025 01:18:47 +1000 Subject: [PATCH] Detect Immutable files in EXT4 Adds artifact Linux.Forensics.ImmutableFiles for detecting immutable files. --- .github/workflows/go.yml | 4 + accessors/ext4/ext4_accessor.go | 16 +- .../Linux/Forensics/ImmutableFiles.yaml | 50 +++ .../testdata/server/testcases/ext4.out.yaml | 288 +++--------------- go.mod | 3 +- go.sum | 4 +- 6 files changed, 120 insertions(+), 245 deletions(-) create mode 100644 artifacts/definitions/Linux/Forensics/ImmutableFiles.yaml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 33096f0f54e..0787c555fbe 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -23,6 +23,10 @@ jobs: with: go-version: '^1.23' + # Caching seems to really slow down the build due to the time + # taken to save the cache. + cache: false + - run: go version - name: Get dependencies diff --git a/accessors/ext4/ext4_accessor.go b/accessors/ext4/ext4_accessor.go index cb81d372d24..25e753a9952 100644 --- a/accessors/ext4/ext4_accessor.go +++ b/accessors/ext4/ext4_accessor.go @@ -26,7 +26,17 @@ func (self *Ext4FileInfo) IsDir() bool { } func (self *Ext4FileInfo) Data() *ordereddict.Dict { - return self.Dict() + data := ordereddict.NewDict(). + Set("Inode", self.Inode()). + Set("Uid", self.Uid()). + Set("Gid", self.Gid()) + + flags := self.Flags() + if len(flags) > 0 { + data.Set("Flags", flags) + } + + return data } func (self *Ext4FileInfo) UniqueName() string { @@ -130,8 +140,10 @@ func (self *Ext4FileSystemAccessor) ReadDirWithOSPath( // List the directory. for _, info := range dir { + name := info.Name() + // Skip these useless directories. - if info.Name() == "." || info.Name() == ".." { + if name == "" || name == "." || name == ".." { continue } diff --git a/artifacts/definitions/Linux/Forensics/ImmutableFiles.yaml b/artifacts/definitions/Linux/Forensics/ImmutableFiles.yaml new file mode 100644 index 00000000000..5e9adbd34a6 --- /dev/null +++ b/artifacts/definitions/Linux/Forensics/ImmutableFiles.yaml @@ -0,0 +1,50 @@ +name: Linux.Forensics.ImmutableFiles +description: | + Attackers sometimes enable immutable files in Linux. + + This prevents files from being modified. However this is sometimes a + strong signal. + + This artifact searches the filesystem for such files. + + NOTE: We use the ext4 accessor to parse the low level filessystem. + +precondition: | + SELECT * FROM info() where OS = 'linux' + +parameters: + - name: SearchFilesGlob + default: /home/* + description: Use a glob to define the files that will be searched. + - name: OneFilesystem + default: N + type: bool + description: When set we do not follow a link to go on to a different filesystem. + + - name: DoNotFollowSymlinks + type: bool + default: N + description: If specified we are allowed to follow symlinks while globbing + +column_types: + - name: ATime + type: timestamp + - name: MTime + type: timestamp + - name: CTime + type: timestamp + + +sources: +- query: | + SELECT OSPath, + Sys.mft as Inode, + Mode.String AS Mode, Size, + Mtime AS MTime, + Atime AS ATime, + Ctime AS CTime, + IsDir, Mode, Data + FROM glob(globs=SearchFilesGlob, + one_filesystem=OneFilesystem, + accessor="ext4", nosymlink=DoNotFollowSymlinks) + WHERE Data.Flags =~ "IMMUTABLE" diff --git a/artifacts/testdata/server/testcases/ext4.out.yaml b/artifacts/testdata/server/testcases/ext4.out.yaml index c5757e4069f..4258f8cf610 100644 --- a/artifacts/testdata/server/testcases/ext4.out.yaml +++ b/artifacts/testdata/server/testcases/ext4.out.yaml @@ -23,21 +23,9 @@ ORDER BY OSPath "IsDir": true, "IsLink": false, "Data": { - "Name": "directory1", - "FullPath": "directory1", - "Size": 1024, - "Mode": 2147500525, - "ModeStr": "drwxr-xr-x", - "ModTime": "2018-09-16T11:26:12.384292851Z", - "Mtime": "2018-09-16T11:26:12.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000256Z", - "Btime": "2018-09-16T11:26:44Z", - "Data": { - "Inode": 12, - "Uid": 0, - "Gid": 0 - } + "Inode": 12, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -56,21 +44,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune1", - "FullPath": "directory1/fortune1", - "Size": 48, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:25:09.384292851Z", - "Mtime": "2018-09-16T11:25:09.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000014Z", - "Btime": "2018-09-16T11:25:00Z", - "Data": { - "Inode": 17, - "Uid": 0, - "Gid": 0 - } + "Inode": 17, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -89,21 +65,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune2", - "FullPath": "directory1/fortune2", - "Size": 56, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:25:00.384292851Z", - "Mtime": "2018-09-16T11:25:00.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000086Z", - "Btime": "2018-09-16T11:26:11Z", - "Data": { - "Inode": 18, - "Uid": 0, - "Gid": 0 - } + "Inode": 18, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -122,21 +86,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune5", - "FullPath": "directory1/fortune5", - "Size": 344, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:26:11.384292851Z", - "Mtime": "2018-09-16T11:26:11.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000027Z", - "Btime": "2018-09-16T11:26:12Z", - "Data": { - "Inode": 19, - "Uid": 0, - "Gid": 0 - } + "Inode": 19, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -155,21 +107,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune6", - "FullPath": "directory1/fortune6", - "Size": 111, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:26:12.384292851Z", - "Mtime": "2018-09-16T11:26:12.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000013Z", - "Btime": "2018-09-16T11:25:28Z", - "Data": { - "Inode": 20, - "Uid": 0, - "Gid": 0 - } + "Inode": 20, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -188,21 +128,9 @@ ORDER BY OSPath "IsDir": true, "IsLink": false, "Data": { - "Name": "subdirectory1", - "FullPath": "directory1/subdirectory1", - "Size": 1024, - "Mode": 2147500525, - "ModeStr": "drwxr-xr-x", - "ModTime": "2018-09-16T11:25:30.384292851Z", - "Mtime": "2018-09-16T11:25:30.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000256Z", - "Btime": "2018-09-16T11:26:26Z", - "Data": { - "Inode": 14, - "Uid": 0, - "Gid": 0 - } + "Inode": 14, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -221,21 +149,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune3", - "FullPath": "directory1/subdirectory1/fortune3", - "Size": 52, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:25:28.384292851Z", - "Mtime": "2018-09-16T11:25:28.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000016Z", - "Btime": "2018-09-16T11:25:33Z", - "Data": { - "Inode": 21, - "Uid": 0, - "Gid": 0 - } + "Inode": 21, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -254,21 +170,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune4", - "FullPath": "directory1/subdirectory1/fortune4", - "Size": 65, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:25:33.384292851Z", - "Mtime": "2018-09-16T11:25:33.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.00000001Z", - "Btime": "2018-09-16T11:26:25Z", - "Data": { - "Inode": 22, - "Uid": 0, - "Gid": 0 - } + "Inode": 22, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -287,21 +191,9 @@ ORDER BY OSPath "IsDir": true, "IsLink": false, "Data": { - "Name": "subdirectory2", - "FullPath": "directory1/subdirectory2", - "Size": 1024, - "Mode": 2147500525, - "ModeStr": "drwxr-xr-x", - "ModTime": "2018-09-16T11:26:26.384292851Z", - "Mtime": "2018-09-16T11:26:26.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000212399Z", - "Btime": "2018-09-08T06:08:45Z", - "Data": { - "Inode": 15, - "Uid": 0, - "Gid": 0 - } + "Inode": 15, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -320,21 +212,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune7", - "FullPath": "directory1/subdirectory2/fortune7", - "Size": 40, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:26:25.384292851Z", - "Mtime": "2018-09-16T11:26:25.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000005Z", - "Btime": "2018-09-16T11:26:26Z", - "Data": { - "Inode": 23, - "Uid": 0, - "Gid": 0 - } + "Inode": 23, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -353,21 +233,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune8", - "FullPath": "directory1/subdirectory2/fortune8", - "Size": 20, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:26:26.384292851Z", - "Mtime": "2018-09-16T11:26:26.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000075Z", - "Btime": "2018-09-16T11:26:44Z", - "Data": { - "Inode": 24, - "Uid": 0, - "Gid": 0 - } + "Inode": 24, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -386,21 +254,9 @@ ORDER BY OSPath "IsDir": true, "IsLink": false, "Data": { - "Name": "directory2", - "FullPath": "directory2", - "Size": 1024, - "Mode": 2147500525, - "ModeStr": "drwxr-xr-x", - "ModTime": "2018-09-16T11:26:44.384292851Z", - "Mtime": "2018-09-16T11:26:44.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000256Z", - "Btime": "2018-09-16T11:25:30Z", - "Data": { - "Inode": 13, - "Uid": 0, - "Gid": 0 - } + "Inode": 13, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -419,21 +275,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune10", - "FullPath": "directory2/fortune10", - "Size": 303, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:26:44.384292851Z", - "Mtime": "2018-09-16T11:26:44.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000009Z", - "Btime": "2018-09-16T11:26:42Z", - "Data": { - "Inode": 25, - "Uid": 0, - "Gid": 0 - } + "Inode": 25, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -452,21 +296,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "fortune9", - "FullPath": "directory2/fortune9", - "Size": 39, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-16T11:26:42Z", - "Mtime": "2018-09-16T11:26:42Z", - "Atime": "2018-09-17T08:03:24Z", - "Ctime": "2018-09-17T08:03:24Z", - "Btime": "1970-01-01T00:00:00Z", - "Data": { - "Inode": 26, - "Uid": 0, - "Gid": 0 - } + "Inode": 26, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -485,21 +317,9 @@ ORDER BY OSPath "IsDir": true, "IsLink": false, "Data": { - "Name": "lost+found", - "FullPath": "lost+found", - "Size": 12288, - "Mode": 2147500480, - "ModeStr": "drwx------", - "ModTime": "2018-09-08T05:48:46.384292851Z", - "Mtime": "2018-09-08T05:48:46.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000256Z", - "Btime": "2018-09-16T11:26:12Z", - "Data": { - "Inode": 11, - "Uid": 0, - "Gid": 0 - } + "Inode": 11, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" @@ -518,21 +338,9 @@ ORDER BY OSPath "IsDir": false, "IsLink": false, "Data": { - "Name": "thejungle.txt", - "FullPath": "thejungle.txt", - "Size": 849597, - "Mode": 33188, - "ModeStr": "-rw-r--r--", - "ModTime": "2018-09-08T06:08:45.384292851Z", - "Mtime": "2018-09-08T06:08:45.384292851Z", - "Atime": "2018-09-17T08:03:24.384292851Z", - "Ctime": "2018-09-17T08:03:24.000000012Z", - "Btime": "2018-09-16T11:25:09Z", - "Data": { - "Inode": 16, - "Uid": 0, - "Gid": 0 - } + "Inode": 16, + "Uid": 0, + "Gid": 0 }, "Globs": [ "/**" diff --git a/go.mod b/go.mod index ad45ac4dd5e..a118333d3dd 100644 --- a/go.mod +++ b/go.mod @@ -104,7 +104,7 @@ require ( github.com/Velocidex/file-rotatelogs v0.0.0-20211221020724-d12e4dae4e11 github.com/Velocidex/fileb0x v1.1.2-0.20241111170537-c093c89cd042 github.com/Velocidex/go-ewf v0.0.0-20240210123447-97dc81b7d8c3 - github.com/Velocidex/go-ext4 v0.0.0-20240608083317-8dd00855b069 + github.com/Velocidex/go-ext4 v0.0.0-20250118151314-92624304aad4 github.com/Velocidex/go-fat v0.0.0-20230923165230-3e6c4265297a github.com/Velocidex/go-journalctl v0.0.0-20241004063153-cc1c858415bd github.com/Velocidex/go-mscfb v0.0.0-20240618091452-31f4ccc54002 @@ -311,6 +311,7 @@ require ( // replace github.com/Velocidex/sigma-go => ../sigma-go // replace github.com/Velocidex/tracee_velociraptor => ../tracee_velociraptor // replace github.com/Velocidex/fileb0x => ../fileb0x +// replace github.com/Velocidex/go-ext4 => ../go-ext4 // Remove search for html end block. This allows inserting unbalanced // HTML tags into the markdown diff --git a/go.sum b/go.sum index 0113286c8c2..9b555f3f77b 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ github.com/Velocidex/go-elasticsearch/v7 v7.3.1-0.20191001125819-fee0ef9cac6b h1 github.com/Velocidex/go-elasticsearch/v7 v7.3.1-0.20191001125819-fee0ef9cac6b/go.mod h1:draN67DBVJDAVmLWDIJ85CrV0UxmIGfWZ4njukhINQs= github.com/Velocidex/go-ewf v0.0.0-20240210123447-97dc81b7d8c3 h1:0/ra1WgtmIrYZY4oU3pgp5l9A+5/DgJpz3mAyt0eVik= github.com/Velocidex/go-ewf v0.0.0-20240210123447-97dc81b7d8c3/go.mod h1:JrGP9QRoPe63ijMmU1UTfoGySg+zpgx68XcsGV/dItI= -github.com/Velocidex/go-ext4 v0.0.0-20240608083317-8dd00855b069 h1:9OVXv4dSnn23i5nL0vA/BBN4TbGSOk0496n1g1rOTIo= -github.com/Velocidex/go-ext4 v0.0.0-20240608083317-8dd00855b069/go.mod h1:Sbqqh1t+nYXmNWw0dZC8LOIxP7z5Wg94SP+4Ej1QZqg= +github.com/Velocidex/go-ext4 v0.0.0-20250118151314-92624304aad4 h1:8akJy9YyVvycf06NHPWmjWj8qf8YNsNjEkm19CRwBw8= +github.com/Velocidex/go-ext4 v0.0.0-20250118151314-92624304aad4/go.mod h1:Sbqqh1t+nYXmNWw0dZC8LOIxP7z5Wg94SP+4Ej1QZqg= github.com/Velocidex/go-fat v0.0.0-20230923165230-3e6c4265297a h1:dWHPlB3C86vh+M5P14dZxF6Hh8o2/u8FTRF/bs2EM+Q= github.com/Velocidex/go-fat v0.0.0-20230923165230-3e6c4265297a/go.mod h1:g74FCv59tsVP48V2o1eyIK8aKbNKPLJIJ+HuiUPVc6E= github.com/Velocidex/go-journalctl v0.0.0-20241004063153-cc1c858415bd h1:CSTW6zYoG1IFxaGM3N42wSwruigV1xZ4gNzjLgb2xIc=