-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
146 lines (125 loc) · 3.57 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package main
import (
"flag"
"fmt"
"os"
"regexp"
"strings"
)
type detection struct {
ip string
timestamp string
words []string
}
var detections map[string]detection
var debugMode *bool
var configFile *string
func main() {
fmt.Println("Running reportAbuse")
debugMode = flag.Bool("debug", false, "if available we are debugging")
configFile = flag.String("config", "", "path to config json file")
flag.Parse()
if *debugMode {
fmt.Println("DEBUG MODE. Will not send any emails to the hosters!")
}
loadConfig() // load and check configuration
loadPatterns() // load detection patterns
initDatabase() // init the database
detections = make(map[string]detection)
for _, logFile := range cfg.Logfiles {
fmt.Println("Examine file " + logFile)
examineFile(logFile)
}
closeDatabase()
}
func examineFile(logFile string) {
// read the file
content, err := os.ReadFile(logFile)
if err != nil {
fmt.Print(err)
return
}
fmt.Printf("- File size: %v bytes\n", len(content))
timeZoneOffset := " " + getTimezoneOffset()
// find all entries that match the "bad" words
re := regexp.MustCompile(cfg.RegEx)
matches := re.FindAllStringSubmatch(string(content[:]), -1)
for _, match := range matches {
tim := match[cfg.RegGroupDate]
ip := match[cfg.RegGroupIP]
page := match[cfg.RegGroupPage]
if cfg.Mode == "direct" || cfg.Mode == "" {
// take all entries found by regex
if entry, ok := detections[ip]; ok {
// add detected attack
entry.words = append(entry.words, page)
detections[ip] = entry
} else {
// create new entry
var nEntry detection
nEntry.timestamp = tim + timeZoneOffset
nEntry.words = append(entry.words, page)
nEntry.ip = ip
detections[ip] = nEntry
}
}
if cfg.Mode == "page" {
// find all entries that match the "bad" words
for _, word := range patterns {
if strings.Contains(page, "/"+word) {
if entry, ok := detections[ip]; ok {
// add detected word
entry.words = append(entry.words, page)
detections[ip] = entry
} else {
// create new entry
var nEntry detection
nEntry.timestamp = tim + timeZoneOffset
nEntry.words = append(nEntry.words, page)
nEntry.ip = ip
detections[ip] = nEntry
}
break
}
}
}
}
// thin the processing list using minAttacks config value
for key, detection := range detections {
if len(detection.words) < cfg.MinAttacks {
if *debugMode {
fmt.Printf("- NOTE: Skip detected attack from IP %v because of not enough attacks (minAttacks).\n", key)
}
delete(detections, key)
}
}
// Check against database to avoid multiple reports for the same attacker IP
knownCount := 0
for key, detection := range detections {
val, _ := ipDB.Get([]byte(key))
if len(val) == 0 {
// ip not yet found in database
fmt.Println("- Found new attacker from", key)
hosterMail := getHosterMail(key)
if !*debugMode {
err = ipDB.Put([]byte(key), []byte(hosterMail))
if err != nil {
panic("Failed to add entry to database!")
}
} else {
fmt.Printf("- NOTE: Do not remember IP %v because of debug mode.\n", key)
}
if hosterMail == "" {
fmt.Println("- Cannot send email because I can't find abuse email")
continue // with next entry
}
fmt.Println("- Sending email to", hosterMail, " Attack timestamp", detection.timestamp)
notifyHoster(detection, hosterMail)
} else {
// ip found, remove from processing list
knownCount++
delete(detections, key)
}
}
fmt.Printf("Detected %v new attacks and %v already known attackers\n", len(detections), knownCount)
}