diff --git a/browser/README.md b/browser/README.md new file mode 100644 index 0000000..ad86915 --- /dev/null +++ b/browser/README.md @@ -0,0 +1,68 @@ +# NFStream Browser Extensions (experimental) + +This repository contains NFStream browser extensions (Chrome and Firefox) that was implemented +as part of a exploratory work for end-host ground truth generation. +The main idea was to explore what could be extracted from a browser context data (tab_id, tab_url, +etc.) to enrich end-host visibility when the process generating the traffic is a browser. + +This implementation was first ([**this commit**][commit]) introduced in NFStream as experimental/draft code +(not officially documented), we decided for clarity purposes to isolate it and provide it in this separate repository. + +Feel free to use it or extend it as part of your research experiments. + +## How it works? + +NFStream browser extension is simply a listener on `onResponseStarted` of [**webRequest**][webrequest] browser API that +perform the following actions: + +* extract features such as timestamp, tab identifier, request identifier, tab is active, server IP address and URL. +* export it as HTTP POST it a JSON format to **localhost:export_port**. + +`export_port` is configurable by the user in the nfstream extension options menu and is set by default to `28314`. + +## How to use it? + +### Setup your environment + +``` bash +python3 -m pip install --upgrade pip +virtualenv venv-py -p /usr/bin/python3 +source venv-py/bin/activate +``` + +### Load the extensions + +#### On Chrome + +* Open: chrome://extensions +* Enable `Developer Mode`. +* Click on `Load unpacked`. +* Select `nfstream/browser/chrome` folder. +* Now NFStream extension is loaded, you can navigate to details menu and then select extension options to configure +a specific export port. + +#### On Firefox + +* Open `about:debugging` +* Click `This Firefox` +* Click `Load Temporary Add-on` +* Open the `nfstream/browser/firefox` and select any file inside the extension, +* Now NFStream extension is loaded, you can navigate to `about:addons` and then select extension preferences +to configure specific export port. + + +### extension_server + +``` +usage: python3 extension_server.py listening_port_number + +positional arguments: + listening_port_number port number to listen to (default 28314). +``` + +The script will display exported data from the extensions. That's it! + +[webrequest]: https://developer.chrome.com/docs/extensions/reference/webRequest/ +[commit]: https://github.com/nfstream/nfstream/commit/729e69d97bac69c12609b28271d32542e9f5dc45 + + diff --git a/browser/chrome/background.js b/browser/chrome/background.js new file mode 100644 index 0000000..197d359 --- /dev/null +++ b/browser/chrome/background.js @@ -0,0 +1,36 @@ +/* +------------------------------------------------------------------------------------------------------------------------ +background.js +Copyright (C) 2019-22 - NFStream Developers +This file is part of NFStream, a Flexible Network Data Analysis Framework (https://www.nfstream.org/). +NFStream is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. +NFStream is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with NFStream. +If not, see . +------------------------------------------------------------------------------------------------------------------------ +*/ + +function send_event(event) { + chrome.tabs.query({active:true, currentWindow:true}, + function(tabs) { + if ((! String(event.ip).match("127.0.0.1")) && (! (event.ip == undefined)) + && (! (tabs[0] == undefined)) && (! (tabs[0].url == undefined)) + && (! (tabs[0].id == undefined)) && (! (event.timeStamp == undefined))) { // Filter out + chrome.tabs.sendMessage(tabs[0].id, {browser: "chrome", + version: "1.0", + timestamp: event.timeStamp, + tab_id: tabs[0].id, + req_id: event.requestId, + ip_address: event.ip, + tab_is_active: tabs[0].active, + tab_url: tabs[0].url}); + } + }); +} + + +chrome.webRequest.onResponseStarted.addListener(send_event, {urls: [""]}, ["extraHeaders", + "responseHeaders"]); \ No newline at end of file diff --git a/browser/chrome/images/nfstream_icon_128.png b/browser/chrome/images/nfstream_icon_128.png new file mode 100644 index 0000000..e1a6e05 Binary files /dev/null and b/browser/chrome/images/nfstream_icon_128.png differ diff --git a/browser/chrome/images/nfstream_icon_16.png b/browser/chrome/images/nfstream_icon_16.png new file mode 100644 index 0000000..4abbc29 Binary files /dev/null and b/browser/chrome/images/nfstream_icon_16.png differ diff --git a/browser/chrome/images/nfstream_icon_32.png b/browser/chrome/images/nfstream_icon_32.png new file mode 100644 index 0000000..6dec620 Binary files /dev/null and b/browser/chrome/images/nfstream_icon_32.png differ diff --git a/browser/chrome/images/nfstream_icon_48.png b/browser/chrome/images/nfstream_icon_48.png new file mode 100644 index 0000000..05ddaba Binary files /dev/null and b/browser/chrome/images/nfstream_icon_48.png differ diff --git a/browser/chrome/manifest.json b/browser/chrome/manifest.json new file mode 100644 index 0000000..63f539d --- /dev/null +++ b/browser/chrome/manifest.json @@ -0,0 +1,28 @@ +{ + "manifest_version": 2, + "version": "1.0", + "name": "NFStream for Chrome", + "description": "NFStream Chrome extension for system visibility.", + "author": "NFStream Developers", + "homepage_url" : "https://www.nfstream.org/", + "icons": { + "16": "images/nfstream_icon_16.png", + "32": "images/nfstream_icon_32.png", + "48": "images/nfstream_icon_48.png", + "128": "images/nfstream_icon_128.png" + }, + "background" : { + "scripts": ["background.js"] + }, + "options_ui": { + "page": "options.html", + "open_in_tab": false + }, + "content_scripts": [ + { + "matches": [""], + "js": ["nfstream.js"] + } + ], + "permissions": ["webRequest", "storage", "tabs", ""] +} \ No newline at end of file diff --git a/browser/chrome/nfstream.js b/browser/chrome/nfstream.js new file mode 100644 index 0000000..0f896db --- /dev/null +++ b/browser/chrome/nfstream.js @@ -0,0 +1,33 @@ +/* +------------------------------------------------------------------------------------------------------------------------ +nfstream.js +Copyright (C) 2019-22 - NFStream Developers +This file is part of NFStream, a Flexible Network Data Analysis Framework (https://www.nfstream.org/). +NFStream is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. +NFStream is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with NFStream. +If not, see . +------------------------------------------------------------------------------------------------------------------------ +*/ + +function upload_to_nfstream(msg) { + chrome.storage.local.get({export_port: '28314'}, + function (items) { + var xhr = new XMLHttpRequest(); + var uid = "nfstream-" + msg.browser + "-" + msg.tab_id + "-" + msg.req_id + ".json"; + try { + xhr.open("POST", + "http://localhost:" + items.export_port + "/" + uid, + true); + xhr.setRequestHeader("Content-type", "application/json"); + xhr.send(JSON.stringify(msg)); + } catch (e) { + // ignore + }}); +} + + +chrome.runtime.onMessage.addListener(upload_to_nfstream); \ No newline at end of file diff --git a/browser/chrome/options.html b/browser/chrome/options.html new file mode 100644 index 0000000..29cefff --- /dev/null +++ b/browser/chrome/options.html @@ -0,0 +1,33 @@ + + + + + + + + + NFStream for Chrome + +
+ + +

