47

基于开源蜜罐的实践与功能扩展

 4 years ago
source link: https://www.tuicool.com/articles/fQvUnaV
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

0×00 前言

具有一定规模的公司都会有自己的机房,当网络规模和硬件系统到达一定程度,就需要跟进各种安全预警防护手段,而蜜罐系统就是一种常见的防护手段之一,蜜罐主要是通过在网络环境当中,用虚拟各种真实服务的守护进程,去模拟各种常见的应用服务,比如http、mysql、FTP服务等。

一般情况下,蜜罐系统是模拟出来的服务,不具备提供真实服务的能力,比如有蜜罐监听的ftp端口21,但21端口其实只是一个空的监听,不能提供FTP服务,并且己方人员,知道蜜罐是一个报警装置,不会去刻意访问,只有不明身份的攻击者才有可能去访问蜜罐进行漏洞尝试探测。

如果有人对蜜罐进行了大量的针对性操作,并且访问操作的Payload属于攻击数据,我们可以蜜罐的报警日志,判断这个可能是一个可以的攻击行为,或是误碰蜜罐行为,虽然服务不是真实的服务,但攻击的的渗透Payload数据都是真实的。

MviYFf2.jpg!web

一般可以在网络环境中,通过交换机的端口聚合技术,在一台机器上创建多个网段的IP,将流量引入集中到一台机器上,然后在这台机器上部署各个IP的监听,理想的状态下是可以提供这种机制的,如果不行也可以通过树莓派等手段,创建蜜罐。现在有很多种的蜜罐系统可以提供给安全运维人员使用,进行蜜罐系统部署,在各色的开源蜜罐系统中,我们选了相对比较有代表性的蜜罐系统给大家介绍,一般会根据蜜罐的:难易性、可用性、展示性、交互性等方面考察蜜罐系统的特点,从另一个角度划分,可分成纯命令行和UI画面支持的。

a):纯命令行式的操作方式,比如Opencanary这个蜜罐系统。
b):有Dashboard界面的可视化蜜罐系统,比如OpenCanary,Hfish等,这次主要介绍的是Opecanary。
OpenCanary蜜罐系统

OpenCanary是一种开源蜜罐系统,在黑帽在2015开源的,实现语言采用Python实现,因为是开源的代码,我们可基于这一套代码,进行扩展和改写, 也因为Python的友好性,可以相对快速的实现我们想要的新功能。Opencanary的代码量真的不是很大,代码结构清晰,扩展简单,设计的不复杂。

OpenCanay是一种基于命令行式的蜜罐服务系统,没有给出过多的UI操作界面,基本操作都是通过命令行,然后通过改写配置文件,完成对蜜罐的各种配置。通过配置文件的方式对专家啊来说,不是一问题,但操作友好性上还是一般,但是可以达到正配置目的。

Opencanary的基本实现原理也是通过设置各种监听的端口,模拟各种流行的服务,而底层实现端口监听,依赖于twisted的python库实现。

0×01 安装Python

opencanary是基于Python的运行环境的,提到运行环境,我们往往就面临着一个不同操作系统Python环境不同的问题,为了避免各种平台差异造成的依赖包困扰,我们选择虚拟的Python环境virtualenv来安装Opencananry,这样所需环境都在虚拟的环境下安装。同时又在默认的系统环境下,通过pip安装部署,大多数情况下,是不会出现问题。

部署Opencanary之前请确保您的机器上有大于等Python2.7版本的Python语言环境系统,至少可以用于运行Python语言,之后基于这个创建我们需要的Opencanary运行环境。

0×02 部署依赖

使用virtualenv保证Python环境下依赖包互隔离不影响,还是要强调一下,不用这个也可以正常装,这样可以直接在系统默认的库下改源码。

相关的依赖库,在Python安装之后,都是通过pip来安装。

2.1 安装PIP

python get-pip.py

在安装PIP的前,还要需要安装Python的开发安装包:python-deve,如果不安装python开发包,会提示找不到python.h这个文件,pip是没有办法正常安装。

2.2 安装VirtualEnv

安装virtualenv之后,我们可以将之后使用的各种依赖包,都装到虚拟环境中,而不装到python默认的库文件中,一般用root用户安装的virtualenv生成文件都在/root/.virtualenvs/中。而系统默认安装的库文件的位置是在,/usr/lib/python2.7/site-packages/中,这样环境之间的依赖包不互相影响。

细分的这么清楚,是因为后续我们要修改opencanary源码,不同的部署方法,源代码的位置是不一样的。opencanary是在python2.x下运行的,在官网github上可以看到更具体说明,而一些特别的需要关注点的,在这里给出一些重点的说明。

sudo pip install virtualenvsudo pip install virtualenvwrapper --upgrade --ignore-installed

有了virtualenv,我们可以在一台机器上,装多个python的环境。但实际我们装个python2.7版本的环境就基本满足目前的需求。

