2021SC@SDUSC
本周内容是上周的延续,在本周的文章里,完成了对 OSSIM 安全信息管理系统的 Frameworkd 模块的所有源码以及实现原理的分析。同时完成了 OSSIM 的日志分析部分的简要概述与分析。下周开始,将对 OSSIM 的关联分析机制进行源码分析。
这里主要实现的功能就是在 Framework 模块中有关数据库相关的操作
主要的类就是 OssimDB 类,这个类的主要实现如下:
首先导入所需的相关 python 自带模块
import sys
import time
from threading import Lock
try:
import MySQLdb
import MySQLdb.cursors
import _mysql_exceptions
except ImportError:
sys.exit("You need python MySQLdb module installed")
然后导入其他自定义的模块
from Logger import Logger
接下来就是具体类函数
__init__(self, host, database, user, password)
构造函数,初始化类相关属性
def __init__(self, host, database, user, password):
self._host = host
self._database = database
self._user = user
self._password = ""
if password is not None:
self._password = password
self._conn = None
self._connected = False
self._mutex = Lock()
connect(self)
数据库连接函数
def connect(self):
#先判断一下是否已经连接数据库
if self._connected:
return
调用 MySQLdb.connect 进行数据库连接相关操作,要求连接失败次数不能超过五次
self._connected = False
attempt = 1
while not self._connected and attempt <= 5:
try:
self._conn = MySQLdb.connect(host=self._host, user=self._user, passwd=self._password,db=self._database, cursorclass=MySQLdb.cursors.DictCursor)
self._conn.autocommit(True)
self._connected = True
except Exception, e:
logger.info("Can't connect to database (%s@%s) error: %s" % (self._user, self._host, e))
logger.info("Retry in 2 seconds...")
attempt += 1
time.sleep(2)
return self._connected
exec_query(self, query)
该函数的主要功能为执行数据库相关查询工作,仅执行有返回结果的 SQL 语句
传入参数为查询语句,返回值为查询的结果
def exec_query(self, query):
self._mutex.acquire()
arr = [] #返回的查询结果
max_retries = 3 #最大尝试次数
retries = 0 #尝试次数
retry_query = False
cursor = None
continue_working = True
先判断一下是否已经连接数据库,如果没有连接则调用刚才分析的self.connect()进行数据库连接,然后执行数据库查询操作,根据尝试次数来判断是否继续执行
while continue_working:
try:
#先判断一下是否已经连接数据库,如果没有连接则调用刚才分析的self.connect()进行数据库连接
if not self._connected or self._conn is None:
self.connect()
执行数据库查询操作
cursor = self._conn.cursor()
cursor.execute(query)
arr = cursor.fetchall()
continue_working = False
直接使得retries大于max_retries,然后在后面的代码中就会终止while循环,结束查询
retries = max_retries + 1
cursor.close()
#异常捕获
except _mysql_exceptions.OperationalError, (exc, msg):
logger.debug('MySQL Operational Error executing query:\n----> %s \n----> [(%d, %s)]' % (query, exc, msg))
if exc != 2006:
logger.error('MySQL Operational Error executing query')
self.__close()
except Exception, e:
logger.error('Error executing query:\n----> [{0}]'.format(e))
self.__close()
超过最大次数则停止while循环
if retries >= max_retries:
continue_working = False
else:
retries += 1
time.sleep(1)
self._mutex.release()
if not arr:
arr = []
return arr
execute_non_query(self, query, autocommit=False)
:
该函数的主要功能是用于执行没有查询结果返回的语句。
变量 autocommit
的值表示是否自动提交,默认情况下,这个值设为 true,即自动提交。
在执行完 SQL 语句后,if 判断是否是自动提交,如果不是自动提交,再调用函数 self._conn.commit()
实现提交。
同样也设置了最多尝试次数 max_retries
,其值初始为3,用于判断是否继续进行 while 循环。
返回值 returnvalue
表示是否执行成功,成功返回 true,否则返回 false
def execute_non_query(self, query, autocommit=False):
self._mutex.acquire()
max_retries = 3
retries = 0
retry_query = False
cursor = None
continue_working = True
returnvalue = False
self._conn.autocommit(autocommit)
while continue_working:
try:
if not self._connected or self._conn is None:
self.connect()
cursor = self._conn.cursor()
cursor.execute(query)
if not autocommit:
self._conn.commit()
continue_working = False
retries = max_retries + 1
cursor.close()
returnvalue = True
except _mysql_exceptions.OperationalError, e:
logger.error('Operation Error:\n%s\n[%s]' % (query, e))
self._conn.rollback()
self.__close()
returnvalue = False
except Exception, e:
logger.error('Error executing query:\n %s \n [%s]' % (query, e))
self._conn.rollback()
self.__close()
returnvalue = False
if retries >= max_retries:
continue_working = False
else:
retries += 1
time.sleep(1)
self._mutex.release()
return returnvalue
def __close(self)
:
最后一个函数的主要功能是调用self._conn.close()函数,断开数据库连接
def __close(self):
if self._conn:
try:
self._conn.close()
except _mysql_exceptions.ProgrammingError as err:
logger.error("Error while closing the connection: {}".format(err))
except AttributeError as err:
logger.info("Trying to close the connection to mysql: {}".format(err))
except Exception as err:
logger.error("Can't close the connection to the database: {}".format(err))
finally:
self._conn = None
self._connected = False
else:
logger.info("Trying to execute close method on connection which doesn't exist")
数据是企业的财富,同样日志蕴含着丰富的信息。Ping 命令是再平常不过的网管工具了,使用 ICMP 来 ping 对方主机,就知道是否存活。但在故障时,能够 ping 通对方的网络接口,不代表机器正常,也有可能系统已出现故障,这样就需要从日志中获取详细信息。日志不仅告诉你主机是否存活,还能够告诉你主机上运行应用程序的状态,以及它们在做什么,在真正宕机前发出各种日志报警信息。下面是一个主机遭受 SSH 攻击的日志,然后我们对其信息进行分析 :
Sep 17 07:00:02 webserver.com:sshd[20392]:Failed password for illegal user test from 192.168.11.2 port 33783 ssh2
从该日志可以看出,用户 test 登录失败,而且他是非法用户,这条消息表明,一个攻击者使用扫描器探测服务器。试图利用字典里的用户进行试探,但攻击还没成功。
当然,一旦攻击成功,黑客接管主机后完全有可能删除这条日志(为了避免日志被删除,可以将这条日志发送到远程的日志收集服务器集中存储)。接着看下面的日志。
Sep 17 10:09:10 webserver.com adduser [2341]: new user: name=test,uid=0,gid=0,home=/root/cgi,shell=/bin/bash
很显然,从这条消息看出,系统添加了系统管理员账户名称为 test,用户 ID 为 0 (相当于 root 权限用户)。
用户行为会被日志记录下来,比如用户登录、注销和启动进程等,系统审计工具能提供细颗粒的日志记录,而且这种日志一旦被系统记录并收集之后,就不会因系统故障恢复正常而被修改,也就是说日志记录了系统从正常→故障→正常的完整过程,由于每条日志都有时间戳,它们提供了每个事件的时间顺序,日志不仅告诉我们发生了什么,还告诉我们事件发生的时间和顺序,而且日志发送到日志服务器后也提供了独立的日志收集仓库,一旦原始主机上的日志遭到破坏(比如篡改和删除),独立的日志搜集服务就是可靠的日志附加来源。
例如,在 Linux 中普通用户通过 sudo 来执行管理员命令,无意间删除了重要的系统文件而导致故障,在取证时,sudo日志提供了取证日志。又如,用户张三在很短的时间内,从地域跨度很大的两个不同的位置,用 SSH 方式登录系统,这种异常行为就非常可疑。
在 Unix/Linux 中内置了强大的命令行日志分析工具,例如 grep、awk、tail、sed 等,用它们分析简单的日志没有问题,然而随着日志尺寸的不断增大,这种分析工具也暴露出一些问题:
(1)不同日志之间无法关联,用人工分析法无法同时分析来自不同网络设备的日志,更无法搞清楚整个网络攻击是如何发生发展的。
(2)在超过 1 GB大小的日志中检索信息非常困难。
(3)Grep 这样的工具只能对明确的关键字信息检索,无法生成统计趋势和异常行为。
以上问题都可以在 OSSIM 系统的日志分析平台中得到解决。
一个海量日志收集系统,首先要考虑采用什么样的日志采集模型。一般有两种方式,推送(PUSH)方式和拉(PULL)方式。我们常见的 Syslog、Rsyslog 就是采用推送(PUSH)方式,再比如 Facebook、Scribe 也采用了 PUSH 方式,这种方式要求日志收集存储容量大,至少要大于峰值时的数据生成量,否则主动推送过来的数据将来不及处理,另外如果设置不正确,会收到虚假的 syslog 信息,所以必须了解哪些设备启用了syslog。而拉(PULL)方式的日志收集并不常用。
上一篇:OSSIM开源安全信息管理系统(十二)
下一篇: