From a0b129fbc5e86fd03696e53b3f845073372000fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Legin?= Date: Mon, 12 Dec 2022 12:26:30 +0100 Subject: [PATCH] Remove sluethkit/mmls as a dependency --- README.md | 10 +-- processors/local/local.go | 142 +---------------------------- processors/local/local_test.go | 158 --------------------------------- 3 files changed, 4 insertions(+), 306 deletions(-) diff --git a/README.md b/README.md index a967f86..c96e0db 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ - [TarGz](#targz) - [Deb](#deb) - [RPM](#rpm) + - [Zip (and other zip-like formats)](#zip-and-other-zip-like-formats) - [GCP (Google Cloud Platform)](#gcp-google-cloud-platform) - [GCR (Google Container Registry)](#gcr-google-container-registry) - [Windows](#windows) @@ -92,15 +93,10 @@ HashR takes care of the heavy lifting (parsing disk images, volumes, file system docker pull log2timeline/plaso ``` -We also need two additional tools installed on the machine running HashR: - -1. mmls, which is part of Sleuthkit, to list volumes on disk image -1. 7z, which is used by WSUS importer for recursive extraction in Windows Update packages - -You can install both with the following command: +We also need 7z, which is used by WSUS importer for recursive extraction of Windows Update packages, to be installed on the machine running HashR: ``` shell -sudo apt install p7zip-full sleuthkit +sudo apt install p7zip-full ``` You need to allow the user, under which HashR will run, to run certain commands via sudo. Assuming that your user is `hashr` create a file `/etc/sudoers.d/hashr` and put in: diff --git a/processors/local/local.go b/processors/local/local.go index 947e6a5..cb831bd 100644 --- a/processors/local/local.go +++ b/processors/local/local.go @@ -18,12 +18,9 @@ package local import ( "bytes" "fmt" - "os" "os/exec" "path/filepath" "regexp" - "strconv" - "strings" "github.com/golang/glog" ) @@ -74,148 +71,11 @@ func (p *Processor) ImageExport(sourcePath string) (string, error) { exportDir := filepath.Join(baseDir, "export") logFile := filepath.Join(baseDir, "image_export.log") - info, err := os.Stat(sourcePath) - if err != nil { - return "", err - } - - var xfsMountPoints []string - - // If the sourcePath is a file, check for XFS volumes and try to mount them. - if info.Mode().IsRegular() { - mmlsOut, err := shellCommand("mmls", sourcePath) - if err != nil { - glog.Infof("mmls error (%v), probably not a disk image", err) - } else { - xfsv, err := xfsVolumes(sourcePath, parseMmlsOutput(mmlsOut)) - if err != nil { - glog.Warningf("error reading source image: %v", err) - } - xfsMountPoints = mountXfs(sourcePath, baseDir, xfsv) - } - } - - // If there is at least one XFS volume that was mounted successfully, we'll pass the mount - // directory to Plaso. - if len(xfsMountPoints) > 0 { - glog.Info("Disk image contains XFS volume(s)") - defer unmountXfs(xfsMountPoints) - sourcePath = filepath.Join(baseDir, "mnt") - } - args := []string{"run", "-v", "/tmp/:/tmp", "log2timeline/plaso", "image_export", "--logfile", logFile, "--partitions", "all", "--volumes", "all", "-w", exportDir, sourcePath} - _, err = shellCommand("docker", args...) + _, err := shellCommand("docker", args...) if err != nil { return "", fmt.Errorf("error while running Plaso: %v", err) } return exportDir, nil } - -func unmountXfs(mountPoints []string) { - for _, mountPoint := range mountPoints { - _, err := shellCommand("sudo", "umount", mountPoint) - if err != nil { - glog.Errorf("error while unmounting volume: %v", err) - } - } -} - -func mountXfs(imagePath, baseDir string, xfsVolumes []volume) []string { - var mountPoints []string - - for _, volume := range xfsVolumes { - mountSubdir := filepath.Join(baseDir, "mnt", fmt.Sprintf("p%d", volume.id)) - if err := os.MkdirAll(mountSubdir, 0755); err != nil { - glog.Errorf("could not create mount directory: %v", err) - continue - } - - _, err := shellCommand("sudo", "mount", "-t", "xfs", "-o", fmt.Sprintf("loop,offset=%d", volume.start*512), imagePath, mountSubdir) - if err != nil { - glog.Errorf("error while executing mount cmd: %v", err) - continue - } - mountPoints = append(mountPoints, mountSubdir) - } - - return mountPoints -} - -func xfsVolumes(imagePath string, volumes []volume) ([]volume, error) { - file, err := os.Open(imagePath) - if err != nil { - return nil, err - } - defer file.Close() - - var xfsPartitions []volume - for _, volume := range volumes { - data := make([]byte, 4) - _, err := file.ReadAt(data, int64(volume.start*512)) - if err != nil { - glog.Errorf("Could not read image file: %v", err) - continue - } - // Check if first 4 bytes of the volume matches XFS signature. - if bytes.Equal(data, []byte{88, 70, 83, 66}) { - xfsPartitions = append(xfsPartitions, volume) - } - } - - return xfsPartitions, nil -} - -func parseMmlsOutput(output string) []volume { - matches := reRow.FindAllString(output, -1) - var volumes []volume - - for _, match := range matches { - // Replace multiple, consecutive spaces with one. - s := reSpace.ReplaceAllString(match, " ") - // Split the row. - row := strings.Split(s, " ") - - if len(row) < 5 { - glog.Warningf("Skipping row %q due to wrong format", row) - continue - } - - var start, length, id int - var err error - - if row[0] == "000:" { - id = 0 - } else { - id, err = strconv.Atoi(strings.TrimRight(strings.TrimLeft(row[0], "0"), ":")) - if err != nil { - glog.Warningf("Skipping row %q due to error: %v", row, err) - continue - } - } - - if row[2] == "0000000000" { - start = 0 - } else { - start, err = strconv.Atoi(strings.TrimLeft(row[2], "0")) - if err != nil { - glog.Warningf("Skipping row %q due to error: %v", row, err) - continue - } - } - - if row[4] == "0000000000" { - length = 0 - } else { - length, err = strconv.Atoi(strings.TrimLeft(row[4], "0")) - if err != nil { - glog.Warningf("Skipping row %q due to error: %v", row, err) - continue - } - } - - volumes = append(volumes, volume{id: id, start: start, length: length}) - } - - return volumes -} diff --git a/processors/local/local_test.go b/processors/local/local_test.go index 801e7e1..39216c5 100644 --- a/processors/local/local_test.go +++ b/processors/local/local_test.go @@ -22,151 +22,8 @@ import ( "os/exec" "path/filepath" "testing" - - "github.com/google/go-cmp/cmp" ) -func TestParseMmlsOutput(t *testing.T) { - cases := []struct { - output string - want []volume - }{ - { - output: `DOS Partition Table -Offset Sector: 0 -Units are in 512-byte sectors - - Slot Start End Length Description -000: Meta 0000000000 0000000000 0000000001 Primary Table (#0) -001: ------- 0000000000 0000002047 0000002048 Unallocated -002: 000:000 0000002048 0020971519 0020969472 Linux (0x83)`, - want: []volume{ - {id: 0, start: 0, length: 1}, - {id: 1, start: 0, length: 2048}, - {id: 2, start: 2048, length: 20969472}, - }, - }, - { - output: `GUID Partition Table (EFI) -Offset Sector: 0 -Units are in 512-byte sectors - - Slot Start End Length Description -000: Meta 0000000000 0000000000 0000000001 Safety Table -001: ------- 0000000000 0000000063 0000000064 Unallocated -002: Meta 0000000001 0000000001 0000000001 GPT Header -003: Meta 0000000002 0000000033 0000000032 Partition Table -004: 010 0000000064 0000016447 0000016384 RWFW -005: 005 0000016448 0000016448 0000000001 KERN-C -006: 006 0000016449 0000016449 0000000001 ROOT-C -007: 008 0000016450 0000016450 0000000001 reserved -008: 009 0000016451 0000016451 0000000001 reserved -009: ------- 0000016452 0000020479 0000004028 Unallocated -010: 001 0000020480 0000053247 0000032768 KERN-A -011: 003 0000053248 0000086015 0000032768 KERN-B -012: 007 0000086016 0000118783 0000032768 OEM -013: ------- 0000118784 0000249855 0000131072 Unallocated -014: 011 0000249856 0000315391 0000065536 EFI-SYSTEM -015: 004 0000315392 0004509695 0004194304 ROOT-B -016: 002 0004509696 0008703999 0004194304 ROOT-A -017: 000 0008704000 0018874476 0010170477 STATE -018: ------- 0018874477 0020971519 0002097043 Unallocated`, - want: []volume{ - {id: 0, start: 0, length: 1}, - {id: 1, start: 0, length: 64}, - {id: 2, start: 1, length: 1}, - {id: 3, start: 2, length: 32}, - {id: 4, start: 64, length: 16384}, - {id: 5, start: 16448, length: 1}, - {id: 6, start: 16449, length: 1}, - {id: 7, start: 16450, length: 1}, - {id: 8, start: 16451, length: 1}, - {id: 9, start: 16452, length: 4028}, - {id: 10, start: 20480, length: 32768}, - {id: 11, start: 53248, length: 32768}, - {id: 12, start: 86016, length: 32768}, - {id: 13, start: 118784, length: 131072}, - {id: 14, start: 249856, length: 65536}, - {id: 15, start: 315392, length: 4194304}, - {id: 16, start: 4509696, length: 4194304}, - {id: 17, start: 8704000, length: 10170477}, - {id: 18, start: 18874477, length: 2097043}, - }, - }, - { - output: `GUID Partition Table (EFI) -Offset Sector: 0 -Units are in 512-byte sectors - - Slot Start End Length Description -000: Meta 0000000000 0000000000 0000000001 Safety Table -001: ------- 0000000000 0000002047 0000002048 Unallocated -002: Meta 0000000001 0000000001 0000000001 GPT Header -003: Meta 0000000002 0000000033 0000000032 Partition Table -004: 000 0000002048 0000006143 0000004096 p.legacy -005: 001 0000006144 0000047103 0000040960 p.UEFI -006: 002 0000047104 0020971486 0020924383 p.lxroot -007: ------- 0020971487 0020971519 0000000033 Unallocated`, - want: []volume{ - {id: 0, start: 0, length: 1}, - {id: 1, start: 0, length: 2048}, - {id: 2, start: 1, length: 1}, - {id: 3, start: 2, length: 32}, - {id: 4, start: 2048, length: 4096}, - {id: 5, start: 6144, length: 40960}, - {id: 6, start: 47104, length: 20924383}, - {id: 7, start: 20971487, length: 33}, - }, - }, - { - output: `GUID Partition Table (EFI) -Offset Sector: 0 -Units are in 512-byte sectors - - Slot Start End Length Description -000: Meta 0000000000 0000000000 0000000001 Safety Table -001: ------- 0000000000 0000002047 0000002048 Unallocated -002: Meta 0000000001 0000000001 0000000001 GPT Header -003: Meta 0000000002 0000000033 0000000032 Partition Table -004: 000 0000002048 0000411647 0000409600 EFI System Partition -005: 001 0000411648 0041940991 0041529344 -006: ------- 0041940992 0041943039 0000002048 Unallocated`, - want: []volume{ - {id: 0, start: 0, length: 1}, - {id: 1, start: 0, length: 2048}, - {id: 2, start: 1, length: 1}, - {id: 3, start: 2, length: 32}, - {id: 4, start: 2048, length: 409600}, - {id: 5, start: 411648, length: 41529344}, - {id: 6, start: 41940992, length: 2048}, - }, - }, - } - - for _, tc := range cases { - got := parseMmlsOutput(tc.output) - - if !cmp.Equal(tc.want, got, cmp.AllowUnexported(volume{})) { - t.Errorf("parseMmlsOutput() unexpected diff (-want/+got):\n%s", cmp.Diff(tc.want, got, cmp.AllowUnexported(volume{}))) - } - } -} - -func TestXfsVolumes(t *testing.T) { - gotVolumes, err := xfsVolumes("testdata/disk_2_xfs_volumes.raw", []volume{{id: 1, start: 2048, length: 6144}, {id: 2, start: 8192, length: 4096}, {id: 3, start: 12288, length: 6144}}) - if err != nil { - t.Fatalf("unexpected error while running xfsVolumes(): %v", err) - } - - wantVolumes := []volume{ - {id: 1, start: 2048, length: 6144}, - {id: 3, start: 12288, length: 6144}, - } - if !cmp.Equal(wantVolumes, gotVolumes, cmp.AllowUnexported(volume{})) { - t.Errorf("xfsVolumes() unexpected diff (-want/+got):\n%s", cmp.Diff(wantVolumes, gotVolumes, cmp.AllowUnexported(volume{}))) - } -} - func TestExecute(t *testing.T) { bytes, err := execute("echo", "test").Output() if err != nil { @@ -215,23 +72,8 @@ func TestImageExport(t *testing.T) { } -var mmlsOut = `DOS Partition Table -Offset Sector: 0 -Units are in 512-byte sectors - - Slot Start End Length Description -000: Meta 0000000000 0000000000 0000000001 Primary Table (#0) -001: ------- 0000000000 0000002047 0000002048 Unallocated -002: 000:000 0000002048 0000008191 0000006144 Linux (0x83) -003: 000:001 0000008192 0000012287 0000004096 Linux (0x83) -004: 000:002 0000012288 0000018431 0000006144 Linux (0x83) -005: ------- 0000018432 0000020479 0000002048 Unallocated` - func fakeExecute(command string, args ...string) *exec.Cmd { var mockStdOut string - if command == "mmls" { - mockStdOut = mmlsOut - } cs := []string{"-test.run=TestHelperProcess", "--", command} cs = append(cs, args...)