Skip to content

Commit 78ed9e2

Browse files
committed
Added explanations.
1 parent d57e680 commit 78ed9e2

File tree

3 files changed

+152
-34
lines changed

3 files changed

+152
-34
lines changed

README.md

+46-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,47 @@
11
detect-http-attack
2-
==================
2+
==================
3+
4+
It is a detecting attack tool for HTTP server such as Apache and Nginx.
5+
Analyzing access logs, output formated text as results.
6+
7+
To use shell pipelines easily, all I/O targets are STDIN, STDOUT and STDERR.
8+
9+
### Analyze access log:
10+
11+
./detect-http-attack.rb < /var/log/nginx/access.log
12+
13+
Targets eight or more consecutive senquential access:
14+
15+
./detect-http-attack.rb -s 8 < /var/log/apache/access_log
16+
17+
Regarded as senquential access within 3 seconds:
18+
19+
./detect-http-attack.rb -i 3 < /var/log/apache/access_log
20+
21+
### Notify attack while tailing access log:
22+
23+
Notifying attacks whenever detecting them to STDERR, all results are output to a file.
24+
25+
tail -f /var/log/nginx/access.log | ./detect-http-attack.rb -n > attack.log
26+
27+
### LTSV Format adapted:
28+
29+
Uses Labeled Tab-separated Values (LTSV) format (http://ltsv.org/)
30+
31+
./detect-http-attack.rb -ltsv < /var/log/apache/access_ltsv_log
32+
33+
### Settings and Customize:
34+
35+
edits detect-http-attack.conf
36+
37+
### Usage:
38+
39+
./detect-http-attack.rb --help
40+
41+
Usage: detect_http_attack [options]
42+
-ltsv Log type is LTSV
43+
-n notify when detecting attack
44+
-s COUNT Specify minimum sequential count
45+
-i SECONDS Specify maximum interval seconds
46+
-f CONFFILE Specify configuration file
47+

detect_http_attack.conf

+75-19
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
#
2-
# Detection HTTP Attack
2+
# Detect HTTP Attack
33
#
44
# Configuration file
55
#
66
#=========================
77

88

9+
#=========================
10+
# Exclueded access targets
11+
#==========================
12+
#
913
# Excluded hosts (comma-separated)
1014
exc_hosts=127.0.0.1
1115

@@ -20,39 +24,91 @@ exc_path_match=\.(js|css|jpg|gif|png|ico)$
2024
# Output Template
2125
#==================
2226
#
23-
# Terminal View Format for "tail -f access_log | "
27+
# Template consists of three lines 'head', 'body' and 'foot'.
28+
# Attack log treats sequential accesses from each host as BLOCK.
29+
# 'head' is a first line of BLOCK.
30+
# 'body' is a line for each access.
31+
# 'foot' is a last line of BLOCK.
32+
#
33+
# If '-n' flag is specified,
34+
# attack access is printed as 'serr' line to STDERR
35+
# whenever they are detected.
36+
#
37+
# Each line is specified escape sequences and fields.
38+
# Field name begins with '$'.
39+
#
40+
#
41+
# == Field List ==
42+
#
43+
# = Apache, Nginx Combined Log Format =
2444
#
25-
# head is host, count, ua.
26-
# host that is IP address or Hostname is cyan bold.
27-
# count that is the number of sequential access is magenta.
28-
# ua that is User-Agent is green.
45+
# > $host $ident $user [$time] "$req" $status $size $referer "$ua"
46+
# > 66.249.XX.XX - - [10/Feb/2013:10:27:45 +0900] "GET / HTTP/1.1" 200 1500 "-" "Searchbot/2.1"
47+
#
48+
# host = 66.249.XX.XX
49+
# ident = -
50+
# user = -
51+
# time = 10/Feb/2013:10:27:45 +0900
52+
# req = GET / HTTP/1.1
53+
# status = 200
54+
# size = 1500
55+
# referer = -
56+
# ua = Searchbot/2.1
57+
#
58+
# And futhermore, req is broken down into method, path and version.
59+
#
60+
# method = GET
61+
# path = /
62+
# version = HTTP/1.1
2963
#
30-
# body is date, status, path, size.
31-
# date is access date time by Ruby standard output.
32-
# status is HTTP status code.
33-
# path is the request path of URL.
34-
# referer is HTTP Referer.
35-
#
36-
# Other fields
37-
# $size response size without headers.
38-
#
3964
#
40-
# foot is empty line.
65+
# = LTSV Format =
4166
#
67+
# Field name is applied to each label.
68+
# And futhermore, req is broken down into method, path and version.
69+
#
70+
71+
#
72+
# == Formats ==
73+
#
74+
# = Terminal View Format for "tail -f access_log | " =
75+
#
76+
# head is host, count, ua.
77+
# host that is IP address or Hostname is cyan bold.
78+
# count that is the number of sequential access is magenta.
79+
# ua that is User-Agent is green.
80+
#
81+
# body is date, status, path, size.
82+
# date is access date time by Ruby standard output.
83+
# status is HTTP status code.
84+
# path is the request path of URL.
85+
# referer is HTTP Referer.
86+
#
87+
# foot is empty line.
88+
#
89+
# serr is red host, count, ua, date, status, path and referer at two lines.
90+
#
4291
head=\e[36m\e[1m$host\e[0m\t\e[35m$count\e[0m\t\e[32m$ua\e[0m\n
4392
body=$date\t$status\t$path\t$referer\n
4493
foot=\n
94+
serr=\e[31m\e[1m$host\e[0m\t\e[33m\e[1m$count\e[0m\t$ua\n$date\t$status\t$path\t$referer\n\n
95+
96+
#
97+
# = Terminal View Format without color sequences =
98+
#
4599
#head=$host\t$count\t$ua\n
46100
#body=$date\t$status\t$path\t$referer\n
47101
#foot=\n
102+
#serr=$host\t$count\t$ua\n$date\t$status\t$path\t$referer\n\n
48103

49104
#
50-
# LTSV format
105+
# = LTSV format =
51106
#
52-
# head and foot isn't output.
53-
# body is time, host and req.
107+
# head and foot aren't output.
108+
# body is time, host and req.
54109
#
55110
##head=
56111
#body=time:[$time]\thost:$host\treq:$req\n
57112
##foot=
113+
#serr=time:[$time]\thost:$host\treq:$req\n
58114

detect_http_attack.rb

+31-14
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,10 @@ def parse(line)
9999
values[name.to_sym] = value
100100
}
101101

102-
method, path = values[:req].split(' ')
102+
method, path, version = values[:req].split(' ')
103103
values[:method] = method
104104
values[:path] = path
105+
values[:version] = version
105106
values[:date] = parse_datetime(values[:time])
106107

107108
values
@@ -144,16 +145,18 @@ def get(field)
144145

145146
class Template
146147

147-
def initialize(head, body, foot)
148+
def initialize(head, body, foot, serr)
148149
@re_field = Regexp.new("\\$[a-z]+")
149150

150151
h = head || "$host\\t$count\\t$ua"
151152
b = body || "$date\\t$path\\t$referer"
152153
f = foot || ""
154+
s = serr || "$host\\t$count\\t$ua\\n$date\\t$path\\t$referer"
153155

154156
@head = parse_value(h)
155157
@body = parse_value(b)
156158
@foot = parse_value(f)
159+
@serr = parse_value(s)
157160
end
158161

159162
def parse_value(value)
@@ -182,22 +185,26 @@ def parse_value(value)
182185
vals
183186
end
184187

185-
def print_head(ops, count, row)
188+
def print_head(count, row)
186189
if @head
187-
print_row(@head, ops, count, row)
190+
print_row(@head, STDOUT, count, row)
188191
end
189192
end
190193

191-
def print_body(ops, row)
192-
print_row(@body, ops, "", row)
194+
def print_body(row)
195+
print_row(@body, STDOUT, "", row)
193196
end
194197

195-
def print_foot(ops, count, row)
198+
def print_foot(count, row)
196199
if @foot
197-
print_row(@foot, ops, count, row)
200+
print_row(@foot, STDOUT, count, row)
198201
end
199202
end
200203

204+
def print_serr(count, row)
205+
print_row(@serr, STDERR, count, row)
206+
end
207+
201208
def print_row(templ, ops, count, row)
202209
templ.each do |val|
203210
if val.instance_of?(String)
@@ -221,6 +228,7 @@ class DetectionProcessor
221228

222229
attr :interval_threshold, true
223230
attr :sequence_threshold, true
231+
attr :notify, true
224232
attr :exc_hosts, true
225233
attr :exc_ua, true
226234
attr :exc_path, true
@@ -235,6 +243,8 @@ def initialize(template)
235243
@exc_ua = nil
236244
@exc_path = nil
237245

246+
@realtime_notify = false
247+
238248
@pre_access_map = Hash.new
239249
@ops = STDOUT
240250
end
@@ -254,6 +264,10 @@ def proc(row)
254264
if (date_ts - ts) <= @interval_threshold
255265
al.push(row)
256266
@pre_access_map.store(host, [date_ts, al])
267+
268+
if @notify and al.count >= @sequence_threshold
269+
@template.print_serr(al.count, row)
270+
end
257271
else
258272
if al.count >= @sequence_threshold
259273
print_access(host, al)
@@ -277,13 +291,13 @@ def finalize
277291
def print_access(host, al)
278292
fl = al[0]
279293

280-
@template.print_head(@ops, al.count, fl)
294+
@template.print_head(al.count, fl)
281295

282296
al.each do |row|
283-
@template.print_body(@ops, row)
297+
@template.print_body(row)
284298
end
285299

286-
@template.print_foot(@ops, al.count, fl)
300+
@template.print_foot(al.count, fl)
287301
end
288302
end
289303

@@ -292,12 +306,14 @@ def print_access(host, al)
292306
def get_opts
293307
# Settting from Arguments
294308

295-
opts = { :parser => 'combined', :max_interval => 3, :min_seq => 8 }
309+
opts = { :parser => 'combined', :max_interval => 3, :min_seq => 8, :notify => false }
296310

297311
opt = OptionParser.new
298312

299313
opt.on('-ltsv', 'Log type is LTSV') { |v| opts[:parser] = "ltsv" }
300314

315+
opt.on('-n', 'notify when detecting attack') { |v| opts[:notify] = true }
316+
301317
opt.on('-s COUNT', 'Specify minimum sequential count') do |count|
302318
opts[:min_seq] = count.to_i
303319
end
@@ -327,11 +343,12 @@ def main
327343

328344
conf = Configuration.new(opts[:conf_file])
329345

330-
template = Template.new(conf.get(:head), conf.get(:body), conf.get(:foot))
346+
template = Template.new(conf.get(:head), conf.get(:body), conf.get(:foot), conf.get(:serr))
331347

332348
processor = DetectionProcessor.new(template)
333349
processor.interval_threshold = opts[:max_interval]
334350
processor.sequence_threshold = opts[:min_seq]
351+
processor.notify = opts[:notify]
335352

336353
exc_hosts = conf.get(:exc_hosts)
337354
if exc_hosts
@@ -340,7 +357,7 @@ def main
340357

341358
exc_ua = conf.get(:exc_ua_match)
342359
if exc_ua
343-
processor.exc_ua = Regexp.new(exc_ua, "i")
360+
processor.exc_ua = Regexp.new(exc_ua, Regexp::IGNORECASE)
344361
end
345362

346363
exc_path = conf.get(:exc_path_match)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy