Skip to content

Commit

Permalink
version v0.30.0
Browse files Browse the repository at this point in the history
  • Loading branch information
cuhsat committed Jun 14, 2024
1 parent 11636e1 commit e9556d5
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 12 deletions.
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# FACT
Forensic Artifacts Collecting Toolkit.
# Forensic Artifacts Collecting Toolkit

A basic shell pipeline for extracting forensic artifacts from disk images. Relevant artifacts will be processed and provided in [ECS](https://www.elastic.co/guide/en/ecs/current/index.html) format for ingestion with [Logstash](https://www.elastic.co/de/logstash).

```sh
# fmount image.dd | ffind | flog
# fmount image.dd | ffind | flog -D logstash
```

## Tools
Expand Down Expand Up @@ -99,14 +100,12 @@ Required system commands:

> Use `make tools` to install [Eric Zimmerman's Tools](https://ericzimmerman.github.io/#!index.md).
#### Roadmap
- [ ] [System Active Directory](https://forensics.wiki/active_directory/)
- [ ] [System Prefetch Files](https://forensics.wiki/prefetch/)
- [x] [System Event Logs](https://forensics.wiki/windows_event_log_%28evt%29/)
- [ ] [System AmCache](https://forensics.wiki/amcache/)
- [x] [User JumpLists](https://forensics.wiki/jump_lists/)
- [x] [User ShellBags](https://forensics.wiki/shell_item/)
- [ ] [User Browser Histories](https://forensics.wiki/google_chrome/)
Supported artifacts for Windows 7+ systems:

- [System Event Logs](https://forensics.wiki/windows_event_log_%28evt%29/)
- [User JumpLists](https://forensics.wiki/jump_lists/)
- [User ShellBags](https://forensics.wiki/shell_item/)
- [User Browser Histories](https://forensics.wiki/google_chrome/)

## License
Released under the [MIT License](LICENSE).
15 changes: 14 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,21 @@ module github.com/cuhsat/fact
go 1.22

require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mxk/go-vss v1.2.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.19.0 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.52.1 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.30.1 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)
29 changes: 29 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mxk/go-vss v1.2.0 h1:JpdOPc/P6B3XyRoddn0iMiG/ADBi3AuEsv8RlTb+JeE=
github.com/mxk/go-vss v1.2.0/go.mod h1:ZQ4yFxCG54vqPnCd+p2IxAe5jwZdz56wSjbwzBXiFd8=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.52.1 h1:uau0VoiT5hnR+SpoWekCKbLqm7v6dhRL3hI+NQhgN3M=
modernc.org/libc v1.52.1/go.mod h1:HR4nVzFDSDizP620zcMCgjb1/8xk2lg5p/8yjfGv1IQ=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk=
modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
67 changes: 67 additions & 0 deletions internal/flog/sqlite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SQLite functions.
package flog

import (
"database/sql"
"path/filepath"
"strings"

_ "modernc.org/sqlite"
)

type Url struct {
Title string
Url string
Time int64
}

func History(src string) (urls []Url, err error) {
var query string

switch strings.ToLower(filepath.Base(src)) {
case "history":
query = `
SELECT u.url, u.title, (t.visit_time-11644473600000000)
FROM urls AS u, visits AS t
WHERE u.id = t.url
;`
case "places.sqlite":
query = `
SELECT u.url, COALESCE(u.title, ''), t.visit_date
FROM moz_places AS u, moz_historyvisits AS t
WHERE u.id = t.place_id
;`
}

db, err := sql.Open("sqlite", src)

if err != nil {
return
}

defer db.Close()

rows, err := db.Query(query)

if err != nil {
return
}

defer rows.Close()

for rows.Next() {
u := Url{}

err = rows.Scan(&u.Url, &u.Title, &u.Time)

if err != nil {
return
}

urls = append(urls, u)
}

err = rows.Err()

return
}
Binary file modified internal/testdata/windows/user.zip
Binary file not shown.
51 changes: 51 additions & 0 deletions pkg/ecs/history.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// ECS history mapping functions.
package ecs

import (
"fmt"
"net"
"net/url"
"strconv"
"time"

"github.com/cuhsat/fact/internal/flog"
)

func MapHistory(fu *flog.Url, src string) (log *Log, err error) {
log = NewLog(fmt.Sprint(fu), src, &Base{
Timestamp: time.Unix(fu.Time/1000000, 0).UTC(),
Message: fu.Url,
Tags: "History",
Labels: map[string]interface{}{
"Title": fu.Title,
},
})

u, err := url.Parse(fu.Url)

if err != nil {
return log, nil
}

log.Url = &Url{
Original: fu.Url,
Full: u.String(),
Scheme: u.Scheme,
Domain: u.Host,
Path: u.Path,
Query: u.RawQuery,
Fragment: u.Fragment,
Username: u.User.Username(),
}

log.Url.Password, _ = u.User.Password()

_, p, err1 := net.SplitHostPort(u.Host)
port, err2 := strconv.ParseInt(p, 10, 64)

if err1 == nil || err2 == nil {
log.Url.Port = port
}

return
}
14 changes: 14 additions & 0 deletions pkg/ecs/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Log struct {
Agent *Agent `json:"agent"`
Event *Evt `json:"event"`
File *File `json:"file"`
Url *Url `json:"url,omitempty"`
Host *Host `json:"host,omitempty"`
User *User `json:"user,omitempty"`
Process *Process `json:"process,omitempty"`
Expand Down Expand Up @@ -66,6 +67,19 @@ type File struct {
Path string `json:"path,omitempty"`
}

type Url struct {
Original string `json:"original,omitempty"`
Full string `json:"full,omitempty"`
Scheme string `json:"scheme,omitempty"`
Domain string `json:"domain,omitempty"`
Port int64 `json:"port,omitempty"`
Path string `json:"path,omitempty"`
Query string `json:"query,omitempty"`
Fragment string `json:"fragment,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}

type Host struct {
Hostname string `json:"hostname,omitempty"`
MAC string `json:"mac,omitempty"`
Expand Down
41 changes: 41 additions & 0 deletions pkg/flog/flog.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ func Log(files []string, dir string, jp bool) error {
"usrclass.dat",
}

usrHistory := []string{
"history",
"places.sqlite",
}

g := new(errgroup.Group)

for _, f := range files {
Expand All @@ -38,6 +43,8 @@ func Log(files []string, dir string, jp bool) error {
fn = LogJumpList
} else if slices.Contains(usrHives, name) {
fn = LogShellBag
} else if slices.Contains(usrHistory, name) {
fn = LogHistory
} else {
continue
}
Expand Down Expand Up @@ -167,6 +174,40 @@ func LogShellBag(src, dir string, jp bool) (logs []string, err error) {
return logs, nil
}

func LogHistory(src, dir string, jp bool) (logs []string, err error) {
ll, err := flog.History(src)

if err != nil {
return
}

if err = os.MkdirAll(dir, sys.MODE_DIR); err != nil {
return
}

for _, l := range ll {
dst := filepath.Join(dir, fmt.Sprintf("%s.json", ecs.Hash(fmt.Sprint(l))))

m, err := ecs.MapHistory(&l, src)

if err != nil {
sys.Error(err)
continue
}

log, err := write(m, dst, jp)

if err != nil {
sys.Error(err)
continue
}

logs = append(logs, log)
}

return logs, nil
}

func StripHash(in []string) (out []string) {
if len(in) == 0 {
return in
Expand Down
49 changes: 49 additions & 0 deletions pkg/flog/flog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,55 @@ func TestLogShellbag(t *testing.T) {
}
}

func TestLogHistory(t *testing.T) {
cases := []struct {
name, data, file string
}{
{
name: "Test log history",
data: test.Testdata("windows", "user.zip"),
file: "History",
},
{
name: "Test log places.sqlite",
data: test.Testdata("windows", "user.zip"),
file: "places.sqlite",
},
}

for _, tt := range cases {
tmp, _ := os.MkdirTemp(os.TempDir(), "log")

err := zip.Unzip(tt.data, tmp)

if err != nil {
t.Fatal(err)
}

t.Run(tt.name, func(t *testing.T) {
l, err := LogHistory(filepath.Join(tmp, tt.file), tmp, true)

if err != nil {
t.Fatal(err)
}

if len(l) == 0 {
t.Fatal("file count zero")
}

b, err := os.ReadFile(l[0])

if err != nil {
t.Fatal(err)
}

if !json.Valid(b) {
t.Fatal("invalid json")
}
})
}
}

func TestStripHash(t *testing.T) {
cases := []struct {
name, file, hash string
Expand Down

0 comments on commit e9556d5

Please sign in to comment.