Skip to content

Commit bc80854

Browse files
committed
v1.6.0
1 parent 3c42e7e commit bc80854

21 files changed

+1511
-549
lines changed

README.md

+44-27
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,16 @@ which matched the event.
104104

105105
## WHIDS
106106
1. Download and extract the latest WHIDS release https://github.com/0xrawsec/whids/releases
107-
2. Run `install.bat` as **administrator**
108-
3. Verify that files have been created at the **installation directory**
109-
4. With a text editor **opened as administrator** (to prevent changing rights of WHIDs installation directory) open `config.json` and modify it as you wish
110-
5. Skip this if running with a connection to a manager. If there is nothing in the **rules directory** the tool will be useless, so make sure there are some **gene** rules in there. You can get some compiled rules [here](https://raw.githubusercontent.com/0xrawsec/gene-rules/master/compiled.gen)
111-
6. Start the HIDS with `Start.bat` script located in **installation directory**
112-
7. If you configured a **manager** do not forget to run it
107+
2. If you want WHIDS to run along with Sysmon (**strongly recommended**), install it first. An optimal **configuration file** is shipped with the release so that you can take the most out of WHIDS. At installation the **Sysmon service** will be made *dependant* of **WHIDS service** so that we are sure the IDS runs before **Sysmon** starts generating some events.
108+
3. Run `manage.bat` as **administrator**
109+
4. Launch installation by selecting the appropriate option
110+
5. Verify that files have been created at the **installation directory**
111+
6. With a text editor **opened as administrator** (to prevent changing the rights of the WHIDS installation directory) open `config.json` and modify it as you wish. This can also be done from `manage.bat`
112+
7. Skip this if running with a connection to a manager, because rules will be updated automatically. If there is nothing in the **rules directory** the tool will be useless, so make sure there are some **gene** rules in there. Some rules are packaged with WHIDS and you will be prompted if you want to install those or not. If you want the last up to date rules, you can get those [here](https://raw.githubusercontent.com/0xrawsec/gene-rules/master/compiled.gen) (take the **compiled** ones)
113+
8. Start the **services** from appropriate option in `manage.bat` or just reboot (**preferred option** otherwise some enrichment fields will be incomplete leading to false alerts)
114+
9. If you configured a **manager** do not forget to run it in order to receive alerts and dumps
113115

114-
**NB:** whenever you go to the installation directory with **File Explorer** and if you are **Administrator** the explorer will ask you if you want to change the permission of the directory. **DO NOT CLICK YES**, otherwise it will break the folder permissions put in place at installation time. Always access installation directory from **applications started as Administrator**.
116+
**NB:** whenever you go to the installation directory with **Explorer.exe** and if you are **Administrator** the explorer will ask you if you want to change the permission of the directory. **DO NOT CLICK YES**, otherwise it will break the folder permissions put in place at installation time. Always access installation directory from **applications started as Administrator** (i.e. text editor).
115117

116118
# Configuration
117119

@@ -130,7 +132,7 @@ WHIDS configuration file example
130132
"containers-db": "C:\\Program Files\\Whids\\Database\\Containers",
131133
// Forwarder related configuration
132134
"forwarder": {
133-
"client": {
135+
"manager-client": {
134136
// Hostname or IP address of remote manager
135137
"host": "",
136138
// Port used by remote manager
@@ -146,8 +148,13 @@ WHIDS configuration file example
146148
// Maximum upload side for dump forwarding
147149
"max-upload-size": 104857600
148150
},
149-
// Path where to store the alerts
150-
"logs-dir": "C:\\Program Files\\Whids\\Logs\\Alerts",
151+
// Alert logging settings
152+
"logging": {
153+
// Path where to store the alerts
154+
"dir": "C:\\Program Files\\Whids\\Logs\\Alerts",
155+
// Rotation interval
156+
"rotation-interval": "24h"
157+
},
151158
// If local=true the forwarder won't communicate with manager
152159
"local": true
153160
},
@@ -157,26 +164,33 @@ WHIDS configuration file example
157164
"channels": [
158165
"all"
159166
],
160-
// Dump mode: file, memory or all (can be empty)
161-
// file: dumps anything identified as a file in the event
162-
// memory: dumps (guilty) process memory in Windows minidump format
163-
"dump-mode": "file",
164-
// Dumps when criticality of the events is above or equal to treshold
165-
"dump-treshold": 8,
166-
// Where to store dumps
167-
"dump-dir": "C:\\Program Files\\Whids\\Dumps",
168-
// Whether or not to enable dump compression
169-
"dump-compression": true,
167+
// Dump related settings
168+
"dump": {
169+
// Dump mode: file, memory or all (can be empty)
170+
// file: dumps anything identified as a file in the event
171+
// memory: dumps (guilty) process memory in Windows minidump format
172+
// registry: dumps registry in case alert concerns a registry
173+
"mode": "file|registry",
174+
// Dumps when criticality of the events is above or equal to treshold
175+
"treshold": 8,
176+
// Where to store dumps
177+
"dir": "C:\\Program Files\\Whids\\Dumps",
178+
// Whether or not to enable dump compression
179+
"compression": true
180+
},
170181
// Log events with criticality above or equal to treshold
171182
"criticality-treshold": 5,
172183
// Sleep time in seconds between two rules updates (negative number means no update)
173184
"update-interval": 60,
174-
// Whether on not hooks should be enables
185+
// Whether on not hooks should be enabled
175186
"en-hooks": true,
176-
// Whether or not DNS-Client logging should be enabled
177-
"en-dns": true,
178187
// Logfile used to store WHIDS stderr
179-
"logfile": "C:\\Program Files\\Whids\\Logs\\whids.log"
188+
"logfile": "C:\\Program Files\\Whids\\Logs\\whids.log",
189+
// Log all the events passing through the engine (usefull for event dumping or debugging)
190+
"log-all": false,
191+
// Tells WHIDS that it is running on an endpoint. If false any kind
192+
// of dump is disabled to avoid dump things if installed on a WEC
193+
"endpoint": true
180194
}
181195
```
182196

@@ -191,8 +205,7 @@ Manager configuration example
191205
// Port used by the manager
192206
"port": 1519,
193207
// Logfile (automatically rotated) where to store alerts received
194-
// the logs are GZIP compressed
195-
"logfile": "alerts.gz",
208+
"logfile": "alerts.log",
196209
// Server authentication key (see server-key in WHIDS config)
197210
"key": "someserverkey",
198211
// List of authorized client keys (see key in WHIDS config)
@@ -222,7 +235,7 @@ Manager configuration example
222235

223236
# Documentation
224237

225-
To know how to write rules for the engine please visit: https://rawsec.lu/doc/gene/1.4/
238+
To know how to write rules for the engine please visit: https://rawsec.lu/doc/gene/1.6/
226239

227240
# Known Issues
228241

@@ -237,6 +250,10 @@ In order to get the most of WHIDS you need to activate specific features
237250

238251
# Changelog
239252

253+
## v1.6
254+
255+
TBD
256+
240257
## v1.5
241258
* Bunch of code rewritten to make things more consistent:
242259
* WHIDS is no longer command line based, most of the options are configured via a configuration file

collector/forwarder.go

+53-35
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ type LoggingConfig struct {
4747
// from configuration structure.
4848
func (l *LoggingConfig) ParseRotationInterval() (d time.Duration, err error) {
4949
d, err = time.ParseDuration(l.RotationInterval)
50-
if d < MinRotationInterval {
50+
/*if d < MinRotationInterval {
5151
d = MinRotationInterval
52-
}
52+
}*/
5353
return
5454
}
5555

@@ -168,13 +168,13 @@ func (f *Forwarder) Save() error {
168168
if f.logfile == nil {
169169
// This will reopen the first available alerts.gz.X file if several
170170
//lf := filepath.Join(f.fwdConfig.LogConf.Dir, "alerts.gz")
171-
lf := filepath.Join(f.fwdConfig.Logging.Dir, "alerts")
171+
lf := filepath.Join(f.fwdConfig.Logging.Dir, "alerts.log")
172172
ri, err := f.fwdConfig.Logging.ParseRotationInterval()
173173
if err != nil {
174174
return err
175175
}
176176
log.Infof("Rotating logfile every %s", ri)
177-
if f.logfile, err = logfile.OpenTimeRotateLogFile(lf, DefaultLogPerm, ri, time.Second*5); err != nil {
177+
if f.logfile, err = logfile.OpenTimeRotateLogFile(lf, DefaultLogPerm, ri); err != nil {
178178
return err
179179
}
180180
}
@@ -233,6 +233,20 @@ func (f *Forwarder) DiskSpaceQueue() int64 {
233233
return dp
234234
}
235235

236+
// Here we rely on the fact that the layout of the directory is known
237+
// and should be alert.log, alert.log.1, alert.log.2.gz, alert.log.3.gz ...
238+
// if we append in reverse order, older files appears first in the list
239+
func (f *Forwarder) listLogfiles() (files []string) {
240+
files = make([]string, 0)
241+
for wi := range fswalker.Walk(f.fwdConfig.Logging.Dir) {
242+
for _, fi := range wi.Files {
243+
fp := filepath.Join(f.fwdConfig.Logging.Dir, fi.Name())
244+
files = append([]string{fp}, files...)
245+
}
246+
}
247+
return
248+
}
249+
236250
// ProcessQueue processes the events queued
237251
// Todo: needs update with client
238252
func (f *Forwarder) ProcessQueue() {
@@ -258,43 +272,47 @@ func (f *Forwarder) ProcessQueue() {
258272

259273
// Reset logfile for latter Save function use
260274
f.logfile = nil
261-
for wi := range fswalker.Walk(f.fwdConfig.Logging.Dir) {
262-
for _, fi := range wi.Files {
263-
// fullpath
264-
fp := filepath.Join(f.fwdConfig.Logging.Dir, fi.Name())
265-
log.Debug("Processing queued file: %s", fp)
266-
fd, err := os.Open(fp)
275+
//for wi := range fswalker.Walk(f.fwdConfig.Logging.Dir) {
276+
//for _, fi := range wi.Files {
277+
for _, fp := range f.listLogfiles() {
278+
// fullpath
279+
//fp := filepath.Join(f.fwdConfig.Logging.Dir, fi.Name())
280+
//log.Debug("Processing queued file: %s", fp)
281+
log.Infof("Processing queued file: %s", fp)
282+
fd, err := os.Open(fp)
283+
if err != nil {
284+
log.Errorf("Failed to open queued file (%s): %s", fp, err)
285+
continue
286+
}
287+
switch {
288+
case strings.HasSuffix(fp, ".gz"):
289+
// the file is gzip so we have to pass a gzip reader to prepCollectReq
290+
gzr, err := gzip.NewReader(fd)
267291
if err != nil {
268-
log.Errorf("Failed to open queued file (%s): %s", fp, err)
269-
continue
270-
}
271-
272-
if strings.HasSuffix(fp, ".gz") {
273-
// the file is gzip so we have to pass a gzip reader to prepCollectReq
274-
gzr, err := gzip.NewReader(fd)
275-
if err != nil {
276-
log.Errorf("Failed to create gzip reader for queued file (%s): %s", fp, err)
277-
// close file
278-
fd.Close()
279-
continue
280-
}
281-
err = f.Client.PostLogs(gzr)
282-
// we can close the reader since we don't need those anymore
283-
gzr.Close()
292+
log.Errorf("Failed to create gzip reader for queued file (%s): %s", fp, err)
293+
// close file
284294
fd.Close()
285-
}
286-
287-
// We do not remove the logs if we failed to send
288-
if err != nil {
289-
log.Errorf("%s", err)
290295
continue
291296
}
297+
err = f.Client.PostLogs(gzr)
298+
// we can close the reader since we don't need those anymore
299+
gzr.Close()
300+
fd.Close()
301+
case strings.HasSuffix(fp, ".log.1"), strings.HasSuffix(fp, ".log"):
302+
err = f.Client.PostLogs(fd)
303+
}
292304

293-
// everything went fine, then we can delete the queued file
294-
if err = os.Remove(fp); err != nil {
295-
log.Errorf("Failed to delete queued file (%s): %s", fp, err)
296-
}
305+
// We do not remove the logs if we failed to send
306+
if err != nil {
307+
log.Errorf("%s", err)
308+
continue
309+
}
310+
311+
// everything went fine, then we can delete the queued file
312+
if err = os.Remove(fp); err != nil {
313+
log.Errorf("Failed to delete queued file (%s): %s", fp, err)
297314
}
315+
//}
298316
}
299317
}
300318

collector/manager.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ type Manager struct {
181181
tls TLSConfig
182182
srv *http.Server
183183
stop chan bool
184+
done bool
184185
// Gene related members
185186
geneEng *engine.Engine
186187
rules string // to cache the rules concatenated
@@ -197,7 +198,9 @@ func NewManager(c *ManagerConfig) (*Manager, error) {
197198
}
198199

199200
m := Manager{Host: c.Host, Port: fmt.Sprintf("%d", c.Port)}
200-
m.logfile, err = logfile.OpenTimeRotateLogFile(c.Logfile, DefaultLogPerm, time.Hour, time.Second*5)
201+
if m.logfile, err = logfile.OpenTimeRotateLogFile(c.Logfile, DefaultLogPerm, time.Hour); err != nil {
202+
return &m, err
203+
}
201204
m.key = c.Key
202205
m.authorized = datastructs.NewInitSyncedSet(datastructs.ToInterfaceSlice(c.Authorized)...)
203206
m.stop = make(chan bool)
@@ -293,9 +296,17 @@ func (m *Manager) Wait() {
293296
<-m.stop
294297
}
295298

299+
func (m *Manager) IsDone() bool {
300+
return m.done
301+
}
302+
296303
// Shutdown the Manager
297304
func (m *Manager) Shutdown() error {
298305
defer func() { go func() { m.stop <- true }() }()
306+
if m.done {
307+
return nil
308+
}
309+
m.done = true
299310
if m.srv != nil {
300311
m.srv.Shutdown(nil)
301312
}

collector/test/client_test.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ func TestClientGetRules(t *testing.T) {
2929
}
3030
r.AddAuthKey(key)
3131
r.Run()
32-
defer r.Shutdown()
3332

3433
cconf.Key = key
3534
c, err := collector.NewManagerClient(&cconf)
@@ -45,12 +44,12 @@ func TestClientGetRules(t *testing.T) {
4544
if err != nil {
4645
t.Errorf("%s", err)
4746
}
48-
4947
if sha256 != data.Sha256([]byte(rules)) {
5048
t.Errorf("Rules integrity cannot be verified")
5149
}
5250

53-
t.Log(rules)
51+
r.Shutdown()
52+
5453
}
5554

5655
func TestClientPostDump(t *testing.T) {

0 commit comments

Comments
 (0)