LEFT | RIGHT |
1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
2 """Analysis plugin to look up files in nsrlsvr and tag events.""" | 2 """Analysis plugin to look up files in nsrlsvr and tag events.""" |
3 | 3 |
4 import logging | 4 import logging |
5 import socket | 5 import socket |
6 | 6 |
7 from plaso.analysis import interface | 7 from plaso.analysis import interface |
8 from plaso.analysis import manager | 8 from plaso.analysis import manager |
9 from plaso.containers import hashes | |
10 | 9 |
11 | 10 |
12 class NsrlsvrAnalyzer(interface.HashAnalyzer): | 11 class NsrlsvrAnalyzer(interface.HashAnalyzer): |
13 """Class that analyzes file hashes by consulting an nsrlsvr instance. | 12 """Analyzes file hashes by consulting an nsrlsvr instance. |
14 | 13 |
15 Attributes: | 14 Attributes: |
16 analyses_performed (int): number of analysis batches completed by this | 15 analyses_performed (int): number of analysis batches completed by this |
17 analyzer. | 16 analyzer. |
18 hashes_per_batch (int): maximum number of hashes to analyze at once. | 17 hashes_per_batch (int): maximum number of hashes to analyze at once. |
19 seconds_spent_analyzing (int): number of seconds this analyzer has spent | 18 seconds_spent_analyzing (int): number of seconds this analyzer has spent |
20 performing analysis (as opposed to waiting on queues, etc.) | 19 performing analysis (as opposed to waiting on queues, etc.) |
21 wait_after_analysis (int): number of seconds the analyzer will sleep for | 20 wait_after_analysis (int): number of seconds the analyzer will sleep for |
22 after analyzing a batch of hashes. | 21 after analyzing a batch of hashes. |
23 """ | 22 """ |
24 _RECEIVE_BUFFER_SIZE = 4096 | 23 _RECEIVE_BUFFER_SIZE = 4096 |
25 _SOCKET_TIMEOUT = 3 | 24 _SOCKET_TIMEOUT = 3 |
26 | 25 |
27 def __init__(self, hash_queue, digest_hash_recording_queue, **kwargs): | 26 def __init__(self, hash_queue, digest_hash_recording_queue, **kwargs): |
28 """Initializes an nsrlsvr analyzer thread. | 27 """Initializes an nsrlsvr analyzer thread. |
29 | 28 |
30 Args: | 29 Args: |
31 hash_queue (Queue.queue): that contains hashes to be analyzed. | 30 hash_queue (Queue.queue): that contains hashes to be analyzed. |
32 digest_hash_recording_queue (Queue.queue): that the analyzer will add | 31 digest_hash_recording_queue (Queue.queue): that the analyzer will add |
33 resulting digest hash recording to. | 32 resulting digest hash recording to. |
34 """ | 33 """ |
35 super(NsrlsvrAnalyzer, self).__init__( | 34 super(NsrlsvrAnalyzer, self).__init__( |
36 hash_queue, digest_hash_recording_queue, **kwargs) | 35 hash_queue, digest_hash_recording_queue, **kwargs) |
37 self._host = None | 36 self._host = None |
38 self._port = None | 37 self._port = None |
39 self.hashes_per_batch = 100 | 38 self.hashes_per_batch = 100 |
40 | 39 |
| 40 def _GetSocket(self): |
| 41 """Establishes a connection to an nsrlsvr instance. |
| 42 |
| 43 Returns: |
| 44 socket._socketobject: socket connected to an nsrlsvr instance or None if |
| 45 a connection cannot be established. |
| 46 """ |
| 47 try: |
| 48 return socket.create_connection( |
| 49 (self._host, self._port), self._SOCKET_TIMEOUT) |
| 50 |
| 51 except socket.error as exception: |
| 52 logging.error( |
| 53 u'Unable to connect to nsrlsvr with error: {0:s}.'.format(exception)) |
| 54 |
| 55 def _QueryHash(self, nsrl_socket, digest): |
| 56 """Queries nsrlsvr for a specfic hash. |
| 57 |
| 58 Args: |
| 59 nsrl_socket (socket._socketobject): socket of connection to nsrlsvr. |
| 60 digest (str): hash to look up. |
| 61 |
| 62 Returns: |
| 63 bool: True if the hash was found, False if not or None on error. |
| 64 """ |
| 65 query = u'QUERY {0:s}\n'.format(digest) |
| 66 |
| 67 try: |
| 68 nsrl_socket.sendall(query) |
| 69 response = nsrl_socket.recv(self._RECEIVE_BUFFER_SIZE) |
| 70 |
| 71 except socket.error as exception: |
| 72 logging.error( |
| 73 u'Unable to query nsrlsvr with error: {0:s}.'.format(exception)) |
| 74 |
| 75 if not response: |
| 76 return False |
| 77 |
| 78 # Strip end-of-line characters since they can differ per platform on which |
| 79 # nsrlsvr is running. |
| 80 response = response.strip() |
| 81 # nsrlsvr returns "OK 1" if the has was found or "OK 0" if not. |
| 82 return response == b'OK 1' |
| 83 |
41 def Analyze(self, digest_hashes): | 84 def Analyze(self, digest_hashes): |
42 """Looks up digest hashes in nsrlsvr. | 85 """Looks up hashes in nsrlsvr. |
43 | 86 |
44 Args: | 87 Args: |
45 digest_hashes (list[str]): digest hash values to look up. | 88 digest_hashes (list[str]): digest hash values to look up. |
46 | 89 |
47 Returns: | 90 Returns: |
48 list[DigestHashRecording]: digest hash recordings. | 91 list[DigestHashRecording]: digest hash recordings. |
49 """ | 92 """ |
50 logging.debug(u'Opening connection to {0:s}:{1:d}'.format( | 93 logging.debug( |
51 self._host, self._port)) | 94 u'Opening connection to {0:s}:{1:d}'.format(self._host, self._port)) |
52 | 95 |
53 try: | 96 nsrl_socket = self._GetSocket() |
54 nsrl_socket = socket.create_connection( | 97 if not nsrl_socket: |
55 (self._host, self._port), self._SOCKET_TIMEOUT) | |
56 except socket.error as exception: | |
57 logging.error(( | |
58 u'Error communicating with nsrlsvr {0:s}. nsrlsvr plugin is ' | |
59 u'aborting.').format(exception)) | |
60 self.SignalAbort() | 98 self.SignalAbort() |
61 return [] | 99 return [] |
62 | 100 |
63 digest_hash_recordings = [] | 101 hash_analyses = [] |
64 for digest_hash in digest_hashes: | 102 for digest in digest_hashes: |
65 query = u'QUERY {0:s}\n'.format(digest_hash) | 103 response = self._QueryHash(nsrl_socket, digest) |
66 try: | 104 if response is None: |
67 nsrl_socket.sendall(query) | 105 continue |
68 response = nsrl_socket.recv(self._RECEIVE_BUFFER_SIZE) | |
69 except socket.error as exception: | |
70 logging.error(( | |
71 u'Error communicating with nsrlsvr {0:s}. nsrlsvr plugin is ' | |
72 u'aborting.').format(exception)) | |
73 | 106 |
74 self.SignalAbort() | 107 hash_analysis = interface.HashAnalysis(digest, response) |
75 return digest_hash_recordings | 108 hash_analyses.append(hash_analysis) |
76 | |
77 _, _, nsrl_result = response.rpartition(u' ') | |
78 | |
79 found_in_library = nsrl_result == u'1' | |
80 if nsrl_result == u'1': | |
81 found_in_library = True | |
82 label = u'nsrl_present' | |
83 else: | |
84 found_in_library = False | |
85 label = u'nsrl_not_present' | |
86 | |
87 digest_hash_recording = hashes.DigestHashRecording( | |
88 digest_hash=digest_hash, found_in_library=found_in_library, | |
89 library=u'NSRL') | |
90 digest_hash_recording.labels = [label] | |
91 digest_hash_recordings.append(digest_hash_recording) | |
92 | 109 |
93 nsrl_socket.close() | 110 nsrl_socket.close() |
94 | 111 |
95 return digest_hash_recordings | 112 logging.debug( |
| 113 u'Closed connection to {0:s}:{1:d}'.format(self._host, self._port)) |
| 114 |
| 115 return hash_analyses |
96 | 116 |
97 def SetHost(self, host): | 117 def SetHost(self, host): |
98 """Sets the address or hostname of the server running nsrlsvr. | 118 """Sets the address or hostname of the server running nsrlsvr. |
99 | 119 |
100 Args: | 120 Args: |
101 host (str): IP address or hostname to query. | 121 host (str): IP address or hostname to query. |
102 """ | 122 """ |
103 self._host = host | 123 self._host = host |
104 | 124 |
105 def SetPort(self, port): | 125 def SetPort(self, port): |
106 """Sets the port where nsrlsvr is listening. | 126 """Sets the port where nsrlsvr is listening. |
107 | 127 |
108 Args: | 128 Args: |
109 port (int): port to query. | 129 port (int): port to query. |
110 """ | 130 """ |
111 self._port = port | 131 self._port = port |
112 | 132 |
| 133 def TestConnection(self): |
| 134 """Tests the connection to nsrlsvr. |
| 135 |
| 136 Checks if a connection can be set up and queries the server for the |
| 137 MD5 of an empty file and expects a response. The value of the response |
| 138 is not checked. |
| 139 |
| 140 Returns: |
| 141 bool: True if nsrlsvr instance is reachable. |
| 142 """ |
| 143 response = None |
| 144 nsrl_socket = self._GetSocket() |
| 145 if nsrl_socket: |
| 146 response = self._QueryHash( |
| 147 nsrl_socket, u'd41d8cd98f00b204e9800998ecf8427e') |
| 148 nsrl_socket.close() |
| 149 |
| 150 return response is not None |
| 151 |
113 | 152 |
114 class NsrlsvrAnalysisPlugin(interface.HashTaggingAnalysisPlugin): | 153 class NsrlsvrAnalysisPlugin(interface.HashTaggingAnalysisPlugin): |
115 """An analysis plugin for looking up hashes in nsrlsvr.""" | 154 """An analysis plugin for looking up hashes in nsrlsvr.""" |
116 | |
117 # nsrlsvr allows lookups using any of these hash algorithms. | |
118 REQUIRED_HASH_ATTRIBUTES = [u'sha256_hash', u'md5_hash'] | |
119 | 155 |
120 # The NSRL contains files of all different types, and can handle a high load | 156 # The NSRL contains files of all different types, and can handle a high load |
121 # so look up all files. | 157 # so look up all files. |
122 DATA_TYPES = [u'fs:stat', u'fs:stat:ntfs'] | 158 DATA_TYPES = [u'fs:stat', u'fs:stat:ntfs'] |
123 | 159 |
124 URLS = [u'https://rjhansen.github.io/nsrlsvr/'] | 160 URLS = [u'https://rjhansen.github.io/nsrlsvr/'] |
125 | 161 |
126 NAME = u'nsrlsvr' | 162 NAME = u'nsrlsvr' |
127 | 163 |
128 def __init__(self): | 164 def __init__(self): |
129 """Initializes an nsrlsvr analysis plugin.""" | 165 """Initializes an nsrlsvr analysis plugin.""" |
130 super(NsrlsvrAnalysisPlugin, self).__init__(NsrlsvrAnalyzer) | 166 super(NsrlsvrAnalysisPlugin, self).__init__(NsrlsvrAnalyzer) |
131 | 167 |
132 def SetHost(self, host): | 168 def SetHost(self, host): |
133 """Sets the address or hostname of the server running nsrlsvr. | 169 """Sets the address or hostname of the server running nsrlsvr. |
134 | 170 |
135 Args: | 171 Args: |
136 host (str): IP address or hostname to query. | 172 host (str): IP address or hostname to query. |
137 """ | 173 """ |
138 self._analyzer.SetHost(host) | 174 self._analyzer.SetHost(host) |
139 | 175 |
140 def SetPort(self, port): | 176 def SetPort(self, port): |
141 """Sets the port where nsrlsvr is listening. | 177 """Sets the port where nsrlsvr is listening. |
142 | 178 |
143 Args: | 179 Args: |
144 port (int): port to query. | 180 port (int): port to query. |
145 """ | 181 """ |
146 self._analyzer.SetPort(port) | 182 self._analyzer.SetPort(port) |
147 | 183 |
| 184 def TestConnection(self): |
| 185 """Tests the connection to nsrlsvr. |
| 186 |
| 187 Returns: |
| 188 bool: True if nsrlsvr instance is reachable. |
| 189 """ |
| 190 return self._analyzer.TestConnection() |
| 191 |
148 | 192 |
149 manager.AnalysisPluginManager.RegisterPlugin(NsrlsvrAnalysisPlugin) | 193 manager.AnalysisPluginManager.RegisterPlugin(NsrlsvrAnalysisPlugin) |
LEFT | RIGHT |