2.3 创建Python虚拟环境

编辑.bash_profile文件内容。

source /usr/local/bin/virtualenvwrapper.sh

virtualevnwrapper.sh的所在位置,取决于你安装时指定的安装位置,有可能在/usr/local/bin下,也可能在/usr/bin下,当初指定的安装目录位置,只有运行了这个 shell脚本,后续执行在shell中,才能找到mkvirtualenv执行文件。

我们创建了一个叫做py27的虚拟环境。

mkvirtualenv py27 -p /usr/bin/python

我们切换到py27这个Python环境下。

workon py27

2.4 安装Opencananry

pip在安装opencanary时,会自动安装他所需求要的各种依赖,一般不出问题的话,一切都会顺利安装完成。

sudo pip install opencanary

0×03 管理配置

Opencanary的配置几乎都在配置文件里生成,第一次安装的时候,我们可以通过命令行创建一个配置文件,这个配置默认只打开了FTP的蜜罐监听

opencanaryd --copyconfig

加copyconfig选项的意思是,是生成了一个模板配置文件/root/.opencanary.conf,整个opencanary支持多种服务的监听:

FTP
HTTP
Proxy
MSSQL
MySQL
NTP
Portscan
RDP
Samba
SIP
SNMP
SSH
Telnet
TFTP
VNC

为了更直观一些,我们截取一段ftp监听的代码:

from opencanary.modules import CanaryService
from twisted.application import internet
from twisted.protocols.ftp import FTPFactory, FTPRealm, FTP, \
USR_LOGGED_IN_PROCEED, GUEST_LOGGED_IN_PROCEED, IFTPShell, \
AuthorizationError
from twisted.cred.portal import Portal
from zope.interface import implements
from twisted.cred.checkers import ICredentialsChecker
from twisted.python import failure
from twisted.cred import error as cred_error, credentials
FTP_PATH = "/briar/data/ftp"
class DenyAllAccess:
implements(ICredentialsChecker)
credentialInterfaces = (credentials.IAnonymous, credentials.IUsernamePassword)
def requestAvatarId(self, credentials):
return failure.Failure(cred_error.UnauthorizedLogin())
class LoggingFTP(FTP):
#ripped from main FTP class, overridden to extract connection info
def ftp_PASS(self, password):
"""
Second part of login. Get the password the peer wants to
authenticate with.
"""
if self.factory.allowAnonymous and self._user == self.factory.userAnonymous:
# anonymous login
creds = credentials.Anonymous()
reply = GUEST_LOGGED_IN_PROCEED
else:
# user login
creds = credentials.UsernamePassword(self._user, password)
reply = USR_LOGGED_IN_PROCEED
logdata = {'USERNAME': self._user, 'PASSWORD': password}
self.factory.canaryservice.log(logdata, transport=self.transport)
del self._user
def _cbLogin((interface, avatar, logout)):
assert interface is IFTPShell, "The realm is busted, jerk."
self.shell = avatar
self.logout = logout
self.workingDirectory = []
self.state = self.AUTHED
return reply
def _ebLogin(failure):
failure.trap(cred_error.UnauthorizedLogin, cred_error.UnhandledCredentials)
self.state = self.UNAUTH
raise AuthorizationError
d = self.portal.login(creds, None, IFTPShell)
d.addCallbacks(_cbLogin, _ebLogin)
return d
class CanaryFTP(CanaryService):
NAME = 'ftp'
def __init__(self,config=None, logger=None):
CanaryService.__init__(self, config=config, logger=logger)
self.banner = config.getVal('ftp.banner', default='FTP Ready.').encode('utf8')
self.port = config.getVal('ftp.port', default=21)
# find a place to check that logtype is initialised
# find a place to check that factory has service attached
self.logtype = logger.LOG_FTP_LOGIN_ATTEMPT
self.listen_addr = config.getVal('device.listen_addr', default='')
def getService(self):
p = Portal(FTPRealm(FTP_PATH), [DenyAllAccess()])
f = FTPFactory(p)
f.protocol = LoggingFTP
f.welcomeMessage = self.banner
f.canaryservice = self
return internet.TCPServer(self.port, f, interface=self.listen_addr)

ftp监听服代码在80多行,就完成模拟服务,可以看到使用twisted库。

from twisted.application import internet
from twisted.protocols.ftp import FTPFactory, FTPRealm, FTP, \
USR_LOGGED_IN_PROCEED, GUEST_LOGGED_IN_PROCEED, IFTPShell, \
AuthorizationError

其它模拟代码都在moules目录下,都可参考。

配置文件中,默认打开了FTP的访问,用一个正常的ftp客户端,无论是命令行还是GUI工具都可以:

{
"device.node_id": "opencanary-1",
"git.enabled": false,
"git.port" : 9418,
"ftp.enabled": true,
"ftp.port": 21,
"ftp.banner": "FTP server ready",
"http.banner": "Apache/2.2.22 (Ubuntu)",
"http.enabled": false,
"http.port": 80,
"http.skin": "nasLogin",
"http.skin.list": [
{
"desc": "Plain HTML Login",
"name": "basicLogin"
},
{
"desc": "Synology NAS Login",
"name": "nasLogin"
}
],
"httpproxy.enabled" : false,
"httpproxy.port": 8080,
"httpproxy.skin": "squid",
"httproxy.skin.list": [
{
"desc": "Squid",
"name": "squid"
},
{
"desc": "Microsoft ISA Server Web Proxy",
"name": "ms-isa"
}
],
"logger": {
"class": "PyLogger",
"kwargs": {
"formatters": {
"plain": {
"format": "%(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
},
"file": {
"class": "logging.FileHandler",
"filename": "/var/tmp/opencanary.log"
}
}
}
},
"portscan.enabled": false,
"portscan.logfile":"/var/log/kern.log",
"portscan.synrate": 5,
"portscan.nmaposrate": 5,
"portscan.lorate": 3,
"smb.auditfile": "/var/log/samba-audit.log",
"smb.enabled": false,
"mysql.enabled": false,
"mysql.port": 3306,
"mysql.banner": "5.5.43-0ubuntu0.14.04.1",
"ssh.enabled": false,
"ssh.port": 22,
"ssh.version": "SSH-2.0-OpenSSH_5.1p1 Debian-4",
"redis.enabled": false,
"redis.port": 6379,
"rdp.enabled": false,
"rdp.port": 3389,
"sip.enabled": false,
"sip.port": 5060,
"snmp.enabled": false,
"snmp.port": 161,
"ntp.enabled": false,
"ntp.port": "123",
"tftp.enabled": false,
"tftp.port": 69,
"tcpbanner.maxnum":10,
"tcpbanner.enabled": false,
"tcpbanner_1.enabled": false,
"tcpbanner_1.port": 8001,
"tcpbanner_1.datareceivedbanner": "",
"tcpbanner_1.initbanner": "",
"tcpbanner_1.alertstring.enabled": false,
"tcpbanner_1.alertstring": "",
"tcpbanner_1.keep_alive.enabled": false,
"tcpbanner_1.keep_alive_secret": "",
"tcpbanner_1.keep_alive_probes": 11,
"tcpbanner_1.keep_alive_interval":300,
"tcpbanner_1.keep_alive_idle": 300,
"telnet.enabled": false,
"telnet.port": "23",
"telnet.banner": "",
"telnet.honeycreds": [
{
"username": "admin",
"password": "$pbkdf2-sha512$19000$bG1NaY3xvjdGyBlj7N37Xw$dGrmBqqWa1okTCpN3QEmeo9j5DuV2u1EuVFD8Di0GxNiM64To5O/Y66f7UASvnQr8.LCzqTm6awC8Kj/aGKvwA"
},
{
"username": "admin",
"password": "admin1"
}
],
"mssql.enabled": false,
"mssql.version": "2012",
"mssql.port":1433,
"vnc.enabled": false,
"vnc.port":5000
}

当配置文件用–copyconfig生成后,我们执行Opencanary服务启动程序。对于我们想创建的监听服务,我们把对应服务在配置中的.enabled选项设置成true即可,用–restart重启opencanary服务之后,就可以打开服务端口监听

启动服务

opencanaryd --start

当所有的模拟监听服务都打开的时候,会出现类似下面的进程信息。

netstat -plunt
tcp 0 0 0.0.0.0:2222 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:23 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:1433 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:3389 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:8001 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:9418 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 12683/python
tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 12683/python
udp 0 0 0.0.0.0:57197 0.0.0.0:* 8994/python
udp 0 0 0.0.0.0:5060 0.0.0.0:* 12683/python
udp 0 0 0.0.0.0:69 0.0.0.0:* 12683/python
udp 0 0 0.0.0.0:123 0.0.0.0:* 12683/python
udp 0 0 0.0.0.0:161 0.0.0.0:* 12683/python

重启服务

opencanaryd --restart

关闭服务

opencanaryd --stop

其它的命令参数使用可以用–help查一下。

opencanaryd --help

0×04 报警日志

Opencanary模拟了各种服务的监听,当模拟监听被触发的时候就会报警,产生日志,下面给出发报警的方式和产生的日志格式。一般情况下,我们会在机房和内网的网络环境下部署蜜罐。如果蜜罐系统放到外网,会不时的被各种组织的探测手段发现,进行标识统计。而在内网外部的探测行为是进不了内网的,除非节点被攻破,成为攻击的入口。

