Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

transforms: add Yara support #326

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 10 additions & 8 deletions event/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,25 @@ func DestinationHardwareAddr(addr net.HardwareAddr) Option {
}
}

// Protocol returns an option for setting the protocol.
// This is redundant if SourceAddr is used.
func Protocol(p string) Option {
return func(m Event) {
m.Store("protocol", p)
}
}

// SourceAddr returns an option for setting the source-ip value.
func SourceAddr(addr net.Addr) Option {
return func(m Event) {
if ta, ok := addr.(*net.TCPAddr); ok {
m.Store("source-ip", ta.IP.String())
m.Store("source-port", ta.Port)
m.Store("protocol", "tcp")
} else if ua, ok := addr.(*net.UDPAddr); ok {
m.Store("source-ip", ua.IP.String())
m.Store("source-port", ua.Port)
m.Store("protocol", "udp")
}
}
}
Expand All @@ -195,7 +205,6 @@ func DestinationAddr(addr net.Addr) Option {
m.Store("destination-port", ta.Port)
} else if ua, ok := addr.(*net.UDPAddr); ok {
m.Store("destination-ip", ua.IP.String())
m.Store("destination-port", ua.Port)
}
}
}
Expand Down Expand Up @@ -263,13 +272,6 @@ func Service(v string) Option {
}
}

// Protocol sets the protocol of the event
func Protocol(v string) Option {
return func(m Event) {
m.Store("protocol", v)
}
}

