Quantcast
Channel: 碳基体
Viewing all articles
Browse latest Browse all 75

web日志异常检测实践之长度异常模型

$
0
0
作为一个码渣与数学渣,在数据分析的实践过程是非常虐心的,但所幸的是有些问题可以用helloword的水平来解决,本文就介绍一种从web日志中发现攻击日志的简单方法。

一、实验环境

输入数据源:web访问日志,2T左右
输出数据:攻击日志
开发环境:*inux+hdfs+mapreduce+java


二、模型原理
假设参数值长度的分布近似正态分布,对于正态或近似正态分布,我们知道其分布曲线近似下面的钟形图,我们用正态曲线下横轴上一定区间的面积来反映改区间的数据量占统计总量的百分比,或变量值落在该区间的概率分布。

均值和加减标准差之间的面积比如如下:
web日志异常检测实践之长度异常模型 - 碳基体 - 碳基体
 
web日志异常检测实践之长度异常模型 - 碳基体 - 碳基体标准差之间的面积为68%
web日志异常检测实践之长度异常模型 - 碳基体 - 碳基体标准差之间的面积为95%
web日志异常检测实践之长度异常模型 - 碳基体 - 碳基体标准差之间的面积为99.7% 


根据经验,访问日志中的异常数据是落在钟形图的两侧的,而异常数据中会造成破坏的攻击数据则分布在钟形图的右侧,因此我们可以通过以下不等式来筛选中访问日志中的异常数据

valuelength > dExpectation + dDeviation*number_of_devi

valuelength:待检测的参数值长度

dExpectation:所有请求参数值长度均值

dDeviation:所有请求参数值长度标准差

number_of_devi:调参,距离几个标准差


 
三、模型的MapReduce实现流程图


web日志异常检测实践之长度异常模型 - 碳基体 - 碳基体

 

模型由4个MapReduce Job构成(如上图所示),

1. 日志解析Job

Mapper任务:按以下过程提取数据 (host+path+filename?paraName=paraValue, {"length":"xx","uri":"xxx"})

Reducer任务:数据去重

提取访问日志中的以下数据

host 域名

uri 请求URI (进一步解析为请求路径,查询字符串,参数名/值对)

http_response_code 响应码

按以下条件进行日志过滤

(1)只处理响应码2xx,3xx的访问日志

http_response_code.startsWith("2") || http_response_code.startsWith("3")

(2)不处理静态请求

String [] path_bl = { "gif","jpe","png","bmp","ico","js","cs","avi","wma","mkv","doc","pdf","ppt","txt","csv","xls","lnk" };

(3)不处理参数名异常的数据

paramname.matches("[-\\.\\w\\d\\[\\]\\x22\\x27\\s]+")

(4)不处理查询字符串为空的请求

(5)不处理参数值为空的请求

2. E(X) D(X) 计算Job

Mapper任务:计算每个参数值的长度(host+path+filename?paraName,length)

Reducer任务:计算每个参数值的均值与标准差

host+path+filename?paraName,{"expectation":xxx,"deviation":xxx})

并将结果存储到redis数据库中


均值计算公式:

web日志异常检测实践之长度异常模型 - 碳基体 - 碳基体

 

标准差计算公式:

web日志异常检测实践之长度异常模型 - 碳基体 - 碳基体
 关键代码如下:

//assume is a normal distribution
double expectation = 0.0;
double variance = 0.0;
double deviation = 0.0;
double sum = 0.0;
double sum_square = 0.0;
int count = 0;

JSONObject result_json = new JSONObject();

while(v.hasNext()){
String value_str = v.next().toString();
double iLength = Double.parseDouble(value_str);
sum += iLength;
sum_square += iLength * iLength;
count++;
}

expectation = sum / count;
variance = (sum_square - sum*sum/count)/count;
deviation = Math.sqrt(variance);

result_json.put("expectation", expectation);
result_json.put("deviation", deviation);
jedis.set(k.toString(), result_json.toJSONString()) ;

output.collect(k, new Text(result_json.toJSONString()));

3. 异常提取Job

Mapper任务:读取redis数据库中的参数值对应的均值与标准差,按以下条件提取异常数据

(host+path+filename?paraName=paraValue, 

{"originURL":"xxx","attackType":"none","expectation":xxx,"deviation":xxx,"length":xxx})

(1)长度值在异常范围内的

valuelength > dExpectation + dDeviation*number_of_devi

(2)排除参数名在白名单内的请求

if(Toolkit.isParaNameWL(k, paraName))
return;

参数白名单从配置文件中读取ParaNameWL

web日志异常检测实践之长度异常模型 - 碳基体 - 碳基体

 (3)去除参数取值类型在白名单中的数据

允许URI,Path,Email,SafeText

if(Toolkit.isPathText(paraValue) ||Toolkit.isSafeText(paraValue) || Toolkit.isURIText(paraValue) || Toolkit.isEmailText(paraValue))
return;

Reducer任务:数据去重

4. 攻击提取Job

异常不是攻击,接下来我们使用弱攻击签名来提取攻击

if(Toolkit.isSQLI(paraValue)){
attackType = "SQLI";
}else if(Toolkit.isFI("FI")){
attackType = "FI";
}else if(Toolkit.isXSS("XSS")){
attackType = "XSS";
}else if(Toolkit.isCE("CE")){
attackType = "CE";
}

以SQL签名来示例,这里的

public static boolean isSQLI(String str){
boolean isSQLI = false;

Set<String> pattern = new HashSet<String>();
pattern.add("select");
pattern.add("union");
pattern.add("benchmark");
pattern.add("sleep");
pattern.add("pg_sleep");
pattern.add("updatexml");
pattern.add("extractvalue");
pattern.add("dbms_pipe");
pattern.add("hex");
pattern.add("ascii");
pattern.add("cast");
pattern.add("concat");
pattern.add("convert");
pattern.add("chr");
pattern.add("char");
pattern.add("upper");
pattern.add("substr");
pattern.add("substring");
pattern.add("floor");
pattern.add("bitand");
pattern.add("exec");
pattern.add("xp_cmdshell");
pattern.add("waitfor");
pattern.add("iif");
pattern.add("outfile");
pattern.add("dumpfile");
pattern.add("*/");
pattern.add("insert");
pattern.add("update");
pattern.add("delete");
pattern.add("drop");

isSQLI = containsWord(pattern, str);

return isSQLI;
}

攻击数据实例:
web日志异常检测实践之长度异常模型 - 碳基体 - 碳基体
 
、后续
长度异常模型由离线的MapReduce批处理版本改成Storm实时处理版本,均值和标准差的计算可以改成迭代的实现方式,选择合适的数据流的窗口即可。


模型不足的地方:
遗漏点:
(1)非2xx,3xx日志里的攻击数据:e.g.基于错误的攻击 
(2)payload在参数名中的攻击数据 e.g. xxx/y?a['[payload]']=1
(3)URL重写后的攻击 e.g.  xxx/haodai-news-[payload].html  
(4)攻击路径非常规URL e.g. 隐藏的非正常路径可以访问到的
(5)重定向漏洞未覆盖(因为将所有的PATH,URI加白)

没有一个模型能解决所有问题,可以采用分而治之的方式来解决这些问题。

正态分布很好的鼓励了我,每当自我厌弃的时候,就想着自己哪有那么高的概率处于钟形的左侧,然后就自愈了


Viewing all articles
Browse latest Browse all 75

Trending Articles