正是当有人非法进入内网后,才会出现勿碰蜜罐的事件发生概率提高,而内部防御不只是一种防御手段,各家公司都提供了流量监听产品,HIDS这种工具,Opencanary就像您居家过日子,晚上门口多放了几个啤酒瓶子,如真的门锁被破开,入侵者有能会碰到屋中放置的啤酒瓶子。

蜜罐系统,可以模拟各种程序协议,我们以FTP为例,展示一下蜜罐的触碰与报警日志生成的流程,Opencanary中各种服务的触碰方法,我们会这个例子的后面列出来。

关于FTP,我们测试蜜罐服务器是192.168.0.6,我们启动了opencanary,然后用ftp客户端进行访问:

ftp 192.168.0.6

一般会出现,类似下面的操作过程:

[root@localhost opencanary]# ftp 192.168.0.6
Connected to 192.168.0.6 (192.168.0.6).
220 FTP server ready
Name (192.168.0.6:root): test
331 Password required for test.
Password:
530 Sorry, Authentication failed.
Login failed.
ftp>

这个过程交互结束之后,就会生成一条json数据,json日志数据如下:

{"src_port": 35990, "logdata": {"USERNAME": "test", "PASSWORD": "123456"}, "logtype": 2000, "dst_host": "192.168.0.6", "dst_port": 21, "src_host": "192.168.0.5"}

这是一次针对192.168.0.6的21号端口ftp访问,OpenCanary有一个ftp模拟的python监听实现脚本,之前也说过是基于twisted的实现,当蜜罐系统监听到入侵者访问时,就会把相应的paylog存起来,然后记录到日志文件中,在opencanary.conf中设置的日志文件位置是/usr/tmp/opencanary.log中。

官方的opencanary的日志都是生成到本地,后面我们会给出一,如何将日志转存到其它地方。

下面我们列举出,几乎所有opencanary蜜罐支持协议的触发方法:

4.1 HTTP

触发方法

curl 0.0.0.0:80

日志数据

{"dst_host": "172.18.200.58", "dst_port": 80, "local_time": "2019-01-07 13:47:45.817940", "logdata": {"HOSTNAME": "172.18.200.58", "PASSWORD": "admin888", "PATH": "/index.html", "SKIN": "nasLogin", "USERAGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:61.0) Gecko/20100101 Firefox/61.0", "USERNAME": "admin"}, "logtype": 3001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54488}

4.2 FTP

触发方法

ftp 172.12.200.58

日志数据

{"dst_host": "172.18.200.58", "dst_port": 80, "local_time": "2019-01-07 13:47:45.817940", "logdata": {"HOSTNAME": "172.18.200.58", "PASSWORD": "admin888", "PATH": "/index.html", "SKIN": "nasLogin", "USERAGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:61.0) Gecko/20100101 Firefox/61.0", "USERNAME": "admin"}, "logtype": 3001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54488}

4.3 SSH

触发方法

ssh [email protected]

日志

{"dst_host": "172.18.200.58", "dst_port": 2222, "local_time": "2019-01-07 13:54:27.811101", "logdata": {"SESSION": "3"}, "logtype": 4000, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54639}

4.4 Telnet

触发方法

telnet 172.18.200.58

日志数据

{"dst_host": "172.18.200.58", "dst_port": 23, "honeycred": false, "local_time": "2019-01-07 13:56:45.341785", "logdata": {"PASSWORD": "admin888", "USERNAME": "admin123"}, "logtype": 6001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54676}

4.5 Mysql

触发方法

mysql -h172.18.200.58 -uroot -p

日志数据

{"dst_host": "172.18.200.58", "dst_port": 3306, "local_time": "2019-01-07 13:58:25.922257", "logdata": {"PASSWORD": "18076c09615de80ddb2903191b783714918b4c4f", "USERNAME": "root"}, "logtype": 8001, "node_id": "opencanary-1", "src_host": "172.18.220.253", "src_port": 46662}

4.6 Git

触发方法

git clone git://192.168.1.7:9418/tmp.git

日志数据

{"dst_host": "192.168.1.7", "dst_port": 9418, "local_time": "2019-01-05 15:38:46.368627", "logdata": {"HOST": "192.168.1.7:9418", "REPO": "tmp.git"}, "logtype": 16001, "node_id": "opencanary-1", "src_host": "192.168.1.3", "src_port": 57606}

4.7 NTP

触发方法

git clone git://192.168.1.7:9418/tmp.git

日志数据

{"dst_host": "0.0.0.0", "dst_port": 123, "local_time": "2019-01-05 15:58:52.075987", "logdata": {"NTP CMD": "monlist"}, "logtype": 11001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 57886}

4.8 Redis

触发方法

(env) [root@honeypot Honeypot]# redis-cli -h 192.168.1.7192.168.1.7:6379> keys *(error) NOAUTH Authentication required.192.168.1.7:6379> config get requirepass(error) ERR unknown command 'config'192.168.1.7:6379> auth admin(error) ERR invalid password192.168.1.7:6379>

日志数据

{"dst_host": "192.168.1.7", "dst_port": 6379, "local_time": "2019-01-05 16:05:11.637269", "logdata": {"ARGS": "", "CMD": "COMMAND"}, "logtype": 17001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 34471}

4.9 TCP

触发方法

telnet 192.168.1.6 8001

日志

{"dst_host": "192.168.1.6", "dst_port": 8001, "local_time": "2019-01-05 17:18:51.601478", "logdata": {"BANNER_ID": "1", "DATA": "", "FUNCTION": "CONNECTION_MADE"}, "logtype": 18002, "node_id": "opencanary-1", "src_host": "192.168.1.3", "src_port": 59176}

4.10 VNC

触发方法

用VNC客户端访问

日志数据

{"dst_host": "192.168.1.7", "dst_port": 5000, "local_time": "2019-01-06 08:21:28.951940", "logdata": {"VNC Client Response": "58c00be9ee5b7f3b666771dd2bda9309", "VNC Password": "<Password was not in the common list>", "VNC Server Challenge": "953e2dff7e4d3a3114527c282817ce1d"}, "logtype": 12001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 54634}

4.11 RDP

触发方法

日志数据

{"dst_host": "192.168.1.7", "dst_port": 3389, "local_time": "2019-01-06 08:59:13.890934", "logdata": {"DOMAIN": "", "HOSTNAME": "HelloHost", "PASSWORD": "helloword", "USERNAME": "administrator1"}, "logtype": 14001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 59955}

4.12 SIP

触发方法

hydra -l adminsip -p password 192.168.1.7 sip

日志格式

{"dst_host": "0.0.0.0", "dst_port": 5060, "local_time": "2019-01-06 09:55:12.578148", "logdata": {"HEADERS": {"call-id": ["[email protected]"], "content-length": ["0"], "cseq": ["1 REGISTER"], "from": ["<sip:[email protected]>"], "to": ["<sip:[email protected]>"], "via": ["SIP/2.0/UDP 10.0.2.15:46759;received=192.168.1.7"]}}, "logtype": 15001, "node_id": "opencanary-1", "src_host": "192.168.1.7", "src_port": 46759}

4.13 SNMP

触发方法

hydra -p password 192.168.1.7 snmp

日志数据

{"dst_host": "0.0.0.0", "dst_port": 161, "local_time": "2019-01-06 11:17:27.266214", "logdata": {"COMMUNITY_STRING": "password", "REQUESTS": ["1.3.6.1.2.1.1.1"]}, "logtype": 13001, "node_id": "opencanary-1", "src_host": "192.168.1.7", "src_port": 47112}

4.14 NMAP

触发方法

sudo nmap -v -Pn -O 192.168.1.7

日志数据

{"dst_host": "192.168.1.7", "dst_port": "21", "local_time": "2019-01-06 16:35:24.356080", "logdata": {"FIN": "", "ID": "37499", "IN": "eth1", "LEN": "60", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "PSH": "", "RES": "0x00", "SYN": "", "TOS": "0x00", "TTL": "56", "URG": "", "URGP": "0", "WINDOW": "256"}, "logtype": 5002, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "40098"}

4.15 SYN探测

触发方法

sudo nmap -sS 192.168.1.7

日志数据

{"dst_host": "192.168.1.7", "dst_port": "21", "local_time": "2019-01-06 16:35:24.190176", "logdata": {"ID": "51918", "IN": "eth1", "LEN": "56", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "RES": "0x00", "SYN": "", "TOS": "0x00", "TTL": "58", "URGP": "0", "WINDOW": "512"}, "logtype": 5001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "40088"}

4.16 FIN探测

触发方法

sudo nmap -sF 192.168.1.7

日志数据

{"dst_host": "192.168.1.7", "dst_port": "23", "local_time": "2019-01-06 16:46:18.336954", "logdata": {"FIN": "", "ID": "29768", "IN": "eth1", "LEN": "40", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "RES": "0x00", "TOS": "0x00", "TTL": "59", "URGP": "0", "WINDOW": "1024"}, "logtype": 5005, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "35116"}

4.17 XmasTree探测

触发方法

sudo nmap -sX 192.168.1.7

日志数据

{"dst_host": "192.168.1.7", "dst_port": "139", "local_time": "2019-01-06 16:48:46.225539", "logdata": {"FIN": "", "ID": "19984", "IN": "eth1", "LEN": "40", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "PSH": "", "RES": "0x00", "TOS": "0x00", "TTL": "56", "URG": "", "URGP": "0", "WINDOW": "1024"}, "logtype": 5004, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "50913"}

4.18 Null探测

触发方法

sudo nmap -sN 192.168.1.7

日志数据