+Update + diff --git a/browser/chrome/options.js b/browser/chrome/options.js new file mode 100644 index 0000000..20365d3 --- /dev/null +++ b/browser/chrome/options.js @@ -0,0 +1,44 @@ +/* +------------------------------------------------------------------------------------------------------------------------ +options.js +Copyright (C) 2019-22 - NFStream Developers +This file is part of NFStream, a Flexible Network Data Analysis Framework (https://www.nfstream.org/). +NFStream is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. +NFStream is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with NFStream. +If not, see . +------------------------------------------------------------------------------------------------------------------------ +*/ + +function restore_options() { + chrome.storage.local.get({export_port: '28314'}, + function(items) { + document.getElementById('export_port').value = items.export_port; + }); +} + +function update_input(element, color) { + element.style.border = '3px solid'; + element.style.borderColor = color; + setTimeout(function() { + element.style.border = ''; + element.style.borderColor = ''; + restore_options(); + }, 750); +} + +function update_options() { + var export_port = document.getElementById('export_port'); + if ((export_port.value < 0) || (export_port.value > 65525) || (export_port.value == undefined)) { + update_input(export_port, "red"); + } else { + chrome.storage.local.set({export_port: export_port.value}, + update_input(export_port, "green")); + } +} + +document.addEventListener('DOMContentLoaded', restore_options); +document.getElementById('update').addEventListener('click', update_options); \ No newline at end of file diff --git a/browser/chrome/style.css b/browser/chrome/style.css new file mode 100644 index 0000000..70fb227 --- /dev/null +++ b/browser/chrome/style.css @@ -0,0 +1,43 @@ +/* +------------------------------------------------------------------------------------------------------------------------ +style.css +Copyright (C) 2019-22 - NFStream Developers +This file is part of NFStream, a Flexible Network Data Analysis Framework (https://www.nfstream.org/). +NFStream is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. +NFStream is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with NFStream. +If not, see . +------------------------------------------------------------------------------------------------------------------------ +*/ + +div {text-align: center;} + +a.custom_button{ + display:inline-block; + padding:0.3em 1.2em; + margin:0 0.1em 0.1em 0; + border:0.16em solid rgba(255,255,255,0); + border-radius:2em; + box-sizing: border-box; + text-decoration:none; + font-family:'Roboto',sans-serif; + font-weight:300; + color:#FFFFFF; + text-shadow: 0 0.04em 0.04em rgba(0,0,0,0.35); + text-align:center; + transition: all 0.2s; +} + +a.custom_button:hover{ + border-color: rgba(255,255,255,1); +} + +@media all and (max-width:30em){ + a.custom_button{ + display:block; + margin:0.2em auto; + } +} diff --git a/browser/extension_server.py b/browser/extension_server.py new file mode 100644 index 0000000..4ff7b9b --- /dev/null +++ b/browser/extension_server.py @@ -0,0 +1,93 @@ +""" +------------------------------------------------------------------------------------------------------------------------ +extension_server.py +Copyright (C) 2019-22 - NFStream Developers +This file is part of NFStream, a Flexible Network Data Analysis Framework (https://www.nfstream.org/). +NFStream is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. +NFStream is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with NFStream. +If not, see . +------------------------------------------------------------------------------------------------------------------------ +""" + +from http.server import HTTPServer, BaseHTTPRequestHandler +from collections import namedtuple +import json +import sys + + +NFRequest = namedtuple('NFRequest', ['browser', + 'timestamp', + 'remote_ip', + 'tab_id', + 'request_id', + 'tab_is_active', + 'tab_url']) + + +class NFRequestHandler(BaseHTTPRequestHandler): + """ Handler for HTTP request from browser extension """ + def _set_headers(self): + """ headers setter """ + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') + self.send_header("Access-Control-Allow-Headers", "X-Requested-With") + self.send_header("Access-Control-Allow-Headers", "content-type") + self.end_headers() + + def do_OPTIONS(self): + """ OPTIONS handler """ + self.send_response(200) + self._set_headers() + + def do_POST(self): + """ POST handler """ + if self.path.endswith('.json') and (self.path.startswith('/nfstream-chrome') or + self.path.startswith('/nfstream-firefox')): + length = self.headers['content-length'] + data = json.loads(self.rfile.read(int(length))) + self.send_response(200) + self._set_headers() + request = NFRequest(data["browser"], + float(data["timestamp"]), + data["ip_address"], + data["tab_id"], + data["req_id"], + data["tab_is_active"], + data["tab_url"]) + + # For sake of brevity, we print it only + print(request) + # However you can use this server in a child process and poll from it using a Queue (channel) + # self.server.channel.put(request) + else: + self.send_response(400) + self._set_headers() + + def log_message(self, fmt, *args): + return + + +class NFRequestServer(HTTPServer): + """ NFRequest HTTP server""" + def __init__(self, *args): + HTTPServer.__init__(self, *args) + self.stopped = False + # self.channel = channel + + +if __name__ == '__main__': # Mandatory if you are running on Windows Platform + try: + port = int(sys.argv[1]) + except IndexError: # not specified + port = 28314 + server_address = ('', port) # localhost with configurable port + server = NFRequestServer(server_address, NFRequestHandler) + try: + while not server.stopped: + server.handle_request() + except KeyboardInterrupt: + exit(0) diff --git a/browser/firefox/background.js b/browser/firefox/background.js new file mode 100644 index 0000000..c02ed1a --- /dev/null +++ b/browser/firefox/background.js @@ -0,0 +1,35 @@ +/* +------------------------------------------------------------------------------------------------------------------------ +background.js +Copyright (C) 2019-22 - NFStream Developers +This file is part of NFStream, a Flexible Network Data Analysis Framework (https://www.nfstream.org/). +NFStream is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. +NFStream is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with NFStream. +If not, see . +------------------------------------------------------------------------------------------------------------------------ +*/ + +function send_event(event) { + browser.tabs.query({active:true, currentWindow:true}, + function(tabs) { + if ((! String(event.ip).match("127.0.0.1")) && (! (event.ip == undefined)) + && (! (tabs[0] == undefined)) && (! (tabs[0].url == undefined)) + && (! (tabs[0].id == undefined)) && (! (event.timeStamp == undefined))) { // Filter out + browser.tabs.sendMessage(tabs[0].id, {browser: "firefox", + version: "1.0", + timestamp: event.timeStamp, + tab_id: tabs[0].id, + req_id: event.requestId, + ip_address: event.ip, + tab_is_active: tabs[0].active, + tab_url: tabs[0].url}); + } + }); +} + + +browser.webRequest.onResponseStarted.addListener(send_event, {urls: [""]}, ["responseHeaders"]); \ No newline at end of file diff --git a/browser/firefox/images/nfstream_icon_128.png b/browser/firefox/images/nfstream_icon_128.png new file mode 100644 index 0000000..e1a6e05 Binary files /dev/null and b/browser/firefox/images/nfstream_icon_128.png differ diff --git a/browser/firefox/images/nfstream_icon_16.png b/browser/firefox/images/nfstream_icon_16.png new file mode 100644 index 0000000..4abbc29 Binary files /dev/null and b/browser/firefox/images/nfstream_icon_16.png differ diff --git a/browser/firefox/images/nfstream_icon_32.png b/browser/firefox/images/nfstream_icon_32.png new file mode 100644 index 0000000..6dec620 Binary files /dev/null and b/browser/firefox/images/nfstream_icon_32.png differ diff --git a/browser/firefox/images/nfstream_icon_48.png b/browser/firefox/images/nfstream_icon_48.png new file mode 100644 index 0000000..05ddaba Binary files /dev/null and b/browser/firefox/images/nfstream_icon_48.png differ diff --git a/browser/firefox/manifest.json b/browser/firefox/manifest.json new file mode 100644 index 0000000..17bd342 --- /dev/null +++ b/browser/firefox/manifest.json @@ -0,0 +1,28 @@ +{ + "manifest_version": 2, + "version": "1.0", + "name": "NFStream for Firefox", + "description": "NFStream Firefox extension for system visibility.", + "author": "NFStream Developers", + "homepage_url" : "https://www.nfstream.org/", + "icons": { + "16": "images/nfstream_icon_16.png", + "32": "images/nfstream_icon_32.png", + "48": "images/nfstream_icon_48.png", + "128": "images/nfstream_icon_128.png" + }, + "background" : { + "scripts": ["background.js"] + }, + "options_ui": { + "page": "options.html", + "open_in_tab": false + }, + "content_scripts": [ + { + "matches": [""], + "js": ["nfstream.js"] + } + ], + "permissions": ["webRequest", "storage", "tabs", ""] +} \ No newline at end of file diff --git a/browser/firefox/nfstream.js b/browser/firefox/nfstream.js new file mode 100644 index 0000000..3d904c8 --- /dev/null +++ b/browser/firefox/nfstream.js @@ -0,0 +1,33 @@ +/* +------------------------------------------------------------------------------------------------------------------------ +nfstream.js +Copyright (C) 2019-22 - NFStream Developers +This file is part of NFStream, a Flexible Network Data Analysis Framework (https://www.nfstream.org/). +NFStream is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. +NFStream is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with NFStream. +If not, see . +------------------------------------------------------------------------------------------------------------------------ +*/ + +function upload_to_nfstream(msg) { + browser.storage.local.get({export_port: '28314'}, + function (items) { + var xhr = new XMLHttpRequest(); + var uid = "nfstream-" + msg.browser + "-" + msg.tab_id + "-" + msg.req_id + ".json"; + try { + xhr.open("POST", + "http://localhost:" + items.export_port + "/" + uid, + true); + xhr.setRequestHeader("Content-type", "application/json"); + xhr.send(JSON.stringify(msg)); + } catch (e) { + // ignore + }}); +} + + +browser.runtime.onMessage.addListener(upload_to_nfstream); \ No newline at end of file diff --git a/browser/firefox/options.html b/browser/firefox/options.html new file mode 100644 index 0000000..29cefff --- /dev/null +++ b/browser/firefox/options.html @@ -0,0 +1,33 @@ + + + + + + + + + NFStream for Chrome + +
+ + +

+Update + diff --git a/browser/firefox/options.js b/browser/firefox/options.js new file mode 100644 index 0000000..22be360 --- /dev/null +++ b/browser/firefox/options.js @@ -0,0 +1,44 @@ +/* +------------------------------------------------------------------------------------------------------------------------ +options.js +Copyright (C) 2019-22 - NFStream Developers +This file is part of NFStream, a Flexible Network Data Analysis Framework (https://www.nfstream.org/). +NFStream is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. +NFStream is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with NFStream. +If not, see . +------------------------------------------------------------------------------------------------------------------------ +*/ + +function restore_options() { + browser.storage.local.get({export_port: '28314'}, + function(items) { + document.getElementById('export_port').value = items.export_port; + }); +} + +function update_input(element, color) { + element.style.border = '3px solid'; + element.style.borderColor = color; + setTimeout(function() { + element.style.border = ''; + element.style.borderColor = ''; + restore_options(); + }, 750); +} + +function update_options() { + var export_port = document.getElementById('export_port'); + if ((export_port.value < 0) || (export_port.value > 65525) || (export_port.value == undefined)) { + update_input(export_port, "red"); + } else { + browser.storage.local.set({export_port: export_port.value}, + update_input(export_port, "green")); + } +} + +document.addEventListener('DOMContentLoaded', restore_options); +document.getElementById('update').addEventListener('click', update_options); \ No newline at end of file diff --git a/browser/firefox/style.css b/browser/firefox/style.css new file mode 100644 index 0000000..70fb227 --- /dev/null +++ b/browser/firefox/style.css @@ -0,0 +1,43 @@ +/* +------------------------------------------------------------------------------------------------------------------------ +style.css +Copyright (C) 2019-22 - NFStream Developers +This file is part of NFStream, a Flexible Network Data Analysis Framework (https://www.nfstream.org/). +NFStream is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later +version. +NFStream is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License along with NFStream. +If not, see . +------------------------------------------------------------------------------------------------------------------------ +*/ + +div {text-align: center;} + +a.custom_button{ + display:inline-block; + padding:0.3em 1.2em; + margin:0 0.1em 0.1em 0; + border:0.16em solid rgba(255,255,255,0); + border-radius:2em; + box-sizing: border-box; + text-decoration:none; + font-family:'Roboto',sans-serif; + font-weight:300; + color:#FFFFFF; + text-shadow: 0 0.04em 0.04em rgba(0,0,0,0.35); + text-align:center; + transition: all 0.2s; +} + +a.custom_button:hover{ + border-color: rgba(255,255,255,1); +} + +@media all and (max-width:30em){ + a.custom_button{ + display:block; + margin:0.2em auto; + } +}