// Message returns an option for setting the payload value.
// should this be just a formatter? eg Bla Bla {src-ip}
func Message(format string, a ...interface{}) Option {
Expand Down
10 changes: 0 additions & 10 deletions listener/canary/tcp_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ func (c *Canary) DecodeHTTP(conn net.Conn) error {
c.events.Send(event.New(
CanaryOptions,
EventCategoryHTTP,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Custom("http.method", request.Method),
Expand Down Expand Up @@ -114,7 +113,6 @@ func (c *Canary) DecodeElasticsearch(conn net.Conn) error {
c.events.Send(event.New(
CanaryOptions,
EventCategoryElasticsearch,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Custom("http.method", request.Method),
Expand Down Expand Up @@ -161,7 +159,6 @@ func (c *Canary) DecodeHTTPS(conn net.Conn) error {
options := []event.Option{
CanaryOptions,
EventCategoryHTTPS,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Payload(buff[:n]),
Expand Down Expand Up @@ -237,7 +234,6 @@ func (c *Canary) DecodeMSSQL(conn net.Conn) error {
c.events.Send(event.New(
CanaryOptions,
EventCategoryMSSQL,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Payload(buff[:n]),
Expand All @@ -262,7 +258,6 @@ func (c *Canary) DecodeTelnet(conn net.Conn) error {
c.events.Send(event.New(
CanaryOptions,
EventCategoryTelnet,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Payload(buff[:n]),
Expand All @@ -287,7 +282,6 @@ func (c *Canary) DecodeRedis(conn net.Conn) error {
c.events.Send(event.New(
CanaryOptions,
EventCategoryRedis,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Payload(buff[:n]),
Expand All @@ -312,7 +306,6 @@ func (c *Canary) DecodeRDP(conn net.Conn) error {
c.events.Send(event.New(
CanaryOptions,
EventCategoryRDP,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Payload(buff[:n]),
Expand All @@ -337,7 +330,6 @@ func (c *Canary) DecodeFTP(conn net.Conn) error {
c.events.Send(event.New(
CanaryOptions,
EventCategoryNBTIP,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Payload(buff[:n]),
Expand All @@ -362,7 +354,6 @@ func (c *Canary) DecodeNBTIP(conn net.Conn) error {
c.events.Send(event.New(
CanaryOptions,
EventCategoryNBTIP,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Payload(buff[:n]),
Expand All @@ -387,7 +378,6 @@ func (c *Canary) DecodeSMBIP(conn net.Conn) error {
options := []event.Option{
CanaryOptions,
EventCategorySMBIP,
event.Protocol("tcp"),
event.SourceAddr(conn.RemoteAddr()),
event.DestinationAddr(conn.LocalAddr()),
event.Payload(buff[:n]),
Expand Down
9 changes: 9 additions & 0 deletions plugins/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
This folder contains some plugin examples, each in its directory.

To use the plugins, compile them, then move the .so file to the data directory for your honeytrap install (for example, /home/peter/.honeytrap):

$ cd plugins/udp-ampl-detector
$ go build -buildmode=plugin udp-ampl-detector.go # For smaller plugin sizes, append "-ldflags '-s'"
$ cp udp-ampl-detector.so /home/peter/.honeytrap

To write plugins, write them under "package main" in a separate directory, and make sure to export the correct function(s) - Transform for transforms, Service for services, and so on. For more details, refer to the documentation.
69 changes: 69 additions & 0 deletions plugins/plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Honeytrap
* Copyright (C) 2016-2017 DutchSec (https://dutchsec.com/)
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* version 3 along with this program in the file "LICENSE". If not, see
* <http://www.gnu.org/licenses/agpl-3.0.txt>.
*
* See https://honeytrap.io/ for more details. All requests should be sent to
* licensing@honeytrap.io
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* Honeytrap" logo and retain the original copyright notice. If the display of the
* logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
* must display the words "Powered by Honeytrap" and retain the original copyright notice.
*/
package plugins

import (
"fmt"
"os"
"path"
"plugin"
)

func Get(name, symName, folder string) (sym interface{}, found bool, e error) {
filename := path.Join(folder, name+".so")
if _, err := os.Stat(filename); err != nil {
e = fmt.Errorf("Couldn't find dynamic plugin: %s", err.Error())
return
}
found = true
dynamicPl, err := plugin.Open(filename)
if err != nil {
e = fmt.Errorf("Couldn't load dynamic plugin: %s", err.Error())
return
}
sym, err = dynamicPl.Lookup(symName)
if err != nil {
e = fmt.Errorf("Couldn't lookup symbol \"%s\": %s", symName, err.Error())
return
}
return
}

func MustGet(name, symName, folder string) interface{} {
out, found, err := Get(name, symName, folder)
if !found {
panic(fmt.Errorf("Plugin %s not found", name))
}
if err != nil {
panic(err.Error())
}
return out
}
40 changes: 40 additions & 0 deletions plugins/udp-ampl-detector/udp-ampl-detector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
This is an example of a Yara-based plugin.

It matches a few known patterns for UDP amplification attacks.
*/

package main

import (
"github.com/honeytrap/honeytrap/event"
"github.com/honeytrap/honeytrap/transforms"
"github.com/honeytrap/honeytrap/transforms/yara"
"github.com/op/go-logging"
)

var log = logging.MustGetLogger("udp-ampl-detector")

func Transform() transforms.TransformFunc {
matcher, err := yara.NewMatcherFrom("/honeytrap/assets/yara-custom/amplification.yara")
if err != nil {
panic(err)
}
return func(state transforms.State, e event.Event, send func(event.Event)) {
matches, err := matcher.GetMatches(e)
if err != nil {
log.Error(err.Error())
return
}

// Send the original event, as well as an additional event for each match
send(e)
for _, match := range matches {
send(event.New(
event.Sensor("yara-custom"),
event.Category("amplification"),
event.Type(match.Rule),
))
}
}
}
50 changes: 45 additions & 5 deletions pushers/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@
package pushers

import (
"fmt"

"github.com/BurntSushi/toml"

"github.com/honeytrap/honeytrap/event"
"github.com/honeytrap/honeytrap/plugins"
)

// Channel defines a interface which exposes a single method for delivering
Expand Down Expand Up @@ -65,12 +69,48 @@ func WithConfig(c toml.Primitive) func(Channel) error {
}
}

func Get(key string) (ChannelFunc, bool) {
d := Dummy
// Gets a static or dynamic channel implementation, giving priority to static ones.
func Get(name, folder string) (ChannelFunc, error) {
staticCh, ok := channels[name]
if ok {
return staticCh, nil
}

// todo: add Lua support (issue #272)
sym, found, err := plugins.Get(name, "Channel", folder)
if !found {
return nil, fmt.Errorf("Channel %s not found", name)
}
if err != nil {
return nil, err
}
return sym.(ChannelFunc), nil
}

if fn, ok := channels[key]; ok {
return fn, true
func MustGet(name, folder string) ChannelFunc {
out, err := Get(name, folder)
if err != nil {
panic(err.Error())
}
return out
}

type tokenChannel struct {
Channel

Token string
}

return d, false
// Send delivers the slice of PushMessages and using the internal filters
// to filter out the desired messages allowed for all registered backends.
func (mc tokenChannel) Send(e event.Event) {
mc.Channel.Send(event.Apply(e, event.Token(mc.Token)))
}

// TokenChannel returns a Channel to set token value.
func TokenChannel(channel Channel, token string) Channel {
return tokenChannel{
Channel: channel,
Token: token,
}
}