Skip to content

Commit 3b40210

Browse files
Merge branch 'release/1.5.0'
2 parents 08f43a2 + 86ca654 commit 3b40210

29 files changed

+604
-245
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ Thug is a Python low-interaction honeyclient aimed at mimicing the behavior of a
9898
You can see a complete SpamScope report with Thug analysis [here](https://goo.gl/Y4kWCv).
9999

100100
### VirusTotal (optional)
101-
It's possible add to results (for mail attachments) VirusTotal report. You need a private API key.
101+
It's possible add to results (for mail attachments and sender ip address) the VirusTotal report. You need a private API key.
102+
103+
### Shodan (optional)
104+
It's possible add to results the Shodan report for sender ip address. You need a private API key.
102105

103106
### Elasticsearch (optional)
104107
It's possible to store the results in Elasticsearch. In this case you should install `elasticsearch` package.
@@ -205,6 +208,8 @@ $ export ZEMANA_ENABLED=True
205208
$ export ZEMANA_APIKEY="your key"
206209
$ export ZEMANA_PARTNERID="your partner id"
207210
$ export ZEMANA_USERID="your userid"
211+
$ export SHODAN_ENABLED=True
212+
$ export SHODAN_APIKEY="your key"
208213
```
209214

210215

conf/spamscope.example.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,23 @@ tokenizer:
7070
# Max number of hashes saved for filter function
7171
maxlen_attachments: 1000000
7272

73+
# If True the same ip address is filtered and not analyzed.
74+
filter_network: True
75+
76+
# Max number of hashes saved for filter function
77+
maxlen_network: 1000000
78+
79+
80+
# Network bolt configuration
81+
network:
82+
shodan:
83+
enabled: False
84+
api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
85+
86+
virustotal:
87+
enabled: False
88+
api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
89+
7390

7491
# Attachments bolt configuration
7592
attachments:

docs/images/schema_topology.jpg

6.69 KB
Loading

docs/images/schema_topology.png

11.3 KB
Loading

project.clj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
(defproject spamscope "1.4.10-SNAPSHOT"
1+
(defproject spamscope "1.5.0-SNAPSHOT"
22
:resource-paths ["_resources"]
33
:target-path "_build"
44
:min-lein-version "2.0.0"
55
:jvm-opts ["-client"]
6-
:dependencies [[org.apache.storm/storm-core "1.0.3"]
7-
[org.apache.storm/flux-core "1.0.3"]]
6+
:dependencies [[org.apache.storm/storm-core "1.1.0"]
7+
[org.apache.storm/flux-core "1.1.0"]]
88
:jar-exclusions [#"log4j\.properties" #"org\.apache\.storm\.(?!flux)" #"trident" #"META-INF" #"meta-inf" #"\.yaml"]
99
:uberjar-exclusions [#"log4j\.properties" #"org\.apache\.storm\.(?!flux)" #"trident" #"META-INF" #"meta-inf" #"\.yaml"]
1010
)

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
PyYAML==3.12
22
backports.functools-lru-cache==1.3
33
chainmap==1.0.2
4-
elasticsearch==5.2.0
4+
elasticsearch==5.3.0
55
mail-parser==1.1.10
66
patool==1.12
77
pyparsing==2.2.0
88
python-magic==0.4.12
99
redis==2.10.5
10+
shodan==1.6.5
1011
simplejson==3.10.0
1112
six==1.10.0
1213
ssdeep==3.2

src/bolts/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@
2424
from .tokenizer import Tokenizer
2525
from .urls_handler_attachments import UrlsHandlerAttachments
2626
from .urls_handler_body import UrlsHandlerBody
27+
from .network import Network

src/bolts/json_maker.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,14 @@ def _compose_output(self, greedy_data):
4949
mail["is_filtered"] = greedy_data["tokenizer"][2]
5050

5151
# Attachments
52-
mail["with_attachments"] = greedy_data["attachments"][1]
52+
# with_raw_attachments: the mail has attachments
53+
# with_attachments: the mail has not filtered attachments
54+
mail["with_raw_attachments"] = greedy_data["attachments"][1]
55+
attachments = greedy_data["attachments"][2]
5356

54-
if mail["with_attachments"]:
55-
mail["attachments"] = greedy_data["attachments"][2]
57+
if attachments:
58+
mail["with_attachments"] = True
59+
mail["attachments"] = attachments
5660

5761
# Urls in attachments:
5862
# Add urls attachments because you can have more differents attachments
@@ -64,6 +68,11 @@ def _compose_output(self, greedy_data):
6468
urls = greedy_data["urls-handler-attachments"][2]
6569
mail["urls_attachments"] = reformat_urls(urls)
6670

71+
# Network
72+
network = greedy_data["network"][1]
73+
if network:
74+
mail["network"] = network
75+
6776
# Add intelligence output only if mail is not filtered
6877
if not mail["is_filtered"]:
6978

src/bolts/network.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
Copyright 2016 Fedele Mantuano (https://twitter.com/fedelemantuano)
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
"""
19+
20+
21+
from __future__ import absolute_import, print_function, unicode_literals
22+
from modules import AbstractBolt
23+
from modules.networks import processors
24+
25+
26+
class Network(AbstractBolt):
27+
outputs = ['sha256_random', 'network', 'is_filtered']
28+
29+
def process(self, tup):
30+
sha256_random = tup.values[0]
31+
ipaddress = tup.values[1]
32+
is_filtered = tup.values[2]
33+
34+
try:
35+
results = {}
36+
37+
if not is_filtered and ipaddress:
38+
for p in processors:
39+
try:
40+
p(self.conf[p.__name__], ipaddress, results)
41+
except KeyError:
42+
self.log("KeyError: {!r} doesn't exist in conf".format(
43+
p.__name__), "error")
44+
45+
except Exception as e:
46+
self.log("Failed process network for mail: {}".format(
47+
sha256_random), "error")
48+
self.raise_exception(e, tup)
49+
50+
else:
51+
self.emit([sha256_random, results])

src/bolts/phishing.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@ def _load_lists(self):
6262
self.log("Phishing targets keywords reloaded")
6363

6464
def _search_phishing(self, greedy_data):
65+
with_urls = False
6566

6667
# If mail is filtered don't check for phishing
6768
is_filtered = greedy_data["tokenizer"][2]
6869

6970
if is_filtered:
70-
return False, False
71+
return False, False, False
7172

7273
# Reset phishing bitmap
7374
self._pb.reset_score()
@@ -83,8 +84,9 @@ def _search_phishing(self, greedy_data):
8384
urls_body = greedy_data["urls-handler-body"][2]
8485
urls_attachments = greedy_data["urls-handler-attachments"][2]
8586

86-
# TODO: if an attachment is filtered the score is not complete
87-
# more different mails can have same attachment
87+
# TODO: if an attachment is filtered, the score is not complete
88+
# more different mails can have the same attachment
89+
# more different attachments can have the same mail
8890
attachments = MailAttachments(greedy_data["attachments"][2])
8991

9092
urls = (
@@ -110,14 +112,15 @@ def _search_phishing(self, greedy_data):
110112
# Target not added because urls come already analyzed text
111113
for k, v in urls:
112114
if k:
115+
with_urls = True
113116
if any(check_urls(k, i) for i in self._t_keys.values()):
114117
self._pb.set_property_score(v)
115118

116119
# Check subject
117120
if swt(subject, self._s_keys):
118121
self._pb.set_property_score("mail_subject")
119122

120-
return self._pb.score, list(targets)
123+
return self._pb.score, list(targets), with_urls
121124

122125
def process_tick(self, freq):
123126
"""Every freq seconds you reload the keywords. """
@@ -135,10 +138,15 @@ def process(self, tup):
135138
if not diff:
136139
with_phishing = False
137140

138-
score, targets = self._search_phishing(
141+
score, targets, with_urls = self._search_phishing(
139142
self._mails.pop(sha256_random))
140143

141-
if score:
144+
# There is phishing only if there is also urls
145+
# Mail can have target without phishing
146+
if score and with_urls:
142147
with_phishing = True
148+
else:
149+
with_phishing = False
150+
score = 0
143151

144152
self.emit([sha256_random, with_phishing, score, targets])

0 commit comments

Comments
 (0)