├── .gitignore
├── AttackMapServer
├── AttackMapServer.py
├── index.html
└── static
│ ├── flags.zip
│ ├── index.css
│ └── map.js
├── DataServer
├── DataServer.py
├── const.py
├── syslog-gen.py
└── syslog-gen.sh
├── DataServerDB
└── db-dl.sh
├── LICENSE
├── README.md
├── deploy.sh
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 |
3 | env/
4 |
5 | dump.rdb
6 | DataServer/dump.rdb
7 | AttackMapServer/dump.rdb
8 |
9 | DataServerDB/GeoLite2-City.mmdb
10 | DataServerDB/GeoLite2-City.mmdb.gz
11 | DataServer/__pycache__/
12 |
13 | AttackMapServer/static/flags/*
14 |
--------------------------------------------------------------------------------
/AttackMapServer/AttackMapServer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | """
4 | AUTHOR: Matthew May - mcmay.web@gmail.com
5 | """
6 |
7 | # Imports
8 | import json
9 | import redis
10 | import tornadoredis
11 | #import tornado.httpserver
12 | import tornado.ioloop
13 | import tornado.web
14 | import tornado.websocket
15 | #import re
16 |
17 | from os import getuid, path
18 | from sys import exit
19 |
20 |
21 | # Look up service colors
22 | service_rgb = {
23 | 'FTP':'#ff0000',
24 | 'SSH':'#ff8000',
25 | 'TELNET':'#ffff00',
26 | 'EMAIL':'#80ff00',
27 | 'WHOIS':'#00ff00',
28 | 'DNS':'#00ff80',
29 | 'HTTP':'#00ffff',
30 | 'HTTPS':'#0080ff',
31 | 'SQL':'#0000ff',
32 | 'SNMP':'#8000ff',
33 | 'SMB':'#bf00ff',
34 | 'AUTH':'#ff00ff',
35 | 'RDP':'#ff0060',
36 | 'DoS':'#ff0000',
37 | 'ICMP':'#ffcccc',
38 | 'OTHER':'#6600cc'
39 | }
40 |
41 |
42 | class IndexHandler(tornado.web.RequestHandler):
43 | @tornado.web.asynchronous
44 | def get(request):
45 | request.render('index.html')
46 |
47 |
48 | class WebSocketChatHandler(tornado.websocket.WebSocketHandler):
49 | def __init__(self, *args, **kwargs):
50 | super(WebSocketChatHandler, self).__init__(*args,**kwargs)
51 | self.listen()
52 |
53 | def check_origin(self, origin):
54 | return True
55 |
56 | @tornado.gen.engine
57 | def listen(self):
58 |
59 | print('[*] WebSocketChatHandler opened')
60 |
61 | try:
62 | # This is the IP address of the DataServer
63 | self.client = tornadoredis.Client('127.0.0.1')
64 | self.client.connect()
65 | print('[*] Connected to Redis server')
66 | yield tornado.gen.Task(self.client.subscribe, 'attack-map-production')
67 | self.client.listen(self.on_message)
68 | except Exception as ex:
69 | print('[*] Could not connect to Redis server.')
70 | print('[*] {}'.format(str(ex)))
71 |
72 | def on_close(self):
73 | print('[*] Closing connection.')
74 |
75 | # This function is called everytime a Redis message is received
76 | def on_message(self, msg):
77 |
78 | if len(msg) == 0:
79 | print ("msg == 0\n")
80 | return None
81 |
82 | if 'ip_blocked' in msg:
83 | ip = re.split(":",msg)
84 | #fp = open('/mnt/map_attack_blk/LOG4.log','a')
85 | #fp.write(ip[1]+"\n")
86 | #fp.close()
87 |
88 | try:
89 | json_data = json.loads(msg.body)
90 | except Exception as ex:
91 | return None
92 |
93 | if 'msg_type' in json_data:
94 | msg_type = json_data['msg_type']
95 | else:
96 | msg_type = None
97 | if 'msg_type2' in json_data:
98 | msg_type2 = json_data['msg_type2']
99 | else:
100 | msg_type2 = None
101 | if 'msg_type3' in json_data:
102 | msg_type3 = json_data['msg_type3']
103 | else:
104 | msg_type3 = None
105 | if 'protocol' in json_data:
106 | protocol = json_data['protocol']
107 | else:
108 | protocol = None
109 | if 'src_ip' in json_data:
110 | src_ip = json_data['src_ip']
111 | else:
112 | src_ip = None
113 | if 'dst_ip' in json_data:
114 | dst_ip = json_data['dst_ip']
115 | else:
116 | dst_ip = None
117 | if 'src_port' in json_data:
118 | src_port = json_data['src_port']
119 | else:
120 | src_port = None
121 | if 'dst_port' in json_data:
122 | dst_port = json_data['dst_port']
123 | else:
124 | dst_port = None
125 | if 'latitude' in json_data:
126 | src_lat = json_data['latitude']
127 | else:
128 | src_lat = None
129 | if 'longitude' in json_data:
130 | src_long = json_data['longitude']
131 | else:
132 | src_long = None
133 | if 'dst_lat' in json_data:
134 | dst_lat = json_data['dst_lat']
135 | else:
136 | dst_lat = None
137 | if 'dst_long' in json_data:
138 | dst_long = json_data['dst_long']
139 | else:
140 | dst_long = None
141 | if 'city' in json_data:
142 | city = json_data['city']
143 | else:
144 | city = None
145 | if 'continent' in json_data:
146 | continent = json_data['continent']
147 | else:
148 | continent = None
149 | if 'continent_code' in json_data:
150 | continent_code = json_data['continent_code']
151 | else:
152 | continent_code = None
153 | if 'country' in json_data:
154 | country = json_data['country']
155 | else:
156 | country = None
157 | if 'iso_code' in json_data:
158 | iso_code = json_data['iso_code']
159 | else:
160 | iso_code = None
161 | if 'postal_code' in json_data:
162 | postal_code = json_data['postal_code']
163 | else:
164 | postal_code = None
165 | if protocol:
166 | color = service_rgb[protocol]
167 | else:
168 | color = '#000000'
169 | if 'event_count' in json_data:
170 | event_count = json_data['event_count']
171 | else:
172 | event_count = None
173 | if 'continents_tracked' in json_data:
174 | continents_tracked = json_data['continents_tracked']
175 | else:
176 | continents_tracked = None
177 | if 'countries_tracked' in json_data:
178 | countries_tracked = json_data['countries_tracked']
179 | else:
180 | countries_tracked = None
181 | if 'ips_tracked' in json_data:
182 | ips_tracked = json_data['ips_tracked']
183 | else:
184 | ips_tracked = None
185 | if 'unknowns' in json_data:
186 | unknowns = json_data['unknowns']
187 | else:
188 | unknowns = None
189 | if 'event_time' in json_data:
190 | event_time = json_data['event_time']
191 | else:
192 | event_time = None
193 | if 'country_to_code' in json_data:
194 | country_to_code = json_data['country_to_code']
195 | else:
196 | country_to_code = None
197 | if 'ip_to_code' in json_data:
198 | ip_to_code = json_data['ip_to_code']
199 | else:
200 | ip_to_code = None
201 |
202 | msg_to_send = {
203 | 'type': msg_type,
204 | 'type2': msg_type2,
205 | 'type3': msg_type3,
206 | 'protocol': protocol,
207 | 'src_ip': src_ip,
208 | 'dst_ip': dst_ip,
209 | 'src_port': src_port,
210 | 'dst_port': dst_port,
211 | 'src_lat': src_lat,
212 | 'src_long': src_long,
213 | 'dst_lat': dst_lat,
214 | 'dst_long': dst_long,
215 | 'city': city,
216 | 'continent': continent,
217 | 'continent_code': continent_code,
218 | 'country': country,
219 | 'iso_code': iso_code,
220 | 'postal_code': postal_code,
221 | 'color': color,
222 | 'event_count': event_count,
223 | 'continents_tracked': continents_tracked,
224 | 'countries_tracked': countries_tracked,
225 | #'ips_tracked': "" + str(ips_tracked) + "",
226 | 'ips_tracked': ips_tracked,
227 | 'unknowns': unknowns,
228 | 'event_time': event_time,
229 | 'country_to_code': country_to_code,
230 | 'ip_to_code': ip_to_code,
231 | }
232 |
233 |
234 | self.write_message(json.dumps(msg_to_send))
235 |
236 | def main():
237 | # Register handler pages
238 | handlers = [
239 | (r'/websocket', WebSocketChatHandler),
240 | (r'/static/(.*)', tornado.web.StaticFileHandler, {'path': 'static'}),
241 | (r'/flags/(.*)', tornado.web.StaticFileHandler, {'path': 'static/flags'}),
242 | (r'/', IndexHandler)
243 | ]
244 |
245 | # Define the static path
246 | #static_path = path.join( path.dirname(__file__), 'static' )
247 |
248 | # Define static settings
249 | settings = {
250 | #'static_path': static_path
251 | }
252 |
253 | # Create and start app listening on port 8888
254 | try:
255 | app = tornado.web.Application(handlers, **settings)
256 | app.listen(8888)
257 | print('[*] Waiting on browser connections...')
258 | tornado.ioloop.IOLoop.instance().start()
259 | except Exception as appFail:
260 | print(appFail)
261 |
262 | if __name__ == '__main__':
263 | try:
264 | main()
265 | except KeyboardInterrupt:
266 | print('\nSHUTTING DOWN')
267 | exit()
268 |
--------------------------------------------------------------------------------
/AttackMapServer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | GeoIP Attack Map
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | Service |
40 |
41 |
42 |
43 |
44 |
45 | |
46 | FTP |
47 |
48 |
49 |
50 | |
51 | SSH |
52 |
53 |
54 |
55 | |
56 | TELNET |
57 |
58 |
59 |
60 | |
61 | EMAIL |
62 |
63 |
64 |
65 | |
66 | WHOIS |
67 |
68 |
69 |
70 | |
71 | DNS |
72 |
73 |
74 |
75 | |
76 | HTTP |
77 |
78 |
79 |
80 | |
81 | HTTPS |
82 |
83 |
84 |
85 | |
86 | SQL |
87 |
88 |
89 |
90 | |
91 | SNMP |
92 |
93 |
94 |
95 | |
96 | SMB |
97 |
98 |
99 |
100 | |
101 | AUTH |
102 |
103 |
104 |
105 | |
106 | RDP |
107 |
108 |
109 |
110 | |
111 | DOS |
112 |
113 |
114 |
115 | |
116 | ICMP |
117 |
118 |
119 |
120 | |
121 | OTHER |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | Qtd |
132 | |
133 | IP |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | Qtd |
147 | |
148 | Country |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | Timestamp |
162 | IP |
163 | |
164 | Country |
165 | City |
166 | Service |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 | Attack Types |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 | Timestamp |
198 | Exploit |
199 | |
200 | IP |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
--------------------------------------------------------------------------------
/AttackMapServer/static/flags.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MatthewClarkMay/geoip-attack-map/58ac0af0fced187291e600704bcf9a15f7ce6e03/AttackMapServer/static/flags.zip
--------------------------------------------------------------------------------
/AttackMapServer/static/index.css:
--------------------------------------------------------------------------------
1 | html {
2 | background:black;
3 | }
4 | body {
5 | height:100%;
6 | width:100%;
7 | margin:0;
8 | padding:0;
9 | }
10 | #map {
11 | height:100%;
12 | width:100%;
13 | position:absolute;
14 | top:0;
15 | bottom:0;
16 | }
17 | .leaflet-container {
18 | background:black;
19 | }
20 | .circle {
21 | height:18px;
22 | width:18px;
23 | border-radius:50%;
24 | margin:0px;
25 | }
26 |
27 | #informIP {
28 | display:none;
29 | height:auto;
30 | width:250px;
31 | position:absolute;
32 | bottom:30%;
33 | left:10%;
34 | padding:15px;
35 | border-radius:25px;
36 | background:#666161;
37 | opacity:0.9;
38 | }
39 |
40 | #informIP #fechar{
41 | float:right;
42 | left:90%;
43 | border-radius:25px;
44 | color:red;
45 | font-style:oblique;
46 | }
47 |
48 | #informIP #blockIP{
49 | position:relative;
50 | left:30%;
51 | padding:10px;
52 | font-weight:bold;
53 | box-shadow:1px 1px;
54 | }
55 | #informIP h3{
56 | font-size:1.4em;
57 | padding:0px;
58 | color:#ffffff;
59 | }
60 |
61 | .container-fluid {
62 | height:25%;
63 | width:100%;
64 | position:absolute;
65 | bottom:3.5%;
66 | background:black;
67 | opacity:0.7;
68 | }
69 | .row {
70 | height:100%;
71 | }
72 | .row table {
73 | height:100%;
74 | margin:0px
75 | }
76 | tbody {
77 | height:100%;
78 | }
79 | table th, td{
80 | text-align:center;
81 | }
82 | th, td {
83 | color:white;
84 | }
85 | .col-md-1 {
86 | height:100%;
87 | overflow-y:scroll;
88 | overflow-x:auto;
89 | }
90 | .col-md-2 {
91 | height:100%;
92 | overflow-y:scroll;
93 | overflow-x:auto;
94 | }
95 | .col-md-7 {
96 | height:100%;
97 | overflow-y:scroll;
98 | overflow-x:auto;
99 | }
100 |
101 | .col-md-8 {
102 | height:158px;
103 | overflow-y:scroll;
104 | overflow-x:auto;
105 | }
106 |
107 | .col-md-4 {
108 | height:154px;
109 | overflow-y:scroll;
110 | overflow-x:auto;
111 | }
112 |
--------------------------------------------------------------------------------
/AttackMapServer/static/map.js:
--------------------------------------------------------------------------------
1 | // To access by a browser in another computer, use the external IP of machine running AttackMapServer
2 | // from the same computer(only), you can use the internal IP.
3 | // Example:
4 | // - AttackMapServer machine:
5 | // - Internal IP: 127.0.0.1
6 | // - External IP: 192.168.11.106
7 | var webSock = new WebSocket("ws:/127.0.0.1:8888/websocket"); // Internal
8 | //var webSock = new WebSocket("ws:/192.168.1.100:8888/websocket"); // External
9 |
10 | // link map
11 |
12 | L.mapbox.accessToken = "pk.eyJ1IjoibW1heTYwMSIsImEiOiJjaWgyYWU3NWQweWx2d3ltMDl4eGk5eWY1In0.9YoOkALPP7zaoim34ZITxw";
13 | var map = L.mapbox.map("map", "mapbox.dark", {
14 | center: [0, 0], // lat, long
15 | zoom: 2
16 | });
17 |
18 | // add full screen option
19 | L.control.fullscreen().addTo(map);
20 |
21 | // hq coords
22 | var hqLatLng = new L.LatLng(37.3845, -122.0881);
23 |
24 | // hq marker
25 | L.circle(hqLatLng, 110000, {
26 | color: 'red',
27 | fillColor: 'yellow',
28 | fillOpacity: 0.5,
29 | }).addTo(map);
30 |
31 | // Append