Snort 安装及使用

安装

在 Arch 上,大概是由于没有及时更新,目前官方库中的 Snort 已经过时,无法拉取源码,会报404
因此需要去官网拉取最新的源码手动编译

wget https://www.snort.org/downloads/snort/daq-2.0.7.tar.gz
tar xvzf daq-2.0.7.tar.gz
cd daq-2.0.7
./configure && make && sudo make install
cd ..

wget https://www.snort.org/downloads/snort/snort-2.9.16.tar.gz
tar xvzf snort-2.9.16.tar.gz
cd snort-2.9.16
./configure --enable-sourcefire && make && sudo make install
cd ..

在这里,daq本身还需要依赖flexbisonrpc-compat,好在这两个可以直接使用包管理安装

DAQ

DAQ(Data Acquisition) 是数据采集模块,使用--daq-list可以看到已安装到 Snort 的数据采集模块

$ snort --daq-list
Available DAQ modules:
pcap(v3): readback live multi unpriv
nfq(v7): live inline multi
ipfw(v3): live inline multi unpriv
dump(v3): readback live inline multi unpriv
afpacket(v5): live inline multi unpriv

默认情况下,Snort 使用的是 pcap。

联动防火墙

Snort 本身只是一个入侵检测软件,但可以使用 inline 模式来实现丢弃警告的包。但 inline 模式对设备网卡有所要求,另一个可行的思路是使用程序监控 Snort 日志,并自动插入到防火墙(不局限于 iptables,也可以使用 PowerShell 插入到 Windows 防火墙)

比较常见的思路是使用 Guardian 来实现 Snort 和 iptables 的联动1,但不幸的是 Guardian 已经年久失修,需要修改代码或安装libperl4-corelibs-perl才能运行。

当然,更省事的办法是自己来实现类似的程序。
大概思路是监听日志文件的改动,发现改动后使用正则表达式提取出对端 IP,使用 Shell 命令将其插入至 iptables2 3 中。

下面是一个可行的 Go 程序(需要sudo运行),如果加以修改,也可用于插入至 Windows 防火墙

package main

import (
    "bytes"
    "fmt"
    "log"
    "os"
    "os/exec"
    "regexp"
    "strings"

    "github.com/fsnotify/fsnotify"
)

const filename = "/home/ohyee/projects/network_security/snort/log/alert"

var f *os.File

// var reader *bufio.Reader
var regex *regexp.Regexp
var iptables string
var buffer = make([]byte, 1024)
var offset int64 = 0

type Alert struct {
    srcip    string
    srcport  string
    dstip    string
    dstport  string
    protocol string
}

func alert() {
    for {
        buf := bytes.NewBuffer([]byte{})
        for {
            length, err := f.ReadAt(buffer, offset)
            fmt.Println(offset, err)
            if length == 0 {
                    break
            }
            offset += int64(length)
            buf.Write(buffer)
        }
        if buf.Len() == 0 {
            break
        }

        line := buf.String()
        fmt.Println(line)

        res := regex.FindAllStringSubmatch(line, -1)
        for _, item := range res {
            alerts := Alert{
                srcip:    item[1],
                srcport:  item[2],
                dstip:    item[3],
                dstport:  item[4],
                protocol: item[5],
            }

            fmt.Printf("%+v", alerts)
            ban(alerts)
        }
    }

}

func ban(a Alert) {
    args := []string{"-I", "INPUT", "-s", a.srcip, "-d", a.dstip, "-p", strings.ToLower(a.protocol), "-j", "REJECT"}
    if a.dstport != "" {
        args = append(args, "--dport", a.dstport[1:])
    }
    cmd := exec.Command(iptables, args...)

    if b, err := cmd.CombinedOutput(); err != nil {
        fmt.Printf("Error %s %+v\n", b, err)
    } else {
        fmt.Printf("Ban %s -> %s (%s)\n", a.srcip, a.dstip, a.protocol)
    }
}

func main() {
    var err error
    if f, err = os.Open(filename); err != nil {
        return
    }
    defer f.Close()

    if regex, err = regexp.Compile("([\\d.]+)([:\\d]*) -> ([\\d.]+)([:\\d]*)[\\n\\s]*([a-zA-Z]+)"); err != nil {
        panic(err)
    }

    if iptables, err = exec.LookPath("iptables"); err != nil {
        panic(err)
    }

    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer watcher.Close()

    alert()

    done := make(chan bool)
    go func() {
        for {
            select {
            case event := <-watcher.Events:
                if event.Op&fsnotify.Write == fsnotify.Write {
                    fmt.Println("modified file:", event.Name)
                    alert()
                }
            case err := <-watcher.Errors:
                fmt.Println("error:", err)
            }
        }
    }()

    err = watcher.Add(filename)
    if err != nil {
        log.Fatal(err)
    }

    <-done
}

参考资料


  1. Snort联动式入侵防御系统的折腾之路(1):Snort&Guardian安装与部署 ↩︎

  2. iptables使用笔记 ↩︎

  3. iptables 使用方式整理 ↩︎