当前位置:   article > 正文

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

开源蜜罐

0×00 前言

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

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

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

一般可以在网络环境中,通过交换机的端口聚合技术,在一台机器上创建多个网段的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监听的代码:

  1. from opencanary.modules import CanaryService
  2. from twisted.application import internet
  3. from twisted.protocols.ftp import FTPFactory, FTPRealm, FTP, \
  4. USR_LOGGED_IN_PROCEED, GUEST_LOGGED_IN_PROCEED, IFTPShell, \
  5. AuthorizationError
  6. from twisted.cred.portal import Portal
  7. from zope.interface import implements
  8. from twisted.cred.checkers import ICredentialsChecker
  9. from twisted.python import failure
  10. from twisted.cred import error as cred_error, credentials
  11. FTP_PATH = "/briar/data/ftp"
  12. class DenyAllAccess:
  13. implements(ICredentialsChecker)
  14. credentialInterfaces = (credentials.IAnonymous, credentials.IUsernamePassword)
  15. def requestAvatarId(self, credentials):
  16. return failure.Failure(cred_error.UnauthorizedLogin())
  17. class LoggingFTP(FTP):
  18. #ripped from main FTP class, overridden to extract connection info
  19. def ftp_PASS(self, password):
  20. """
  21. Second part of login. Get the password the peer wants to
  22. authenticate with.
  23. """
  24. if self.factory.allowAnonymous and self._user == self.factory.userAnonymous:
  25. # anonymous login
  26. creds = credentials.Anonymous()
  27. reply = GUEST_LOGGED_IN_PROCEED
  28. else:
  29. # user login
  30. creds = credentials.UsernamePassword(self._user, password)
  31. reply = USR_LOGGED_IN_PROCEED
  32. logdata = {'USERNAME': self._user, 'PASSWORD': password}
  33. self.factory.canaryservice.log(logdata, transport=self.transport)
  34. del self._user
  35. def _cbLogin((interface, avatar, logout)):
  36. assert interface is IFTPShell, "The realm is busted, jerk."
  37. self.shell = avatar
  38. self.logout = logout
  39. self.workingDirectory = []
  40. self.state = self.AUTHED
  41. return reply
  42. def _ebLogin(failure):
  43. failure.trap(cred_error.UnauthorizedLogin, cred_error.UnhandledCredentials)
  44. self.state = self.UNAUTH
  45. raise AuthorizationError
  46. d = self.portal.login(creds, None, IFTPShell)
  47. d.addCallbacks(_cbLogin, _ebLogin)
  48. return d
  49. class CanaryFTP(CanaryService):
  50. NAME = 'ftp'
  51. def __init__(self,config=None, logger=None):
  52. CanaryService.__init__(self, config=config, logger=logger)
  53. self.banner = config.getVal('ftp.banner', default='FTP Ready.').encode('utf8')
  54. self.port = config.getVal('ftp.port', default=21)
  55. # find a place to check that logtype is initialised
  56. # find a place to check that factory has service attached
  57. self.logtype = logger.LOG_FTP_LOGIN_ATTEMPT
  58. self.listen_addr = config.getVal('device.listen_addr', default='')
  59. def getService(self):
  60. p = Portal(FTPRealm(FTP_PATH), [DenyAllAccess()])
  61. f = FTPFactory(p)
  62. f.protocol = LoggingFTP
  63. f.welcomeMessage = self.banner
  64. f.canaryservice = self
  65. return internet.TCPServer(self.port, f, interface=self.listen_addr)

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

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

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

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

  1. {
  2. "device.node_id": "opencanary-1",
  3. "git.enabled": false,
  4. "git.port" : 9418,
  5. "ftp.enabled": true,
  6. "ftp.port": 21,
  7. "ftp.banner": "FTP server ready",
  8. "http.banner": "Apache/2.2.22 (Ubuntu)",
  9. "http.enabled": false,
  10. "http.port": 80,
  11. "http.skin": "nasLogin",
  12. "http.skin.list": [
  13. {
  14. "desc": "Plain HTML Login",
  15. "name": "basicLogin"
  16. },
  17. {
  18. "desc": "Synology NAS Login",
  19. "name": "nasLogin"
  20. }
  21. ],
  22. "httpproxy.enabled" : false,
  23. "httpproxy.port": 8080,
  24. "httpproxy.skin": "squid",
  25. "httproxy.skin.list": [
  26. {
  27. "desc": "Squid",
  28. "name": "squid"
  29. },
  30. {
  31. "desc": "Microsoft ISA Server Web Proxy",
  32. "name": "ms-isa"
  33. }
  34. ],
  35. "logger": {
  36. "class": "PyLogger",
  37. "kwargs": {
  38. "formatters": {
  39. "plain": {
  40. "format": "%(message)s"
  41. }
  42. },
  43. "handlers": {
  44. "console": {
  45. "class": "logging.StreamHandler",
  46. "stream": "ext://sys.stdout"
  47. },
  48. "file": {
  49. "class": "logging.FileHandler",
  50. "filename": "/var/tmp/opencanary.log"
  51. }
  52. }
  53. }
  54. },
  55. "portscan.enabled": false,
  56. "portscan.logfile":"/var/log/kern.log",
  57. "portscan.synrate": 5,
  58. "portscan.nmaposrate": 5,
  59. "portscan.lorate": 3,
  60. "smb.auditfile": "/var/log/samba-audit.log",
  61. "smb.enabled": false,
  62. "mysql.enabled": false,
  63. "mysql.port": 3306,
  64. "mysql.banner": "5.5.43-0ubuntu0.14.04.1",
  65. "ssh.enabled": false,
  66. "ssh.port": 22,
  67. "ssh.version": "SSH-2.0-OpenSSH_5.1p1 Debian-4",
  68. "redis.enabled": false,
  69. "redis.port": 6379,
  70. "rdp.enabled": false,
  71. "rdp.port": 3389,
  72. "sip.enabled": false,
  73. "sip.port": 5060,
  74. "snmp.enabled": false,
  75. "snmp.port": 161,
  76. "ntp.enabled": false,
  77. "ntp.port": "123",
  78. "tftp.enabled": false,
  79. "tftp.port": 69,
  80. "tcpbanner.maxnum":10,
  81. "tcpbanner.enabled": false,
  82. "tcpbanner_1.enabled": false,
  83. "tcpbanner_1.port": 8001,
  84. "tcpbanner_1.datareceivedbanner": "",
  85. "tcpbanner_1.initbanner": "",
  86. "tcpbanner_1.alertstring.enabled": false,
  87. "tcpbanner_1.alertstring": "",
  88. "tcpbanner_1.keep_alive.enabled": false,
  89. "tcpbanner_1.keep_alive_secret": "",
  90. "tcpbanner_1.keep_alive_probes": 11,
  91. "tcpbanner_1.keep_alive_interval":300,
  92. "tcpbanner_1.keep_alive_idle": 300,
  93. "telnet.enabled": false,
  94. "telnet.port": "23",
  95. "telnet.banner": "",
  96. "telnet.honeycreds": [
  97. {
  98. "username": "admin",
  99. "password": "$pbkdf2-sha512$19000$bG1NaY3xvjdGyBlj7N37Xw$dGrmBqqWa1okTCpN3QEmeo9j5DuV2u1EuVFD8Di0GxNiM64To5O/Y66f7UASvnQr8.LCzqTm6awC8Kj/aGKvwA"
  100. },
  101. {
  102. "username": "admin",
  103. "password": "admin1"
  104. }
  105. ],
  106. "mssql.enabled": false,
  107. "mssql.version": "2012",
  108. "mssql.port":1433,
  109. "vnc.enabled": false,
  110. "vnc.port":5000
  111. }

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

