/
HTTPListener.py
317 lines (234 loc) · 10.1 KB
/
HTTPListener.py
1
2
3
4
5
6
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
import logging
import os
import sys
import threading
import SocketServer
import BaseHTTPServer
import ssl
import socket
import posixpath
import mimetypes
import time
import fakenet.listeners
MIME_FILE_RESPONSE = {
'text/html': 'FakeNet.html',
'image/png': 'FakeNet.png',
'image/ico': 'FakeNet.ico',
'image/jpeg': 'FakeNet.jpg',
'application/octet-stream': 'FakeNetMini.exe',
'application/x-msdownload': 'FakeNetMini.exe',
'application/x-msdos-program': 'FakeNetMini.exe',
'application/pdf': 'FakeNet.pdf',
'application/xml': 'FakeNet.html'
}
class HTTPListener():
def taste(self, data, dport):
request_methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE',
'OPTIONS', 'CONNECT', 'PATCH']
confidence = 1 if dport in [80, 443] else 0
for method in request_methods:
if data.lstrip().startswith(method):
confidence += 2
continue
return confidence
if not mimetypes.inited:
mimetypes.init() # try to read system mime.types
extensions_map = mimetypes.types_map.copy()
extensions_map.update({
'': 'text/html', # Default
})
def __init__(
self,
config={},
name='HTTPListener',
logging_level=logging.DEBUG,
):
self.logger = logging.getLogger(name)
self.logger.setLevel(logging_level)
self.config = config
self.name = name
self.local_ip = '0.0.0.0'
self.server = None
self.name = 'HTTP'
self.port = self.config.get('port', 80)
self.logger.info('Initializing...')
self.logger.debug('Initialized with config:')
for key, value in config.iteritems():
self.logger.debug(' %10s: %s', key, value)
# Initialize webroot directory
path = self.config.get('webroot','defaultFiles')
self.webroot_path = fakenet.listeners.abs_config_path(path)
if self.webroot_path is None:
self.logger.error('Could not locate webroot directory: %s', path)
sys.exit(1)
def start(self):
self.logger.debug('Starting...')
self.server = ThreadedHTTPServer((self.local_ip, int(self.config.get('port'))), ThreadedHTTPRequestHandler)
self.server.logger = self.logger
self.server.config = self.config
self.server.webroot_path = self.webroot_path
self.server.extensions_map = self.extensions_map
if self.config.get('usessl') == 'Yes':
self.logger.debug('Using SSL socket.')
keyfile_path = 'listeners/ssl_utils/privkey.pem'
keyfile_path = fakenet.listeners.abs_config_path(keyfile_path)
if keyfile_path is None:
self.logger.error('Could not locate %s', keyfile_path)
sys.exit(1)
certfile_path = 'listeners/ssl_utils/server.pem'
certfile_path = fakenet.listeners.abs_config_path(certfile_path)
if certfile_path is None:
self.logger.error('Could not locate %s', certfile_path)
sys.exit(1)
self.server.socket = ssl.wrap_socket(self.server.socket, keyfile=keyfile_path, certfile=certfile_path, server_side=True, ciphers='RSA')
self.server_thread = threading.Thread(target=self.server.serve_forever)
self.server_thread.daemon = True
self.server_thread.start()
def stop(self):
self.logger.info('Stopping...')
if self.server:
self.server.shutdown()
self.server.server_close()
class ThreadedHTTPServer(BaseHTTPServer.HTTPServer):
def handle_error(self, request, client_address):
exctype, value = sys.exc_info()[:2]
self.logger.error('Error: %s', value)
class ThreadedHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def __init__(self, *args):
BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args)
def setup(self):
self.request.settimeout(int(self.server.config.get('timeout', 5)))
BaseHTTPServer.BaseHTTPRequestHandler.setup(self)
def do_HEAD(self):
self.server.logger.info('Received HEAD request')
# Process request
self.server.logger.info('%s', '-'*80)
self.server.logger.info(self.requestline)
for line in str(self.headers).split("\n"):
self.server.logger.info(line)
self.server.logger.info('%s', '-'*80)
# Prepare response
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
def do_GET(self):
self.server.logger.info('Received a GET request.')
# Process request
self.server.logger.info('%s', '-'*80)
self.server.logger.info(self.requestline)
for line in str(self.headers).split("\n"):
self.server.logger.info(line)
self.server.logger.info('%s', '-'*80)
# Get response type based on the requested path
response, response_type = self.get_response(self.path)
# Prepare response
self.send_response(200)
self.send_header("Content-Type", response_type)
self.send_header("Content-Length", len(response))
self.end_headers()
self.wfile.write(response)
def do_POST(self):
self.server.logger.info('Received a POST request')
post_body = ''
content_len = int(self.headers.get('content-length', 0))
post_body = self.rfile.read(content_len)
# Process request
self.server.logger.info('%s', '-'*80)
self.server.logger.info(self.requestline)
for line in str(self.headers).split("\n"):
self.server.logger.info(line)
for line in post_body.split("\n"):
self.server.logger.info(line)
self.server.logger.info('%s', '-'*80)
# Store HTTP Posts
if self.server.config.get('dumphttpposts') and self.server.config['dumphttpposts'].lower() == 'yes':
http_filename = "%s_%s.txt" % (self.server.config.get('dumphttppostsfileprefix', 'http'), time.strftime("%Y%m%d_%H%M%S"))
self.server.logger.info('Storing HTTP POST headers and data to %s.', http_filename)
http_f = open(http_filename, 'wb')
if http_f:
http_f.write(self.requestline + "\r\n")
http_f.write(str(self.headers) + "\r\n")
http_f.write(post_body)
http_f.close()
else:
self.server.logger.error('Failed to write HTTP POST headers and data to %s.', http_filename)
# Get response type based on the requested path
response, response_type = self.get_response(self.path)
# Prepare response
self.send_response(200)
self.send_header("Content-Type", response_type)
self.send_header("Content-Length", len(response))
self.end_headers()
self.wfile.write(response)
def get_response(self, path):
response = "<html><head><title>FakeNet</title><body><h1>FakeNet</h1></body></html>"
response_type = 'text/html'
if path[-1] == '/':
response_type = 'text/html'
path += 'index.html'
else:
_, ext = posixpath.splitext(path)
response_type = self.server.extensions_map.get(ext, 'text/html')
# Do after checking for trailing '/' since normpath removes it
response_filename = fakenet.listeners.safe_join(self.server.webroot_path, path)
# Check the requested path exists
if not os.path.exists(response_filename):
self.server.logger.debug('Could not find path: %s', response_filename)
# Try default MIME file
response_filename = os.path.join(self.server.webroot_path, MIME_FILE_RESPONSE.get(response_type, 'FakeNet.html'))
# Check default MIME file exists
if not os.path.exists(response_filename):
self.server.logger.debug('Could not find path: %s', response_filename)
self.server.logger.error('Could not locate requested file or default handler.')
return (response, response_type)
self.server.logger.info('Responding with mime type: %s file: %s', response_type, response_filename)
try:
f = open(response_filename, 'rb')
except Exception, e:
self.server.logger.error('Failed to open response file: %s', response_filename)
response_type = 'text/html'
else:
response = f.read()
f.close()
return (response, response_type)
def log_message(self, format, *args):
return
###############################################################################
# Testing code
def test(config):
import requests
url = "%s://localhost:%s" % ('http' if config.get('usessl') == 'No' else 'https', int(config.get('port', 8080)))
print "\t[HTTPListener] Testing HEAD request."
print '-'*80
print requests.head(url, verify=False, stream=True).text
print '-'*80
print "\t[HTTPListener] Testing GET request."
print '-'*80
print requests.get(url, verify=False, stream=True).text
print '-'*80
print "\t[HTTPListener] Testing POST request."
print '-'*80
print requests.post(url, {'param1':'A'*80, 'param2':'B'*80}, verify=False, stream=True).text
print '-'*80
def main():
"""
Run from the flare-fakenet-ng root dir with the following command:
python2 -m fakenet.listeners.HTTPListener
"""
logging.basicConfig(format='%(asctime)s [%(name)15s] %(message)s', datefmt='%m/%d/%y %I:%M:%S %p', level=logging.DEBUG)
config = {'port': '8443', 'usessl': 'Yes', 'webroot': 'fakenet/defaultFiles' }
listener = HTTPListener(config)
listener.start()
###########################################################################
# Run processing
import time
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
pass
###########################################################################
# Run tests
test(config)
if __name__ == '__main__':
main()