Skip to content

Commit f858bea

Browse files
committed
parse content and calculate checksum from file inside archives
1 parent 02f13f7 commit f858bea

File tree

7 files changed

+151
-27
lines changed

7 files changed

+151
-27
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ options:
5353
findInNetworkDrives: true # enumerate network drive content
5454
findInCDRomDrives: true # enumerate physical CD-ROM and mounted iso / vhd...
5555
output:
56+
copyMatchingFiles: true # create a copy of every matching file
5657
base64Files: true # base64 matched content before copy
5758
filesCopyPath: '' # empty value will copy matched files in the fastfinder.exe folder
5859
```

configuration.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ type Options struct {
3535
}
3636

3737
type Output struct {
38-
Base64Files bool `yaml:"base64Files"`
39-
FilesCopyPath string `yaml:"filesCopyPath"`
38+
Base64Files bool `yaml:"base64Files"`
39+
FilesCopyPath string `yaml:"filesCopyPath"`
40+
CopyMatchingFiles bool `yaml:"copyMatchingFiles"`
4041
}
4142

4243
func (c *Configuration) getConfiguration(configFile string) *Configuration {

examples/example_configuration_linux.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ options:
1515
findInNetworkDrives: false
1616
findInCDRomDrives: false
1717
output:
18+
copyMatchingFiles: true
1819
base64Files: true
1920
filesCopyPath: ''

examples/example_configuration_windows.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ input:
1616
- 'A4AF9EF6E345B3B4EA50DDE672A986C14F9A195E407EBAC36B1652AACC10E3EE'
1717
options:
1818
contentMatchDependsOnPathMatch: false
19-
findInHardDrives: true
19+
findInHardDrives: false
2020
findInRemovableDrives: false
21-
findInNetworkDrives: false
21+
findInNetworkDrives: true
2222
findInCDRomDrives: false
2323
output:
24+
copyMatchingFiles: true
2425
base64Files: true
2526
filesCopyPath: ''
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# scan local Java software installations for known instances of vulnerable #log4j 1.x and 2.x versions
2+
# CVE-2019-17571, CVE-2021-44228
3+
input:
4+
path:
5+
- '*.jar'
6+
- '*.class'
7+
content:
8+
grep: []
9+
yara: []
10+
checksum:
11+
- '39a495034d37c7934b64a9aa686ea06b61df21aa222044cc50a47d6903ba1ca8' # log4j 2.0-rc1 JndiLookup.class
12+
- 'a03e538ed25eff6c4fe48aabc5514e5ee687542f29f2206256840e74ed59bcd2' # log4j 2.0-rc2 JndiLookup.class
13+
- '964fa0bf8c045097247fa0c973e0c167df08720409fd9e44546e0ceda3925f3e' # log4j 2.0.1 JndiLookup.class
14+
- '9626798cce6abd0f2ffef89f1a3d0092a60d34a837a02bbe571dbe00236a2c8c' # log4j 2.0.2 JndiLookup.class
15+
- 'fd6c63c11f7a6b52eff04be1de3477c9ddbbc925022f7216320e6db93f1b7d29' # log4j 2.0 JndiLookup.class
16+
- '03c77cca9aeff412f46eaf1c7425669e37008536dd52f1d6f088e80199e4aae7' # log4j 2.4-2.11.2 JndiManager$1.class
17+
- '1584b839cfceb33a372bb9e6f704dcea9701fa810a9ba1ad3961615a5b998c32' # log4j 2.7-2.8.1 JndiManager.class
18+
- '1fa92c00fa0b305b6bbe6e2ee4b012b588a906a20a05e135cbe64c9d77d676de' # log4j 2.12.0-2.12.1 JndiManager.class
19+
- '293d7e83d4197f0496855f40a7745cfcdd10026dc057dfc1816de57295be88a6' # log4j 2.9.0-2.11.2 JndiManager.class
20+
- '3bff6b3011112c0b5139a5c3aa5e698ab1531a2f130e86f9e4262dd6018916d7' # log4j 2.4-2.5 JndiManager.class
21+
- '547883afa0aa245321e6b1aaced24bc10d73d5af4974d951e2bd53b017e2d4ab' # log4j 2.14.0-2.14.1 JndiManager$JndiManagerFactory.class
22+
- '620a713d908ece7fb09b7d34c2b0461e1c366704da89ea20eb78b73116c77f23' # log4j 2.1-2.3 JndiManager$1.class
23+
- '632a69aef3bc5012f61093c3d9b92d6170fdc795711e9fed7f5388c36e3de03d' # log4j 2.8.2 JndiManager$JndiManagerFactory.class
24+
- '635ccd3aaa429f3fea31d84569a892b96a02c024c050460d360cc869bcf45840' # log4j 2.9.1-2.10.0 JndiManager$JndiManagerFactory.class
25+
- '6540d5695ddac8b0a343c2e91d58316cfdbfdc5b99c6f3f91bc381bc6f748246' # log4j 2.6-2.6.2 JndiManager.class
26+
- '764b06686dbe06e3d5f6d15891250ab04073a0d1c357d114b7365c70fa8a7407' # log4j 2.8.2 JndiManager.class
27+
- '77323460255818f4cbfe180141d6001bfb575b429e00a07cbceabd59adf334d6' # log4j 2.14.0-2.14.1 JndiManager.class
28+
- '8abaebc4d09926cd12b5269c781b64a7f5a57793c54dc1225976f02ba58343bf' # log4j 2.13.0-2.13.3 JndiManager$JndiManagerFactory.class
29+
- '914a64f23e2bcc1ae166af645a21f71f18ad6be8282001ec10b3e45b37064c99' # log4j 2.13.0-2.15.0 JndiManager$1.class
30+
- '91e58af100aface711700562b5002c5d397fb35d2a95d5704db41461ac1ad8fd' # log4j 2.1-2.3 JndiManager$JndiManagerFactory.class
31+
- 'ae950f9435c0ef3373d4030e7eff175ee11044e584b7f205b7a9804bbe795f9c' # log4j 2.1-2.3 JndiManager.class
32+
- 'aec7ea2daee4d6468db2df25597594957a06b945bcb778bbcd5acc46f17de665' # log4j 2.4-2.6.2 JndiManager$JndiManagerFactory.class
33+
- 'b8af4230b9fb6c79c5bf2e66a5de834bc0ebec4c462d6797258f5d87e356d64b' # log4j 2.7-2.8.1 JndiManager$JndiManagerFactory.class
34+
- 'c3e95da6542945c1a096b308bf65bbd7fcb96e3d201e5a2257d85d4dedc6a078' # log4j 2.13.0-2.13.3 JndiManager.class
35+
- 'e4906e06c4e7688b468524990d9bb6460d6ef31fe938e01561f3f93ab5ca25a6' # log4j 2.8.2-2.12.0 JndiManager$1.class
36+
- 'fe15a68ef8a75a3f9d3f5843f4b4a6db62d1145ef72937ed7d6d1bbcf8ec218f' # log4j 2.12.0-2.12.1 JndiManager$JndiManagerFactory.class
37+
- '6adb3617902180bdf9cbcfc08b5a11f3fac2b44ef1828131296ac41397435e3d' # log4j 1.2.4 SocketNode.class
38+
- '3ef93e9cb937295175b75182e42ba9a0aa94f9f8e295236c9eef914348efeef0' # log4j 1.2.6-1.2.9 SocketNode.class
39+
- 'bee4a5a70843a981e47207b476f1e705c21fc90cb70e95c3b40d04a2191f33e9' # log4j 1.2.8 SocketNode.class
40+
- '7b996623c05f1a25a57fb5b43c519c2ec02ec2e647c2b97b3407965af928c9a4' # log4j 1.2.15 SocketNode.class
41+
- '688a3dadfb1c0a08fb2a2885a356200eb74e7f0f26a197d358d74f2faf6e8f46' # log4j 1.2.16 SocketNode.class
42+
- '8ef0ebdfbf28ec14b2267e6004a8eea947b4411d3c30d228a7b48fae36431d74' # log4j 1.2.17 SocketNode.class
43+
- 'd778227b779f8f3a2850987e3cfe6020ca26c299037fdfa7e0ac8f81385963e6' # log4j 1.2.11 SocketNode.class
44+
- 'ed5d53deb29f737808521dd6284c2d7a873a59140e702295a80bd0f26988f53a' # log4j 1.2.5 SocketNode.class
45+
- 'f3b815a2b3c74851ff1b94e414c36f576fbcdf52b82b805b2e18322b3f5fc27c' # log4j 1.2.12 SocketNode.class
46+
- 'fbda3cfc5853ab4744b853398f2b3580505f5a7d67bfb200716ef6ae5be3c8b7' # log4j 1.2.13-1.2.14 SocketNode.class
47+
options:
48+
contentMatchDependsOnPathMatch: true
49+
findInHardDrives: true
50+
findInRemovableDrives: false
51+
findInNetworkDrives: false
52+
findInCDRomDrives: false
53+
output:
54+
copyMatchingFiles: false
55+
base64Files: false
56+
filesCopyPath: ''

finder.go

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"archive/zip"
45
"crypto/md5"
56
"crypto/sha1"
67
"crypto/sha256"
@@ -10,6 +11,7 @@ import (
1011
"strings"
1112

1213
"github.com/dlclark/regexp2"
14+
"github.com/h2non/filetype"
1315
)
1416

1517
// PathsFinder try to match regular expressions in file paths slice
@@ -29,45 +31,66 @@ func PathsFinder(files *[]string, patterns []*regexp2.Regexp) *[]string {
2931
}
3032

3133
// FindInFiles check for pattern or checksum match in files slice
32-
func FindInFiles(files *[]string, patterns []string, checksum []string) *[]string {
34+
func FindInFiles(files *[]string, patterns []string, hashList []string) *[]string {
3335
var matchingFiles []string
3436
InitProgressbar(int64(len(*files)))
35-
for _, f := range *files {
37+
for _, path := range *files {
3638
ProgressBarStep()
37-
b, err := ioutil.ReadFile(f)
39+
b, err := ioutil.ReadFile(path)
3840
if err != nil {
39-
LogMessage(LOG_ERROR, "[ERROR]", "Unable to read file", f)
41+
LogMessage(LOG_ERROR, "[ERROR]", "Unable to read file", path)
4042
continue
4143
}
4244

4345
// cancel analysis if file size is greater than 2Gb
4446
if len(b) > 1024*1024*2048 {
45-
LogMessage(LOG_ERROR, "[ERROR]", "File size is greater than 2Gb, skipping", f)
47+
LogMessage(LOG_ERROR, "[ERROR]", "File size is greater than 2Gb, skipping", path)
4648
continue
4749
}
4850

49-
// if checksum is not empty, calculate md5/sha1/sha256 for every file
50-
if len(checksum) > 0 {
51-
var hashs []string
52-
hashs = append(hashs, fmt.Sprintf("%x", md5.Sum(b)))
53-
hashs = append(hashs, fmt.Sprintf("%x", sha1.Sum(b)))
54-
hashs = append(hashs, fmt.Sprintf("%x", sha256.Sum256(b)))
55-
56-
for _, c := range hashs {
57-
if Contains(checksum, c) && !Contains(matchingFiles, f) {
58-
matchingFiles = append(matchingFiles, f)
59-
LogMessage(LOG_INFO, "[ALERT]", "File match on", f)
60-
}
51+
// get file type
52+
filetype, err := filetype.Match(b)
53+
if err != nil {
54+
LogMessage(LOG_ERROR, "[ERROR]", "Unable to get file type", path)
55+
}
56+
57+
for _, m := range CheckFileChecksumAndContent(path, b, hashList, patterns) {
58+
if !Contains(matchingFiles, m) {
59+
LogMessage(LOG_INFO, "[ALERT]", "File match on", path)
60+
matchingFiles = append(matchingFiles, m)
6161
}
6262
}
6363

64-
for _, expression := range patterns {
65-
if strings.Contains(string(b), expression) {
66-
if !Contains(matchingFiles, f) {
67-
matchingFiles = append(matchingFiles, f)
68-
LogMessage(LOG_INFO, "[ALERT]", "File match on", f)
64+
// if file type is an archive, extract and calculate checksum for every file inside
65+
if Contains([]string{"application/x-tar", "application/x-7z-compressed", "application/zip", "application/vnd.rar"}, filetype.MIME.Value) {
66+
zr, err := zip.OpenReader(path)
67+
if err != nil {
68+
fmt.Printf("cant't open archive file: %s: %v\n", path, err)
69+
continue
70+
}
71+
72+
for _, subFile := range zr.File {
73+
fr, err := subFile.Open()
74+
if err != nil {
75+
fmt.Printf("can't open archive file member for reading: %s (%s): %v\n", path, subFile.Name, err)
76+
continue
77+
}
78+
defer fr.Close()
79+
80+
body, err := ioutil.ReadAll(fr)
81+
if err != nil {
82+
LogMessage(LOG_ERROR, "[ERROR]", "Unable to read file archive member: %s (%s): %v\n", path, subFile.Name, err)
83+
continue
84+
}
85+
86+
for _, m := range CheckFileChecksumAndContent(path, body, hashList, patterns) {
87+
if !Contains(matchingFiles, m) {
88+
LogMessage(LOG_INFO, "[ALERT]", "File match on", path)
89+
matchingFiles = append(matchingFiles, m)
90+
}
6991
}
7092
}
93+
7194
}
7295

7396
// cleaning memory if file size is greater than 512Mb
@@ -78,3 +101,44 @@ func FindInFiles(files *[]string, patterns []string, checksum []string) *[]strin
78101

79102
return &matchingFiles
80103
}
104+
105+
// CheckFileChecksumAndContent check for pattern or checksum match in files slice
106+
func CheckFileChecksumAndContent(path string, content []byte, hashList []string, patterns []string) (matchingFiles []string) {
107+
// compare file checksum with hashList
108+
if len(hashList) > 0 {
109+
matchingFiles = append(matchingFiles, checkForChecksum(path, content, hashList)...)
110+
}
111+
112+
// compare file content with patterns
113+
if len(patterns) > 0 {
114+
matchingFiles = append(matchingFiles, checkForStringPattern(path, content, patterns)...)
115+
}
116+
117+
return matchingFiles
118+
}
119+
120+
// checkForChecksum calculate content checksum and check if it is in hashlist
121+
func checkForChecksum(path string, content []byte, hashList []string) (matchingFiles []string) {
122+
var hashs []string
123+
hashs = append(hashs, fmt.Sprintf("%x", md5.Sum(content)))
124+
hashs = append(hashs, fmt.Sprintf("%x", sha1.Sum(content)))
125+
hashs = append(hashs, fmt.Sprintf("%x", sha256.Sum256(content)))
126+
127+
for _, c := range hashs {
128+
if Contains(hashList, c) && !Contains(matchingFiles, path) {
129+
matchingFiles = append(matchingFiles, path)
130+
}
131+
}
132+
133+
return matchingFiles
134+
}
135+
136+
// checkForStringPattern check if file content matches any specified pattern
137+
func checkForStringPattern(path string, content []byte, patterns []string) (matchingFiles []string) {
138+
for _, expression := range patterns {
139+
if strings.Contains(string(content), expression) {
140+
matchingFiles = append(matchingFiles, path)
141+
}
142+
}
143+
return matchingFiles
144+
}

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ func main() {
248248
}
249249

250250
// copy matching files
251-
if len(*matchContent) > 0 {
251+
if len(*matchContent) > 0 && config.Output.CopyMatchingFiles {
252252
LogMessage(LOG_INFO, "[INFO]", "Copy all matching files")
253253
InitProgressbar(int64(len(*matchPattern)))
254254
for _, f := range *matchContent {

0 commit comments

Comments
 (0)