启动服务

opencanaryd --start

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

netstat -plunt
  1. tcp 0 0 0.0.0.0:2222 0.0.0.0:* LISTEN 12683/python
  2. tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 12683/python
  3. tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 12683/python
  4. tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN 12683/python
  5. tcp 0 0 0.0.0.0:23 0.0.0.0:* LISTEN 12683/python
  6. tcp 0 0 0.0.0.0:1433 0.0.0.0:* LISTEN 12683/python
  7. tcp 0 0 0.0.0.0:3389 0.0.0.0:* LISTEN 12683/python
  8. tcp 0 0 0.0.0.0:8001 0.0.0.0:* LISTEN 12683/python
  9. tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN 12683/python
  10. tcp 0 0 0.0.0.0:9418 0.0.0.0:* LISTEN 12683/python
  11. tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 12683/python
  12. tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 12683/python
  13. udp 0 0 0.0.0.0:57197 0.0.0.0:* 8994/python
  14. udp 0 0 0.0.0.0:5060 0.0.0.0:* 12683/python
  15. udp 0 0 0.0.0.0:69 0.0.0.0:* 12683/python
  16. udp 0 0 0.0.0.0:123 0.0.0.0:* 12683/python
  17. 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

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

  1. [root@localhost opencanary]# ftp 192.168.0.6
  2. Connected to 192.168.0.6 (192.168.0.6).
  3. 220 FTP server ready
  4. Name (192.168.0.6:root): test
  5. 331 Password required for test.
  6. Password:
  7. 530 Sorry, Authentication failed.
  8. Login failed.
  9. 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 root@172.18.200.58

日志

{"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": ["1337@192.168.1.7"], "content-length": ["0"], "cseq": ["1 REGISTER"], "from": ["<sip:adminsip@192.168.1.7>"], "to": ["<sip:adminsip@192.168.1.7>"], "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服务器,作为数据接收端接受数据,后期对数据进行黑白名单处理,数据聚合处理。

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

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

下面这句话是写日志,

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

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

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

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

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

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

  1. "logger": {
  2. "class": "PyLogger",
  3. "kwargs": {
  4. "formatters": {
  5. "plain": {
  6. "format": "%(message)s"
  7. }
  8. },
  9. "handlers": {
  10. "console": {
  11. "class": "logging.StreamHandler",
  12. "stream": "ext://sys.stdout"
  13. },
  14. "file": {
  15. "class": "logging.FileHandler",
  16. "filename": "/var/tmp/opencanary.log"
  17. }
  18. }
  19. }
  20. },

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

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

下面是模块编写的模板。

  1. from opencanary.modules import CanaryService
  2. from twisted.internet.protocol import Protocol
  3. from twisted.internet.protocol import Factory
  4. from twisted.application import internet
  5. class Example0Protocol(Protocol):
  6. """
  7. Example (Fictional) Protocol
  8. $ nc localhost 8007
  9. Welcome!
  10. password: wrong0
  11. password: wrong1
  12. password: wrong2
  13. Bad passwords
  14. $
  15. """
  16. def __init__(self):
  17. self.prompts = 0
  18. def connectionMade(self):
  19. self.transport.write("Welcome!\r\npassword: ")
  20. self.prompts += 1
  21. def dataReceived(self, data):
  22. """
  23. Careful, data recieved here is unbuffered. See example1
  24. for how this can be better handled.
  25. """
  26. password = data.strip("\r\n")
  27. logdata = {"PASSWORD" : password}
  28. self.factory.log(logdata, transport=self.transport)
  29. if self.prompts < 3:
  30. self.transport.write("\r\npassword: ")
  31. self.prompts += 1
  32. else:
  33. self.transport.write("\r\nBad passwords\r\n")
  34. self.transport.loseConnection()
  35. class CanaryExample0(Factory, CanaryService):
  36. NAME = 'example0'
  37. protocol = Example0Protocol
  38. def __init__(self, config=None, logger=None):
  39. CanaryService.__init__(self, config, logger)
  40. self.port = 8007
  41. self.logtype = logger.LOG_BASE_EXAMPLE
  42. CanaryServiceFactory = CanaryExample0

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

  1. class DemoHandler(logging.Handler):
  2. def __init__(self, demo_userid, demo_authkey, allowed_ports):
  3. logging.Handler.__init__(self)
  4. self.demo_userid = str(demo_userid)
  5. self.demo_authkey = str(demo_authkey)
  6. try:
  7. # Extract the list of allowed ports
  8. self.allowed_ports = map(int, str(allowed_ports).split(','))
  9. except:
  10. # By default, report only port 22
  11. self.allowed_ports = [ 22 ]
  12. def emit(self, record):
  13. ...

再修改opencanary.conf配置文件。

  1. "logger": {
  2. "class" : "PyLogger",
  3. "kwargs" : {
  4. "formatters": {
  5. "plain": {
  6. "format": "%(message)s"
  7. }
  8. },
  9. "handlers": {
  10. "dshield": {
  11. "class": "opencanary.logger.DemoHandler",
  12. "demo_userid": "test",
  13. "demo_authkey": "$%$##$#%$%",
  14. "allowed_ports": "22,23"
  15. }
  16. }
  17. }
  18. }

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。

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

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

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加白名单。

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

0×08 总结:

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/603087
推荐阅读
相关标签
  

闽ICP备14008679号