18

一次opencanary自定义实践

 4 years ago
source link: https://www.freebuf.com/sectool/249241.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

自定义修改模块

opencanary 是一个pyhton开发的开源的蜜罐系统。

github地址在: https://github.com/thinkst/opencanary 。完成了常用的蜜罐捕获请求。但是,固有的opencanary 存在如下的问题:

只监听一个IP

日志存储到本地,无法外发

iptables 产生的数据存储到固定文件,无法进一步处理。

部署节点状态无法统一管理

针对以上的问题,对opencanry 完成了相关的自定义化操作。接下来挨个详述各个问题的解决办法。

监听多个IP

通过网卡的trunk 技术在本机上配置相应的子接口,可以让一个服务器包含多个不同网段的IP。一个支持trunk的网卡配置如下:

VLAN=yes
TYPE=vlan
DEVICE=eth0.1001
PHYSDEV=eth0
VLAN_ID=1001
REORDED_HDR=0
BOOTPROTO=static
NAME=eth0.1001
ONBOOT=yes
IPADDR=10.211.1.10
NETMASK=255.255.255.0

这样,同一台服务器就监听了多个IP地址。

通过opencanaryd --copyconfig 生成的配置文件中,修改  device.listen_addr 对应的配置项为 0.0.0.0 既可以完成所有地址的监听。

自定义日志处理器,外发报警日志

默认的日志处理文件在  opencanry/logger.py 中定义, 默认使用的 PyLogger 类的实现,我们只需要修改 该类的 log 方法即可, 修改逻辑如下:

def post2server(self, serverip, jsondata):
        try:
                import urllib2
                url = 'http://'+serverip+'/log/'
                req  = urllib2.Request(url, jsondata, {'Content-Type':'application/json'})
                f = urllib2.urlopen(req)
                response = f.read()
                self.logger.warn(response)
                f.close()       
        except urllib2.URLError, e:
                self.logger.error(e)

    def log(self, logdata, retry=True):
        logdata = self.sanitizeLog(logdata)
        jsondata = json.dumps(logdata, sort_keys=True)
       serverip = "10.210.245.22" # 日志服务器的地址 
        if logdata['src_host']!='127.0.0.1' and logdata['dst_host']!='':
            import uuid
            scheduler = TwistedScheduler()
            scheduler.add_job(self.post2server, args=[serverip, jsondata], id=str(uuid.uuid1()))
            scheduler.start()
        elif logdata['src_host']!='127.0.0.1':
            self.logger.warn(jsondata)

iptables 日志二次处理

默认的 iptables 产生的日志,通过rsyslog 存储在 /var/log/kern.log 中,通过FileSystemWatcher 完成相应的处理。某些环境下,FileSystemWatcher 依赖于 系统的 fsnotify 模块 偶尔不能正常工作。

通过配置rsylsog 的 转发配置,转发到本地监听的syslog 服务,完成iptables 日志的二次处理。

配置如下:

50i kern.* @127.0.0.1:7788

然后,本地在 7788 端口启动一个 syslog 服务完成相应的处理即可。

canaryLogger = logging.getLogger()

LOG_HOST = "127.0.0.1"
LOG_PORT = 7788


class SyslogHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        addr = self.client_address
        data = self.request[0]
        self.handleLine(data)

    def handleLine(self, line):
        global canaryLogger
        self.logger = canaryLogger
        try:
            if 'canaryfw: ' in line:
                logtype = self.logger.LOG_PORT_SYN
                (rubbish, log) = line.split('canaryfw: ')
            elif "canarynmapNULL" in line:
                logtype = self.logger.LOG_PORT_NMAPNULL
                (rubbish, log) = line.split('canarynmapNULL: ')
            elif "canarynmapXMAS" in line:
                logtype = self.logger.LOG_PORT_NMAPXMAS
                (rubbish, log) = line.split('canarynmapXMAS: ')
            elif "canarynmapFIN" in line:
                logtype = self.logger.LOG_PORT_NMAPFIN
                (rubbish, log) = line.split('canarynmapFIN: ')
            elif 'canarynmap: ' in line:
                logtype = self.logger.LOG_PORT_NMAPOS
                (rubbish, log) = line.split('canarynmap: ')
        except ValueError:
            return
        tags = log.split(' ')
        kv = {}
        for tag in tags:
            if tag.find('=') >= 0:
                (key, val) = tag.split('=')
            else:
                key = tag
                val = ''
            kv[key] = val

        try:
            kv.pop('')
        except:
            pass

        data = {}
        data['src_host'] = kv.pop('SRC')
        data['src_port'] = kv.pop('SPT')
        data['dst_host'] = kv.pop('DST')
        data['dst_port'] = kv.pop('DPT')
        data['logtype'] = logtype
        data['logdata'] = kv

        canaryLogger.log(data)


class LogCollect:

    def start(self, logger):
        global canaryLogger
        canaryLogger = logger
        logging.basicConfig(
            level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
        try:
            logging.debug("slog start at %s : %d ", LOG_HOST, LOG_PORT)
            server = SocketServer.ThreadingUDPServer(
                (LOG_HOST, LOG_PORT), SyslogHandler)
            server.allow_reuse_address = True
            #server.request_queue_size = 60
            server.serve_forever(poll_interval=0.5)
        except Exception as ex:
            logging.debug("error start , exit .....")
        except KeyboardInterrupt:
            print ("Crtl+C Pressed. Shutting down.")

添加自定义模块收集服务器信息,发送到中心服务器

# coding=utf-8
# /usr/lib/python2.7/site-packages/opencanary/modules/host.py

from opencanary.modules import CanaryService
from twisted.internet import reactor

from datetime import datetime
from apscheduler.schedulers.twisted import TwistedScheduler
import psutil
import os
import json


class CanaryHost(CanaryService):
    NAME = "host"

    def __init__(self, config=None, logger=None):
        CanaryService.__init__(self, config, logger)

        self.hostname = config.getVal('device.node_id')
        self.localip = config.getVal('device.listen_addr')
        self.serverip = config.getVal('server.ip')
        self.last_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
        self.status = "online"
        self.logtype = logger.LOG_HOST

    def hoststatus(self):
        hostjson = json.dumps({
            "lasttime": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
            "hostname": self.hostname,
            "ip": self.localip,
            "status": self.status,
            "bindIp": self.bindIp()
        })
        try:
            import urllib2
            url = 'http://' + self.serverip + '/host/'
            req = urllib2.Request(
                url, hostjson, {'Content-Type': 'application/json'})
            f = urllib2.urlopen(req)
            response = f.read()
            f.close()
        except urllib2.URLError, e:
            e = {"Hoststatus urllib2 Error:": str(e)}
            self.logger.error(e)

    def bindIp(self):
        netcard_info = []
        info = psutil.net_if_addrs()
        for k, v in info.items():
            for item in v:
                if item[0] == 2 and item[1] != '127.0.0.1':
                    netcard_info.append({
                        "name": str(k),
                        "ip": str(item[1])
                    })

        return json.dumps(netcard_info)

    def startYourEngines(self):
        sched = TwistedScheduler()
        sched.start()
        if not sched.get_job('host_status'):
            sched.add_job(self.hoststatus, 'interval',
                          seconds=10, id='host_status')


CanaryServiceFactory = CanaryHost

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK