商业日志管理系统发展史
* 原创作者:bt0sea
0×00、前言
日志管理系统是信息安全发展过程中一直存在的业务系统,日志是安全的传感器,所以很有必要研究一下日志管理系统发展历史,以及以日志管理系统发展趋势。在文章的最后一段分享一下以前和一帮朋友做的日志管理系统。
0×01、商业日志管理系统发展历史
发展趋势
根据用户企业环境使用的产品以及各大厂商宣传的产品功能,大致可以推断出目前日志管理发展趋势。
第一阶段:SIEM安全日志管理系统 (security information and event management)
1,针对第三方安全产品日志收集能力
日志管理系统做为安全的传感器,需要对任何第三方安全的产品的日志具备灵活的收集能力,某些厂商会开发定制化的收集器,并且公开定制工具。
2,灵活的扩展存储处理能力,
由于日志系统需要收集海量的日志,在关联前后需要使用大量的存储空间,所以灵活的存储能力是必要的。
3,事件多级关联能力,
通过多种模式匹配方式关联基础事件,可以提高威胁事故告警的准确性。
4,快速查询报告能力
快速在海量日志中搜索查询到我们关心的日志信息,反应速度是用户最关心的问题。
第二阶段:MSS(ManagedSecurity Services)
1,提升APT发现能力
MSSP不仅能够降低客户的成本,还能够增强客户的安全能力防护APT攻击,传统提交病毒样本的处理方式无法满足现有的渗透攻击尤其是基于特征的技术(Signature-baseTechnology)已经难以对抗安全威胁
2,集成威胁情报,
威胁情报和事件关联成为了MSSP的热门技术和差异化技术,例如:DNS/URL信誉数据库、文件信誉数据库、电子邮件信誉数据库等。
第三阶段:安全大数据日志管理系统
1,提升APT发现能力,
通过大数据快速处理的能力,在短时间内通过关联分析发现企业内部威胁。
2,快速追溯威胁来源
成熟的商业产品
那么,市场上的玩家都有哪些?
Gartner发布了2015年度的SIEM市场分析报告
1,IBM收购Q1 Lab
2,HP Arcsight
3,Splunk
4,RSA EnVersion
5,Symantec SSIM
6,Intel Security(MacAfee-Enterprise Log Manager)
Garnter发布了2014年度的全球MSS市场分析报告
1,Dell – SecureWorks(IT综合服务提供商)
2,IBM-Fireeye Service/AT&T Service
3,Verizon(电信运营商)
4,Symantec(专业安全厂商)
大数据日志管理系统目前正处在崛起的阶段。目前各大互联网安全公司都在在这个领域下重金研发。
用户场景分析
在哪些场景会使用到日志管理系统呢?这要回归到基本安全管理问题:做为一家互联网公司的CSO,你怎么履行你的安全监控和管理职能?
1,简单使用一些开源工具,招聘一些有安全经验的运维人员做人工分析
Do Nothing –“Manual Analysis”
2,使用成熟的互联网安全商业软件 (传统厂商SEIM系统或者新兴互联网安全系统),少量的具备一般安全经验的运维人员熟练使用这些安全软件完成。SIEM
3,直接把安全外包给MSSP,由专业的公司分析安全威胁,只需要定期从MSSP厂商获得本公司 的安全报告。
国内大部分互联网公司,还是选择第二条路。
0×02、日志管理系统实践
产品架构设计
集中式部署
集中式部署
如果被监管的设备不多而且相对集中,可以采用上图所示的集中式部署实现IT系统的安全信息监控。
分布式部署
分布式部署
如果企业或组织内的IT架构比较复杂,而且监管的设备数目较多,可以采用上图所示的分布式部署方式。
支持的日志类型如下:
GSGSoftInformation Security Management System
管理配置报表模块:ASP.net+IIS+MSSQL
日志收集系统:应用程序+MSSQL
(1)GSGSoftManage 主要的功能 安全事件联动引擎
(2)GSGSoft Master Collection Agent 主动收集安全事件
(3)GSGSoft Slave Collection Agent 被动收集安全事件客户端(主要针对无法主动发送日志情况)
(4)Web Console 主要是安全事件的配置、告警和报表系统
(5)GSGSoft DataBase 安全事件存储数据库
GSGSoft Master Collection Agent功能设计
主动收集安全事件 对其标准化。针对Unix/Linux系统 建议用户打开系统syslog功能
通过syslog收集。
(1)Syslog Server 收集日志模块
(2)snmp trap 日志收集模块
(4)正则表达式查找匹配功能模块
(5)格式化原始事件为本系统识别事件模块
(6)本地存储结构模块
GSGSoft Slave Collection Agent 功能设计
被动收集安全事件客户端
(1)文件、文件夹日志收集模块
1.1 windows 日志收集模块system/security/applcation
1.2 IIS 日志收集模块
1.3 Apache 日志收集模块
(2)xml、csv文件收集模块
2.1 antivirus 扫描日志收集模块 (symantec 趋势 瑞星)
GSGSoft Web Console功能设计
主要是显示安全事件的平台,配置系统
(1)图形化报警功能
(2)灵活的SQL语句的构造系统,可产生多个自定义报表模块
(3)策略设置
GSGSoft Web Console详细设计
(1)图形化报警功能
在系统总览面板-图形化报警,功能设计:如果某个特定的区域发现符合报警条件的情况,则产生告警。并且相应的图形显示为红色扩散状的小球。
系统默认用户为admin,密码为123456,登录后建议管理员修改管理员密码。成功登录后,进入系统的主界面。如下图所示:
当点击相应的红色小球,则显示具体的报警信息,如图:
同时可以进入详细页面查看原始日志和标准化后的报警日志。
查看完报警信息后小球的颜色恢复到原来的蓝色,证明报警信息被网管人员处理过。如图:
图形化报警功能特别适合网管监控大屏幕显示,让安全事件发生源迅速形象的映入网管人员的视野。
报表功能
在安全报表中显示如图:
设置安全报表查询条件,包括:开始结束时间、扫描条数、是否有图形显示、显示图形的X轴和Y轴。
安全报表显示如图:
策略设置功能
如何进行策略创建和分发
在“规则和策略”中左边的策略项中可点击正则表达式配置。
点击其中的“对象”弹出如下:
数据库设计
(1)系统登陆表GSGSoft_System
(3)Root 表Common Event Format
GSGSoft服务器核心代码实现
编程工具:VC++
平台:Windows
技术选型:IOCP架构 (Windows服务器要接收海量的日志一般技术架构是无法支持的)
IocpUdpInstance
IocpUdpInstance处理接收syslog UDP 54端口传输过来的日志数据。代码实现如下:
#include"stdafx.h"
#include"IocpUdpInstance.h"
//
//Construction/Destruction
//
CIocpUdpInstance::CIocpUdpInstance()
{
m_Port = 5353;
m_Inited = FALSE;
Lpbuf = m_buf;
m_BufLen = BUF_LEN;
m_FromLen = sizeof(SOCKADDR_IN);
}
CIocpUdpInstance::~CIocpUdpInstance()
{
Cleanup();
}
BOOLCIocpUdpInstance::Initialize()
{
if(LpParent == NULL) return FALSE;
if(m_Inited) return TRUE;
Lpbuf = m_buf;
m_BufLen= BUF_LEN;
SOCKET s = INVALID_SOCKET;
DWORD m_Result=0; int nRet=0;
//创建SOCKET并建立侦听
if(!m_Socket.CreateSocket(SOCK_DGRAM,IPPROTO_UDP,WSA_FLAG_OVERLAPPED))
goto EXIT;
if(!m_Socket.Bind(m_Port))
goto EXIT;
m_Inited = TRUE;
return TRUE;
EXIT:
Cleanup();
return FALSE;
}
BOOLCIocpUdpInstance::Doit()
{
if(LpParent == NULL ||!Initialize())
return FALSE;
//帮定到IO完成端口
SOCKET s = m_Socket;
if(!LpParent->Invoke(FN_IOCP_BIND,this,(HANDLE)s))
return FALSE;
RecvHandler(0);
return TRUE;
}
voidCIocpUdpInstance::Cleanup()
{
m_Inited = FALSE;
m_Socket.Cleanup();
}
voidCIocpUdpInstance::Release()
{
}
BOOLCIocpUdpInstance::SetValue(DWORD m_Code,...)
{
va_list vl,va_base;
va_start(vl,m_Code);
va_base = (va_list)&m_Code;
if(m_Code == BASE_VALIST)
{
vl = va_arg(vl,va_list);
va_base = vl;
m_Code =va_arg(vl,DWORD);
}
BOOL m_Resoult=TRUE;
switch(m_Code)
{
case IOCP_BUFFER:
{
Lpbuf = va_arg(vl,LPSTR);
m_BufLen=va_arg(vl,LONG);
break;
}
case SOCK_PORT:
{
m_Port=va_arg(vl,int);
break;
}
default:
{
m_Resoult=FALSE;
}
}
if(!m_Resoult)//子类没有处理则调用父类的
{
m_Resoult=CBase::SetValue(BASE_VALIST,va_base);
}
va_end(va_base);
va_end(vl);
return m_Resoult;
}
BOOLCIocpUdpInstance::GetValue(DWORD m_Code,...)
{
va_list vl,va_base;
va_start(vl,m_Code);
va_base = (va_list)&m_Code;
if(m_Code == BASE_VALIST)
{
vl = va_arg(vl,va_list);
va_base = vl;
m_Code =va_arg(vl,DWORD);
}
BOOL m_Resoult=TRUE;
switch(m_Code)
{
case IOCP_HANDLER:
{
LPWSAOVERLAPPEDLpolap = va_arg(vl,LPWSAOVERLAPPED);
CBase**LpBase = va_arg(vl,CBase**); *LpBase = NULL;
*LpBase =CONTAINING_RECORD(Lpolap, CIocpUdpInstance, m_olap);
break;
}
case BASE_OBJECT:
{
*(va_arg(vl,HANDLE*))=(HANDLE)(SOCKET)m_Socket;
break;
}
case SOCK_PORT:
{
*(va_arg(vl,int*))=m_Port;
break;
}
default:
{
m_Resoult=FALSE;
}
}
if(!m_Resoult)//子类没有处理则调用父类的
{
m_Resoult=CBase::GetValue(BASE_VALIST,va_base);
}
va_end(va_base); va_end(vl);
return m_Resoult;
}
DWORDCIocpUdpInstance::Invoke(DWORD m_Code,...)
{
va_list vl;
va_start(vl,m_Code);
DWORD m_Resoult = TRUE;
switch(m_Code)
{
case FN_IOCP_HANDLER:
{
DWORDm_szTrans = va_arg(vl,DWORD);
RecvHandler(m_szTrans);
break;
}
default:
{
m_Resoult=FALSE;
}
}
va_end(vl);
return m_Resoult;
}
BOOLCIocpUdpInstance::RecvHandler(DWORD m_szTrans)
{
if(m_szTrans > 0 &&LpContainer)
{
CBase *t_Tmp =LpContainer->Alloc();
if(t_Tmp)
{
t_Tmp->Initialize();
t_Tmp->SetValue(BASE_OBJECT,&m_Socket);
t_Tmp->SetValue(BASE_PARENT,(CBase*)this);
t_Tmp->SetValue(BASE_HANDLER,LpHandler);
if(!t_Tmp->Invoke(FN_INVOKE,Lpbuf, m_szTrans))
{
if(!t_Tmp->Recycle())t_Tmp->Release();
}
}
}
INITBUF(m_WBuf,Lpbuf,m_BufLen);
memset(&m_olap,0,sizeof(m_olap));m_FromLen=sizeof(SOCKADDR_IN);
int nRet =m_Socket.RecvEx(&m_WBuf,0,(sockaddr*)&m_SockAddr,&m_FromLen,&m_olap);
if(nRet==SOCKET_ERROR)
{
DWORD m_Result =WSAGetLastError();
if(m_Result !=ERROR_IO_PENDING)
{
returnFALSE;
}
}
return TRUE;
}
#include"StdAfx.h"
#include"IocpUdpHandler.h"
CIocpUdpHandler::CIocpUdpHandler(void)
{
hIocp = NULL;
m_Policy_p = NULL;
}
CIocpUdpHandler::~CIocpUdpHandler(void)
{
}
BOOLCIocpUdpHandler::Initialize()
{
if(LpParent == NULL)
return FALSE;
CBase *t_Base = NULL; hIocp = NULL;
LpParent->GetValue(BASE_PARENT,&t_Base);
if(t_Base) //CIocpBase
{
t_Base->GetValue(BASE_OBJECT,&hIocp);
// CAppInstance
if(t_Base->GetValue(BASE_PARENT,&t_Base) && t_Base)
{
t_Base->GetValue(BASE_OBJECT,OBJECT_POLICY, &m_Policy_p);
}
}
return (hIocp &&m_Policy_p);
}
voidCIocpUdpHandler::Cleanup()
{
m_Event.clear();
}
BOOLCIocpUdpHandler::GetValue(DWORD m_Code,...)
{
va_list vl,va_base;
va_start(vl,m_Code);
va_base = (va_list)&m_Code;
if(m_Code == BASE_VALIST)
{
vl = va_arg(vl,va_list);
va_base = vl;
m_Code =va_arg(vl,DWORD);
}
BOOL m_Resoult=TRUE;
switch(m_Code)
{
case BASE_CLASSTYPE:
{
*(va_arg(vl,DWORD*))=0x00000019;
break;
}
default:
{
m_Resoult=FALSE;
}
}
if(!m_Resoult)
{
m_Resoult=CBase::GetValue(BASE_VALIST,va_base);
}
va_end(va_base); va_end(vl);
return m_Resoult;
}
DWORDCIocpUdpHandler::Invoke(DWORD m_Code,...)
{
va_list vl;
va_start(vl, m_Code);
DWORD m_Resoult = TRUE;
switch(m_Code)
{
case FN_INVOKE:
{
m_Event = va_arg(vl, LPCSTR);
ULONG t_Size= va_arg(vl, ULONG);
m_Resoult =PostQueuedCompletionStatus(hIocp, t_Size, (ULONG_PTR)this, NULL);
break;
}
case FN_IOCP_HANDLER:
{
// returnFALSE 不能加 break;
if(m_Policy_p)
{
std::stringt_Event_o;
if(m_Policy_p->GetValue(BASE_AVAILABLE,&m_Event, &t_Event_o))
{
LpHandler->Invoke(ITEM_ADD,&t_Event_o);
}
}
}
default:
{
m_Resoult =FALSE;
}
}
va_end(vl);
return m_Resoult;
}
IocpUdpHandler
IocpUdpHandler处理接收过来的数据,实现代码如下:
#include"StdAfx.h"
#include"IocpUdpHandler.h"
CIocpUdpHandler::CIocpUdpHandler(void)
{
hIocp = NULL;
m_Policy_p = NULL;
}
CIocpUdpHandler::~CIocpUdpHandler(void)
{
}
BOOLCIocpUdpHandler::Initialize()
{
if(LpParent == NULL)
return FALSE;
CBase *t_Base = NULL; hIocp = NULL;
LpParent->GetValue(BASE_PARENT,&t_Base);
if(t_Base) //CIocpBase
{
t_Base->GetValue(BASE_OBJECT,&hIocp);
// CAppInstance
if(t_Base->GetValue(BASE_PARENT,&t_Base) && t_Base)
{
t_Base->GetValue(BASE_OBJECT,OBJECT_POLICY, &m_Policy_p);
}
}
return (hIocp &&m_Policy_p);
}
voidCIocpUdpHandler::Cleanup()
{
m_Event.clear();
}
BOOLCIocpUdpHandler::GetValue(DWORD m_Code,...)
{
va_list vl,va_base;
va_start(vl,m_Code);
va_base = (va_list)&m_Code;
if(m_Code == BASE_VALIST)
{
vl = va_arg(vl,va_list);
va_base = vl;
m_Code =va_arg(vl,DWORD);
}
BOOL m_Resoult=TRUE;
switch(m_Code)
{
case BASE_CLASSTYPE:
{
*(va_arg(vl,DWORD*))=0x00000019;
break;
}
default:
{
m_Resoult=FALSE;
}
}
if(!m_Resoult)
{
m_Resoult=CBase::GetValue(BASE_VALIST,va_base);
}
va_end(va_base); va_end(vl);
return m_Resoult;
}
DWORDCIocpUdpHandler::Invoke(DWORD m_Code,...)
{
va_list vl;
va_start(vl, m_Code);
DWORD m_Resoult = TRUE;
switch(m_Code)
{
case FN_INVOKE:
{
m_Event = va_arg(vl, LPCSTR);
ULONG t_Size= va_arg(vl, ULONG);
m_Resoult =PostQueuedCompletionStatus(hIocp, t_Size, (ULONG_PTR)this, NULL);
break;
}
case FN_IOCP_HANDLER:
{
// returnFALSE 不能加 break;
if(m_Policy_p)
{
std::stringt_Event_o;
if(m_Policy_p->GetValue(BASE_AVAILABLE,&m_Event, &t_Event_o))
{
LpHandler->Invoke(ITEM_ADD,&t_Event_o);
}
}
}
default:
{
m_Resoult =FALSE;
}
}
va_end(vl);
return m_Resoult;
}
Policys正则匹配
CPolicys::CPolicys(void)
{
m_EventHandler = NULL;
}
CPolicys::~CPolicys(void)
{
}
BOOLCPolicys::Initialize()
{
if(LpParent == NULL)
return FALSE;
std::string t_FileName, t_Tmp;
LpParent->GetValue(BASE_FILENAME,&t_FileName);
t_FileName += POLICY_NAME;
LpParent->GetValue(BASE_OBJECT,BASE_EVENT, &m_EventHandler);
HANDLE hFile =CreateFile(t_FileName.c_str(), GENERIC_READ,
FILE_SHARE_READ |FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
ULONG t_Size = GetFileSize(hFile, NULL);
t_Tmp.resize(t_Size);
if(ReadFile(hFile,(LPSTR)t_Tmp.c_str(), t_Size, &t_Size, NULL))
{
m_Policys.clear();
CWorkSpace::XmlContextt_Context;
t_Context.m_Type = XML_WORK_GROUP;
t_Context.LpGroup = &m_Policys;
t_Context.LpMitem = NULL;
SplitXml(t_Tmp.c_str(),"<policy>", "</policy>", "<item ","/>", XmlCallback, &t_Context);
}
CloseHandle(hFile);
return TRUE;
}
void WINAPICPolicys::XmlCallback(LPCSTR LpStr, size_t nLen, LPVOID m_Context)
{
if(LpStr == NULL || nLen == 0 ||m_Context == NULL)
return;
std::string t_Tmp;
CStringParse m_Parse;
m_Parse.SetBuffer(LpStr, nLen);
m_Parse.SetPartition(NULL,"\" ");
m_Parse.SetTrim('"');
m_Parse.Parse();
CPolicy t_Policy;
t_Tmp =m_Parse.GetValue("pid");
if(t_Tmp.empty())
return;
t_Policy.SetPolicyID(ATOUL(t_Tmp.c_str()));
t_Tmp =m_Parse.GetValue("eventid");
t_Policy.SetEventID(ATOUL(t_Tmp.c_str()));
t_Tmp =m_Parse.GetValue("name");
t_Policy.SetName(t_Tmp);
t_Tmp =m_Parse.GetValue("field");
t_Policy.SetFields(t_Tmp);
t_Tmp =m_Parse.GetValue("table");
t_Policy.SetTable(t_Tmp);
t_Tmp =m_Parse.GetValue("regexmatch");
t_Policy.SetRegexMatch(t_Tmp);
t_Tmp =m_Parse.GetValue("regexsplit");
t_Policy.SetRegexSplit(t_Tmp);
CWorkSpace::XmlContext *t_Ctx_p;
t_Ctx_p =(CWorkSpace::XmlContext*)m_Context;
POLICY *t_Policy_p =(POLICY*)t_Ctx_p->LpGroup;
if(t_Policy_p)
{
t_Policy_p->insert(POLICY_PAIR(t_Policy.GetPolicyID(),t_Policy));
}
}
BOOLCPolicys::GetValue(DWORD m_Code,...)
{
va_list vl,va_base;
va_start(vl,m_Code);
va_base = (va_list)&m_Code;
if(m_Code == BASE_VALIST)
{
vl = va_arg(vl,va_list);
va_base = vl;
m_Code =va_arg(vl,DWORD);
}
BOOL t_Resoult = TRUE;
switch(m_Code)
{
case BASE_AVAILABLE:
{
t_Resoult =FALSE;
std::string*t_Event_i, *t_Event_o;
t_Event_i =va_arg(vl,std::string*);
t_Event_o =va_arg(vl,std::string*);
if(t_Event_i== NULL || t_Event_o == NULL)
break;
t_Event_o->clear();
CLockSectiont_Lock(m_Section);
POLICY_ITERitra = m_Policys.begin();
for(; itra!= m_Policys.end(); itra++)
{
std::stringt_Match, t_Split;
t_Match= itra->second.GetRegexMatch();
t_Split= itra->second.GetRegexSplit();
if(t_Match.empty()|| t_Split.empty())
{
continue;
}
//正则表达式是否匹配
boost::regexexpression(t_Match.c_str());
boost::cmatchwhat;
if(boost::regex_match(t_Event_i->c_str(),what, expression))
{
t_Lock.UnLock();t_Resoult = TRUE;
if(m_EventHandler&& g_App.GetViewEvent()) // 加入到显示队列
{
m_EventHandler->Invoke(ITEM_ADD,itra->second.GetEventID(), t_Event_i);
}
//用正则表达式进行解析
boost::regexe(t_Split.c_str());
FIELDl; FIELDITER itrb;
boost::regex_split(std::back_inserter(l),*t_Event_i, e);
for(itrb= l.begin(); itrb != l.end(); itrb++)
{
if(!t_Event_o->empty())
{
*t_Event_o+= ',';
}
*t_Event_o+= '\"';
*t_Event_o+= *itrb;
*t_Event_o+= '\"';
}
break;
}
}
break;
}
default:
{
t_Resoult=FALSE;
}
}
if(!t_Resoult)//子类没有处理则调用父类的
{
t_Resoult =CBase::GetValue(BASE_VALIST,va_base);
}
va_end(va_base); va_end(vl);
return t_Resoult;
}
服务器端处理
(1)、首先通过解析DeviceIdentifier.xml,首先确定是否为支持的设备,支持的设备可以通过xml看出来。代码如下:
<?xmlversion="1.0" encoding="UTF-8"?>
<!-- 全局参数放在这里,如table,SyslogAddress,timefield-->
<DeviceIdentifiertable="GSGSoft_EventRoot" SyslogAddress="Device_IP"timefield="EventTime">
<!-- 策略文件路径 Mapping/DeviceSupplier/DeviceProductName/DeviceVersion/FieldMapping.xml(优先) 或Mapping/DeviceSupplier/DeviceProductName/FieldMapping.xml -->
<Device id="0"name="WebService" eventid="2" seventid="3"DeviceSupplier="Microsoft" DeviceProductName="InternetInformation Server" DeviceVersion="6.0">
<!-- 匹配规则 <![CDATA[这里填写的字符串是不需要转义的]]> -->
<RegexMatcher>
<![CDATA[^\d\d\d\d-\d\d-\d\d\d\d\:\d\d\:\d\d \S+ \d+\.\d+\.\d+\.\d+ \S+ \S+ \S+ \d+ \S+ \d+\.\d+\.\d+\.\d+\S+ \d+ \d+ \d+$]]>
</RegexMatcher>
<LogSample>
<![CDATA[2008-06-04 07:43:41W3SVC1846873709 10.1.3.3 GET /x.asp URL=http://www.google.cn 80 - 10.1.3.3Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.2;+SV1;+.NET+CLR+1.1.4322;+.NET+CLR+2.0.50727;+.NET+CLR+3.0.04506.30;+.NET+CLR+3.0.04506.648)200 0 0]]>
</LogSample>
</Device>
<Device id="2"name="NetWork" eventid="2" seventid="3"DeviceSupplier="Cisco" DeviceProductName="CiscoRouter"DeviceVersion="2500">
<RegexMatcher>
<![CDATA[^<\d+>.*?\:\%\w+\-\d+\-\w+\: [\s|\S]+$]]>
</RegexMatcher>
<LogSample>
<![CDATA[<173>33:*Mar 1 04:53:37.494 UTC:%SYS-5-CONFIG_I: Configured from console by console (192.168.1.100)]]>
</LogSample>
</Device>
<Device id="4"name="OS" eventid="2" seventid="3"DeviceSupplier="Microsoft" DeviceProductName="Windows"DeviceVersion="2003">
<RegexMatcher>
<![CDATA[^.*\S+\sMSWinEventLog\s\d+\s\w+\s\d+.*$]]>
</RegexMatcher>
<LogSample>
<![CDATA[2009-08-15 17:12:47 Local7.Debug 127.0.0.1 vmMSWinEventLog 0 Security 5 Sat Aug 15 17:12:47 2009 592 Security AdministratorUser Success Audit VM 详细追踪 已经创建新的过程: 新的进程 ID: 1456 映像文件名:C:\WINDOWS\system32\cmd.exe 创建者进程 ID: 1280 用户名: Administrator 域: VM 登录 ID:(0x0,0x14FE2) 0]]>
</LogSample>
</Device>
<Device id="6"name="SEP" eventid="2" seventid="3"DeviceSupplier="Symantec" DeviceProductName="EndPointProtection" DeviceVersion="2009">
<RegexMatcher>
<![CDATA[^<\d+>.*SymantecServer \S+:.*$]]>
</RegexMatcher>
<LogSample>
<![CDATA[<54>三月 29 18:32:12 SymantecServer sepserver:DellXP708-33,检测到 [SID: 21590] P2P Edonkey PingMessage。 已允许来自此应用程序的通信: C:\ProgramFiles\Tencent\QQDownload2\QQDownload.exe,本地: 0.0.0.0,本地: 000000000000,远程: ,远程:129.47.150.11,远程: 000000000000,2,其他,Intrusion ID: 0,开始: 2009-03-29 17:35:17,结束: 2009-03-2917:35:17,出现次数: 1,应用程序: C:/Program Files/Tencent/QQDownload2/QQDownload.exe,位置: 默认值,用户: Administrator,域: WORKGROUP]]>
</LogSample>
</Device>
<Device id="10000"name="SYSLOG" eventid="2" seventid="3"DeviceSupplier="Syslog" DeviceProductName="Syslog">
<!-- 最后一条规则,确保日志能够收集 -->
<RegexMatcher>
<![CDATA[^.*$]]>
</RegexMatcher>
<LogSample>
<!-- 日志样本 -->
<![CDATA[This is a testmessage]]>
</LogSample>
</Device>
</DeviceIdentifier>
(2)第二步进入每个详细类型设备支持的正则。
Mapping/DeviceSupplier/DeviceProductName/DeviceVersion/FieldMapping.xml
这里我就介绍一个最复杂的Symantec Endpoint Protection正则匹配,首先先解释一下xml文件。
<policy>
<itempid="2" name="CISCO" // 数据库字段:DeviceGroup 设备类型
subname="trans" // 数据库字段:DealDeviceGroup 日志类型 例如:流量日志
regexmatch="1\w*"//正则校验式
regexsplit="^1(\d)(\d)(\d)(\w*)"//正则表达式
table="event"//存储事件表 不允许改
field="name,id,time"// 解析字段 需要编辑的内容:name,id,time
/>
</policy>
详细可以解析的日志类型
<?xmlversion="1.0" encoding="UTF-8"?>
<MappingRegexDescription="Microsoft/Windows/2003">
<!-- Description 是给策略文件阅读者看的,程序不需要使用-->
<Item id="0">
<!-- 管理员登录成功 -->
<RegexMatcher>
<![CDATA[^<\d+>.*SymantecServer \S+: 站点: .*?,服务器: .*,域: .*,管理员: .*,管理员登录成功.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: 站点: (.*?),服务器: (.*),域: (.*),管理员: (.*),(管理员登录成功).*$]]>
</RegexSplit>
<Fields count="5" >
<![CDATA[CS1,DestinationName,DestinationDomainName,DestinationUserName,EventName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="2">
<!-- 注销管理员 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: 站点: .*?,服务器: .*,域: .*,管理员: .*,注销管理员.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: 站点: (.*?),服务器: (.*),域: (.*),管理员: (.*),(注销管理员).*$]]>
</RegexSplit>
<Fields count="5" >
<![CDATA[CS1,DestinationName,DestinationDomainName,DestinationUserName,EventName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="4">
<!-- 发现病毒 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: 发现病毒,计算机名: .*,源: .*,风险名称: .*,出现次数: \d+,.*,.*,实际的操作: .*,请求的操作: .*,次要操作: .*,事件时间: .*,已插入: .*,结束: .*,域: .*,组: .*,服务器: .*,用户: .*,源计算机: .*,源 IP: .*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (发现病毒),计算机名: (.*),源: (.*),风险名称: (.*),出现次数: (\d+),(.*),.*,实际的操作: (.*),请求的操作: .*,次要操作: .*,事件时间: .*,已插入: .*,结束: .*,域: (.*),组: (.*),服务器: (.*),用户: (.*),源计算机: (.*),源 IP: (.*)$]]>
</RegexSplit>
<Fields count="13" >
<![CDATA[EventName,Device_Name,CS6,CS2,CN1,FileName,DeviceAction,DestinationDomainName,CS4,CS1,SourceUserName,SourceName,SourceIP]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="6">
<!-- 发现安全风险 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: 发现安全风险,计算机名: .*,源: .*,风险名称: .*,出现次数: \d+,.*,.*,实际的操作: .*,请求的操作: .*,次要操作: .*,事件时间: .*,已插入: .*,结束: .*,域: .*,组: .*,服务器: .*,用户: .*,源计算机: .*,源 IP: .*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer\S+: (发现安全风险),计算机名: (.*),源: (.*),风险名称: (.*),出现次数: (\d+),(.*),.*,实际的操作: (.*),请求的操作: .*,次要操作: .*,事件时间: .*,已插入: .*,结束: .*,域: (.*),组: (.*),服务器: (.*),用户: (.*),源计算机: (.*),源 IP: (.*)$]]>
</RegexSplit>
<Fields count="13" >
<![CDATA[EventName,Device_Name,CS6,CS2,CN1,FileName,DeviceAction,DestinationDomainName,CS4,CS1,SourceUserName,SourceName,SourceIP]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="8">
<!-- 风险样例已提交至 Symantec -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: 风险样例已提交至 Symantec,计算机名: .*,源: .*,风险名称: .*,出现次数: \d+,.*,.*,实际的操作: .*,请求的操作: .*,次要操作: .*,事件时间: .*,已插入: .*,结束: .*,域: .*,组: .*,服务器: .*,用户: .*,源计算机: .*,源 IP: .*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (风险样例已提交至 Symantec),计算机名: (.*),源: (.*),风险名称: (.*),出现次数: (\d+),(.*),.*,实际的操作: (.*),请求的操作: .*,次要操作: .*,事件时间: .*,已插入: .*,结束: .*,域: (.*),组: (.*),服务器: (.*),用户: (.*),源计算机: (.*),源 IP: (.*)$]]>
</RegexSplit>
<Fields count="13" >
<![CDATA[EventName,Device_Name,CS6,CS2,CN1,FileName,DeviceAction,DestinationDomainName,CS4,CS1,SourceUserName,SourceName,SourceIP]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="10">
<!-- 客户端已成功下载策略 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: 站点: .*,服务器: .*,域: .*,客户端已成功下载策略,.*?,.*?,.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: 站点: (.*),服务器: (.*),域: (.*),(客户端已成功下载策略),(.*?),(.*?),(.*)$]]>
</RegexSplit>
<Fields count="7" >
<![CDATA[CS1,DestinationName,DestinationDomainName,EventName,DeviceHostName,SourceUserName,SourceDomainName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="12">
<!-- 客户端已成功下载内容软件包 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: 站点: .*,服务器: .*,域: .*,客户端已成功下载内容软件包,.*?,.*?,.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: 站点: (.*),服务器: (.*),域: (.*),(客户端已成功下载内容软件包),(.*?),(.*?),(.*)$]]>
</RegexSplit>
<Fields count="7" >
<![CDATA[CS1,DestinationName,DestinationDomainName,EventName,DeviceHostName,SourceUserName,SourceDomainName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="14">
<!-- 客户端已重新与管理服务器连接 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer\S+: 站点: .*,服务器: .*,域: .*,客户端已重新与管理服务器连接,.*?,.*?,.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: 站点: (.*),服务器: (.*),域: (.*),(客户端已重新与管理服务器连接),(.*?),(.*?),(.*)$]]>
</RegexSplit>
<Fields count="7" >
<![CDATA[CS1,DestinationName,DestinationDomainName,EventName,DeviceHostName,SourceUserName,SourceDomainName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="16">
<!-- 已通过主机完整性检查 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,已通过主机完整性检查.*要求: .*,本地: .*,本地: .*,远程: .*,远程: .*,远程: .*开始: .*,结束: .*,出现次数: \d+,应用程序: .*,位置: .*,用户: .*,域: .*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),(已通过主机完整性检查).*要求: (.*),本地: (.*),本地: .*,远程: .*,远程: .*,远程: .*开始: .*,结束: .*,出现次数: (\d+),应用程序: (.*),位置: (.*),用户: (.*),域: (.*)$]]>
</RegexSplit>
<Fields count="9" >
<![CDATA[DeviceName,EventName,message,SourceIP,CN1,FileName,CS2,SourceUserName,SourceDomainName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="18">
<!-- 已禁止来自此应用程序的通信 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,检测到 .*\s+已禁止来自此应用程序的通信:.*,本地: .*,本地: .*,远程: .*,远程: .*,远程: .*,.*,.*,Intrusion ID: \d+,开始: .*,结束: .*,出现次数: .*,应用程序: .*,位置:.*,用户: .*,域: .*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),(检测到 .*)\s+(已禁止来自此应用程序的通信): (.*),本地: (.*),本地: .*,远程: .*,远程: .*,远程: .*,.*,.*,Intrusion ID: (\d+),开始: .*,结束: .*,出现次数: .*,应用程序: .*,位置:(.*),用户: (.*),域: (.*)$]]>
</RegexSplit>
<Fields count="9" >
<![CDATA[EventTime"field="DeviceName,message,EventName,fileName,SourceIP,CN2,CS2,SourceUserName,SourceDomainName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="20">
<!-- 已允许来自此应用程序的通信 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,检测到 .*\s+已允许来自此应用程序的通信:.*,本地: .*,本地: .*,远程: .*,远程: .*,远程: .*,.*,.*,Intrusion ID: \d+,开始: .*,结束: .*,出现次数: .*,应用程序: .*,位置:.*,用户: .*,域: .*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),(检测到 .*)\s+(已允许来自此应用程序的通信): (.*),本地: (.*),本地: .*,远程: .*,远程: .*,远程: .*,.*,.*,Intrusion ID: (\d+),开始: .*,结束: .*,出现次数: .*,应用程序: .*,位置:(.*),用户: (.*),域: (.*)$]]>
</RegexSplit>
<Fields count="9" >
<![CDATA[EventTime"field="DeviceName,message,EventName,fileName,SourceIP,CN2,CS2,SourceUserName,SourceDomainName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="22">
<!-- 扫描 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: 扫描 ID: .*,开始: .*,结束: .*,.*,时间长度 \(秒\): .*,用户 1: .*,用户 2: .*,.*,命令: .*,威胁: .*,受感染: .*,文件总数: .*,已省略: .*,计算机: .*,IP 地址: .*,域: .*,组: .*,服务器: .*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (扫描) ID: (.*),开始: .*,结束: .*,(.*),时间长度 \(秒\): (.*),用户 1: (.*),用户 2: .*,(.*),命令: (.*),威胁: (.*),受感染: (.*),文件总数: (.*),已省略: (.*),计算机: (.*),IP 地址: (.*),域: (.*),组: (.*),服务器: (.*)$]]>
</RegexSplit>
<Fields count="16" >
<![CDATA[EventName,CN1,Device_EventType,CN2,SourceUserName,message,CS1,CN3,CN4,CN5,CN6,DeviceName,SourceIP,DestinationDomainName,CS4,DestinationName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="24">
<!-- 状态 主机完整性检查已启用 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer\S+: .*?,类别: .*,Smc,主机完整性检查已启用.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),类别: (.*),(Smc),(主机完整性检查已启用).*$]]>
</RegexSplit>
<Fields count="4" >
<![CDATA[DeviceName,DeviceEventID,CS1,EventName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="26">
<!-- 状态 已成功关闭 Symantec Endpoint Protection 服务 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,类别: .*,Symantec AntiVirus,已成功关闭 Symantec Endpoint Protection 服务.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),类别: (.*),(Symantec AntiVirus),(已成功关闭 Symantec Endpoint Protection 服务).*$]]>
</RegexSplit>
<Fields count="4" >
<![CDATA[DeviceName,DeviceEventID,Device_EventType,EventName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="28">
<!-- 状态 新的病毒定义文件已加载 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,类别: .*,Symantec AntiVirus,新的病毒定义文件已加载。版本: \S+。$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),类别: (.*),(Symantec AntiVirus),(新的病毒定义文件已加载)。版本: (\S+)。$]]>
</RegexSplit>
<Fields count="5" >
<![CDATA[DeviceName,DeviceEventID,Device_EventType,EventName,CS6]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="30">
<!-- 状态 停止用作组更新提供程序 (代理服务器) -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,类别: .*,GUP,停止用作组更新提供程序.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),类别: (.*),(GUP),(停止用作组更新提供程序.*)$]]>
</RegexSplit>
<Fields count="4" >
<![CDATA[DeviceName,DeviceEventID,Device_EventType,EventName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="32">
<!-- 状态 Symantec Management Client 已启动。 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,类别: .*,Smc,Symantec Management Client 已启动。.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),类别: (.*),(Smc),(Symantec Management Client 已启动。).*$]]>
</RegexSplit>
<Fields count="4" >
<![CDATA[DeviceName,DeviceEventID,Device_EventType,EventName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="34">
<!-- 状态 位置 已更改为 默认值。 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,类别: .*,Smc,位置 已更改为 默认值。.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),类别: (.*),(Smc),(位置 已更改为 默认值。).*$]]>
</RegexSplit>
<Fields count="4" >
<![CDATA[DeviceName,DeviceEventID,Device_EventType,EventName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="36">
<!-- 状态 网络威胁防护 已激活 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,类别: .*,Smc,网络威胁防护 已激活.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),类别: (.*),(Smc),(网络威胁防护 已激活).*$]]>
</RegexSplit>
<Fields count="4" >
<![CDATA[DeviceName,DeviceEventID,Device_EventType,EventName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="38">
<!-- 状态 ,网络威胁防护 -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: .*?,类别: .*,Smc,网络威胁防护.*引擎版本: \S+ .*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),类别: (.*),(Smc),(网络威胁防护).*引擎版本: (\S+) (.*)$]]>
</RegexSplit>
<Fields count="4" >
<![CDATA[DeviceName,DeviceEventID,Device_EventType,EventName,CS6,Message]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="40">
<!-- 规则 ,客户端服务_Registry_Write -->
<RegexMatcher>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),(.*?),.*?,(.*?),开始: .*,结束: .*,规则: (.*),.*?,(.*?),\d+,(\S+),(.*?),用户: (.*),域: (.*)$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^<\d+>.*?SymantecServer \S+: (.*?),(.*?),.*?,(.*?),开始: .*,结束: .*,规则: (.*),.*?,(.*?),\d+,(\S+),(.*?),用户: (.*),域: (.*)$]]>
</RegexSplit>
<Fields count="9" >
<![CDATA[DeviceHostName,DeviceAction,Device_EventType,EventName,FileName,CS2,CS3,SourceUserName,SourceDomainName]]>
</Fields>
<Extend>
<![CDATA[]]>
</Extend>
</Item>
<Item id="1000">
<RegexMatcher>
<![CDATA[^.*$]]>
</RegexMatcher>
<RegexSplit>
<![CDATA[^(.*)$]]>
</RegexSplit>
<Fields count="1" >
<![CDATA[Message]]>
</Fields>
<Extend count="0" >
<![CDATA[name=`SEP Events`]]>
</Extend>
</Item>
</MappingRegex>
0×03、结论
在产品化的路上,上面的产品Demo还远远没有达到第一阶段SIEM的要求,但是大致雏形已经具备。希望对大家有帮助。目前商业日志大数据方向的产品已经很多,如果大家有兴趣可以聊聊。
* 作者:bt0sea,本文属FreeBuf原创奖励计划文章,未经许可禁止转载