{"dst_host": "192.168.1.7", "dst_port": "5060", "local_time": "2019-01-06 16:51:07.789903", "logdata": {"ID": "26441", "IN": "eth1", "LEN": "40", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "RES": "0x00", "TOS": "0x00", "TTL": "50", "URGP": "0", "WINDOW": "1024"}, "logtype": 5003, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "58015"}

4.19 MSSQL

触发方法

MSSQL客户端工具

日志数据

{"dst_host": "172.18.200.58", "dst_port": 1433, "local_time": "2019-01-07 09:04:58.690137", "logdata": {"AppName": "SQLPro for MSSQL (hankinsoft.com)", "CltIntName": "DB-Library", "Database": "test", "HostName": "Piroguehost", "Language": "us_english", "Password": "sa123456", "ServerName": "172.18.200.58:1433", "UserName": "sa"}, "logtype": 9001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 64344}

4.20 HTTPProxy

触发方法

浏览器配置

日志数据

{"dst_host": "172.18.200.58", "dst_port": 8080, "local_time": "2019-01-07 13:26:47.761297", "logdata": {"PASSWORD": "passsquid", "USERNAME": "squidadmin"}, "logtype": 7001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 53798}

0×05 Opencanary的功能扩展

Opencanay是用Python开源的,我们可以根据我们具体的需求,扩展opencanary的功能,这个需求产生的原因,是我们需要对报警日志进行汇聚。

蜜罐日志是日常威胁日志收集的一部分,蜜罐报警的日志分析的优先级相对是很高的,因为一般情况下,蜜罐的误碰不应该太多(白名单),所以第一时间出现问题,需要蜜罐把数据推给日志分析系统、或是日志接收服务,数据的取得有多种方式,之前一种方式是把日志存到本地的日志文本文件中,实时也写到mysql数据中,然后再由其它程序进行轮训,这种处理方式有一个问题是,轮询是有时间间隔的,定时任务的轮训机制,真实的报警时间和攻击实际发生时间可能不在一个时间点,这样在做关联分析时,如果没有唯一标识机器属性时,比如:mac,没法判断内网IP对应的是同一个人的行为,所以,我们要第一时间接收到蜜罐的报警。

OpenCanary的代码是开源的, 我们只需要改源代码,就可以实现我们的需求,OpenCanary有一个logger日志写入类,我们只要改这个模块的代码即可。

我们一般有几种日志数据存储形式:

1.RESTAPI:我们在蜜罐写日志的地方,调用一个REST API的接口服务,将报警数据转发给其它的REST接口,由接口写数据,分析,报警。

2.RPC:在蜜罐写日志的地方加一个RPC调用接口,像调用一般函数一样,调用一个远程RPC写日志方法。然后在PPC服务端加入各种判定策略逻辑,就像我们之间介绍的semaphore那个项目。

3.UDP Syslog:在蜜罐写日志的地方,直接通过socketc通过syslog协议数据,把json数据发给一个syslog监听,有不同的开源方案都能解决这个问题, 我们一般采用的都是Graylog的数据收集方法,将syslog监听的数据接收后,对其中的json格式数据时进行解析,然后存入到多复本的ES集群中,这个过程中Graylog做了很多ES数据的管理工作,让ES操作管理变得容易,而Splunk如果数据量不大是可以免费使用的, 对于一般数据规模,几个ES集群这种情况,Graylog就可以解决,功能看实际需求,根据自己情况进行扩展,开源可以省下很多开发人月。

修改源代码位置:

logger是一人单例模式的类,logger.py在 opencanary的库文件中,如果不出意外的话,位置应该是:

/usr/lib/python2.7/site-packages/opencanary

如果您是用virtualenv安装,那库的位置就在您的虚拟化python的位置的库文件中。

logger.py源代码大家可以在库文件中找到, 我们不列出所有源代码,为了方便展示,我们不在文件中重写一个handler类,而是直接在写本地日志的地方,将报警日志外发到Syslog监听,或是直接放给Graylog服务器,作为数据接收端接受数据,后期对数据进行黑白名单处理,数据聚合处理。

这样进行扩展的好处就是,不破坏原有的执行时序,达到数据转存的目的。

def log(self, logdata, retry=True):
import syslog_client
graylog = syslog_client.Syslog("198.168.0.8")
graylog.send(json.dumps(logdata), syslog_client.Level.INFO)
logdata = self.sanitizeLog(logdata)
self.logger.warn(json.dumps(logdata, sort_keys=True))

下面这句话是写日志,

self.logger.warn(json.dumps(logdata, sort_keys=True))

我们另外加入几行转发syslog的日志处理:

import syslog_client
graylog = syslog_client.Syslog("198.168.0.8")
graylog.send(json.dumps(logdata), syslog_client.Level.INFO)

这里要特别注意的地方是,在发送日志数据之前,要把数据包装成json数据格式,这样当数据发送给Graylog之后, 就可能直接对数据按json格式的数据切割。

26ZBZvz.jpg!web

也可以通过自定义的syslog监听服务,对数据进行各种加工处理。

我们看一下opencanary.conf中是如何关联到logger这个类的。

    "logger": {
        "class": "PyLogger",
        "kwargs": {
            "formatters": {
                "plain": {
                    "format": "%(message)s"
                }
            },
            "handlers": {
                "console": {
                    "class": "logging.StreamHandler",
                    "stream": "ext://sys.stdout"
                },
                "file": {
                    "class": "logging.FileHandler",
                    "filename": "/var/tmp/opencanary.log"
                }
            }
        }
    },

从这个配置里我们可以看到是,直接关联到了logging.FileaHandler这个类,就是直接写了文件。

实际上我们可以根据Opencanry提供的Exampler.py的模式写一个模块来扩展蜜罐的功能,一样可以在opencanary.conf中引用,这种模板是基于创建监听的。

下面是模块编写的模板。

from opencanary.modules import CanaryService
from twisted.internet.protocol import Protocol
from twisted.internet.protocol import Factory
from twisted.application import internet
class Example0Protocol(Protocol):
"""
Example (Fictional) Protocol
$ nc localhost 8007
Welcome!
password: wrong0
password: wrong1
password: wrong2
Bad passwords
$
"""
def __init__(self):
self.prompts = 0
def connectionMade(self):
self.transport.write("Welcome!\r\npassword: ")
self.prompts += 1
def dataReceived(self, data):
"""
Careful, data recieved here is unbuffered. See example1
for how this can be better handled.
"""
password = data.strip("\r\n")
logdata = {"PASSWORD" : password}
self.factory.log(logdata, transport=self.transport)
if self.prompts < 3:
self.transport.write("\r\npassword: ")
self.prompts += 1
else:
self.transport.write("\r\nBad passwords\r\n")
self.transport.loseConnection()
class CanaryExample0(Factory, CanaryService):
NAME = 'example0'
protocol = Example0Protocol
def __init__(self, config=None, logger=None):
CanaryService.__init__(self, config, logger)
self.port = 8007
self.logtype = logger.LOG_BASE_EXAMPLE
CanaryServiceFactory = CanaryExample0

还有一种方式,我们在logger文件中直接加一个handler处理,把FileHandler变成syslog的SyslogHandler。

class DemoHandler(logging.Handler):
    def __init__(self, demo_userid, demo_authkey, allowed_ports):
        logging.Handler.__init__(self)
        self.demo_userid = str(demo_userid)
        self.demo_authkey = str(demo_authkey)
        try:
            # Extract the list of allowed ports
            self.allowed_ports = map(int, str(allowed_ports).split(','))
        except:
            # By default, report only port 22
            self.allowed_ports = [ 22 ]

    def emit(self, record):
        ...

再修改opencanary.conf配置文件。

"logger": {
    "class" : "PyLogger",
    "kwargs" : {
        "formatters": {
            "plain": {
                "format": "%(message)s"
            }
        },
        "handlers": {
            "dshield": {
                "class": "opencanary.logger.DemoHandler",
                "demo_userid": "test",
                "demo_authkey": "$%$##$#%$%",
                "allowed_ports": "22,23"
            }
        }
    }
}

syslog协议是一种可以基于UDP协议进行数据传输的协议,在早期数据野蛮生长的阶段,我们创建很多基于syslog的服务数据收集,因为一般的安全设备都支持syslog数据的发送。

后期ELK是一种最常见的数据管理,包括Graylog和Clickhosue这些数据管理手段,都可以很好的集中管理数据,但是这是一种粗放的日志数据管理,还没有进入数据深加工的阶段。

0×06 蜜罐与威胁情报

对于内网蜜罐来说,很多时候的攻击IP可能是内网的IP, 并不都是外网IP,所有在与威胁情报库进行比对的时候,查不到相关的数据。但是内网IP会与其它的设备发生关联,在其它的一些网络防护设备上,我们可能看到相关的内网IP发生的其它报警行为。

如果我们将所有防御设备上的交通流量,直接进入关联,那将是一个海量数据的比较,这种情况下, 为了快速的索引数据,我们可能要用ClickHouse这种级别的数据据库产品,在初期阶段,我们相对大量的收集了日志数据,交通数据和报警数据都有,但问题是数据操作起来不灵活。

随着业务数据不断的增长,这种方式不一定适合我们新数据分析场景,我们用一个IP与海量的日志数据,进行比对,是一个很耗费性能资源和时间的事情,所以我们考虑,在原始数据日志之上,建立新一层日数据汇聚层,将有用的数据进行深入的加工和汇聚,我们的安全策略不再直接基于海量的原始日志数据,而是建立在更精准的数据汇聚层的,简单说这些数据是进过整理、整形、去重、提纯的数据,数据之间存在更强的关联,并且是更精巧的数据集。

我们原始的日志数据,基于REST API,这样服务之间的通信友好,不需要特定客户端协议的依赖,只要支持 REST就可以。中间层的数据是对原始数据的精括聚合,我们使用RPC的方式进行数据的查询,我们可以将业务模块的数据,变成像普通函数一样的RPC调用。

对于中间的数据服务来说,无论是RPC还是REST,其实都可以实现我们数据划分的目的,技术手段多种多样,但本质上的问题,某些场景下,用空间换时间, 或是时间换空间。

对于蜜罐报警,我们可以作为一个事件发现的起始点,也是很重要的一点时间点,攻击进行时,因为内网蜜罐的报警,一般情况下是不应该出现高频率的报警的, 所以,实时推送报警信息,在并发生性能上没有太大的压力,直接在可以威胁发生的时间点就报警,异步进行关联分析。

如果是采用报警写数据库,再用有时间间隔的定时计划任务去轮训就会有时间延迟,这也是我们想直接改造opencanary的源代码的一个原因,当然这个问题产生本身和opencanary没关系。

0×07 蜜罐的集中管理

一般在交换机上支持端口复用的环境下,都可以在一台机器上,配置多个IP,opencanary提供了一个报警蜜罐的实现解决方案,但是更多的时候是提供了一个agent,场景多用于内网蜜罐的部署。修改配置文件中的server.ip,对应多个指定的IP。

企业都有很多的机房,所以蜜罐的结点管理就是一个课题,国人在Opencanay的基础上开发社区版的后面管理界面:opencanary web。

7jIj2yb.jpg!web

这个图上的agent就是opencanary, 报警的方式还是传统的邮件,这里就存一个问题,这个邮件告警是集中后的后发出,从图上没有看出是在agent上直接发出的,除非在写入数据库时就报警,这样是第一时间进行的报警,另外大家可以将邮件告警,增加到用公众号报警和syslog告警。

系统实现基于nginx和tornadoweb,数据库系统使用的是mysql,前端口使用的是vue, 具体的安装方法大家可以直接看github。

Zfuimye.jpg!web

Opencanary支持Hpfeeds,还记得那个客户端吗:

hpfeeds-client --host localhost -p 10000 -i honeymap -s cfdd6a68be69464666ae60b66dae69f6 -c geoloc.events publish "{countrycode:'NA', latitude:37.7749, longitude:-122.4194, city:'San Francisco'}"

这个Opencanary也是支持和hpfeeds进行交互的,但是Opencanary用于内网部署,很多IP都是内网IP,不太容易看出地图炮效果。有的IP可以看到在国内的位置地理信息,和CMDB系统关联信息。

有没有一种方法不用WEB管理端也能聚合报警日志呢,其实可以的,用本文的扩展方法,直接将报警数据,第一时间转存到Graylog上,用Graylog管理ES集群,直接在Graylog加查询,聚合,还有报警功能,如果你们公司,有spluk,用splunk也可以做到,总是有很多的方案都可以达到您的目的,但是基于数据关联的策略,还是需要本地中间层数据聚合。

最直接的蜜罐探活监听就是fping,最简单,间隔判断这些机器的网络是否通,zabbix也可以监控到服务,但是需要对zabbix加白名单。

7vYJ3qF.jpg!web

如果使用Graylog或是splunk这种系统,地图信息管理IP和地图炮功能都是有的,但是一篇肯定是写不完了,不介绍可自行实验。

0×08 总结:

Opencanary是一款适合内网使用的蜜罐系统,部署简单,配置容易,扩展方便,但在模拟服务的数据量上可能还不是最全的,我们旨在通过一个蜜罐的例子,来串起整个系统,从感知Agent到威胁报警日志数据汇聚的过程。Opencanary有很大的扩展潜能,因为Opencanary本身的易的扩展性,简单性,只要自己动手丰衣足食,进行大型扩展也要看增加的特性量。Opencanary用于内网蜜罐还是可以满足常用需求的。

传统的管理后台,是一种结点监控和数据日志汇总的角色,再加上报警处理等,然而威胁发现的确认逻辑,是要将所以尽可能取得的关联信息进行策略的过滤匹配,而传统系统如果只是报警存储,威胁的可视范围就局限于自己的威胁发现区域,特别是这种陷阱式的蜜罐。变成威胁信息孤岛,不如将蜜罐作为检查威胁事件的触发者,事件起始点,一旦蜜罐发生报警,将蜜罐与提前准备好的关联威胁数据视图,按内部安全逻辑进行交互,得出更精确的判断,效果要好于白名单的严格和黑名单的粗放控制。

*本文原创作者:糖果L5Q,本文属FreeBuf原创奖励计划,未经许可禁止转载


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK