Lab fixes for 2024

This commit is contained in:
Peter Deutsch 2023-08-16 19:19:15 -07:00
parent cfbf09b7e9
commit b7b3a02bfe
11 changed files with 389 additions and 261 deletions

View File

@ -14,20 +14,15 @@ from selenium.webdriver.chrome.options import Options
parser = argparse.ArgumentParser()
parser.add_argument("--browser", type=str, choices=["chrome", "firefox", "safari"], default="chrome", help="Browser to run automation in.")
parser.add_argument("--domains", type=str, default="google.com,youtube.com,baidu.com,facebook.com", help="Comma-separated list of domain names to collect traces from. Defaults to google.com,youtube.com,baidu.com,facebook.com")
parser.add_argument("--enable_countermeasure", type=bool, default=False, help="Set to true to enable the countermeasure. Browser must be set to Chrome. Defaults to false.")
parser.add_argument("--num_traces_per_domain", type=int, default=40, help="Number of traces to collect per domain.")
parser.add_argument("--trace_length", type=int, default=5000, help="The length of each recorded trace, in milliseconds. Defaults to 5000.")
required = parser.add_argument_group("required arguments")
required.add_argument("--out_filename", type=str, required=True, help="Name of the output file to save traces to.")
required.add_argument("--part", type=int, choices=[2, 3, 4], required=True, help="Set to the part of the lab you're working on.")
required.add_argument("--part", type=int, choices=[2, 3], required=True, help="Set to the part of the lab you're working on.")
opts = parser.parse_args()
if opts.browser != "chrome" and opts.enable_countermeasure:
print("Browser must be set to Chrome in order to enable the countermeasure.")
sys.exit(1)
if os.path.exists(opts.out_filename):
print(f"WARNING: Data already exists at {opts.out_filename}. What do you want to do?")
res = input("[C]ancel [O]verwrite ").lower()
@ -40,11 +35,6 @@ if os.path.exists(opts.out_filename):
# Start serving attacker app
app = Flask(__name__)
# Disable Flask logs
os.environ["WERKZEUG_RUN_MAIN"] = "true"
log = logging.getLogger("werkzeug")
log.disabled = True
@app.route("/")
def root():
return send_from_directory(f"part{opts.part}", "index.html")
@ -63,10 +53,6 @@ def get_browser(victim):
chrome_opts = Options()
chrome_opts.add_experimental_option("excludeSwitches", ["enable-automation"])
if opts.enable_countermeasure and victim:
# Victim has the extension enabled -- attacker does not
chrome_opts.add_extension("part4/extension.crx")
return webdriver.Chrome(options=chrome_opts)
elif opts.browser == "firefox":
return webdriver.Firefox()

View File

@ -1 +0,0 @@
../index.html

185
part2/index.html Normal file
View File

@ -0,0 +1,185 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Website Fingerprinting Lab</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
padding: 64px;
}
h1 {
font-size: 32px;
margin-bottom: 16px;
}
p {
margin-bottom: 8px;
}
#buttons {
margin-bottom: 16px;
}
button {
background: #fff;
border: 2px solid #3498db;
border-radius: 4px;
color: #3498db;
cursor: pointer;
display: inline-block;
font-size: 16px;
margin-right: 16px;
padding: 8px 16px;
}
button.disabled {
background: #ccc;
border-color: #ccc;
color: #666;
cursor: default;
}
.trace {
margin-bottom: 16px;
}
</style>
</head>
<body>
<h1>Website Fingerprinting Lab</h1>
<div id="buttons">
<button id="collect-trace">Collect trace</button>
<button id="download-traces">Download traces</button>
</div>
<div id="traces"></div>
<script src="https://d3js.org/d3.v6.js"></script>
<script type="text/javascript">
const worker = new Worker("worker.js");
const collectTraceButton = document.getElementById("collect-trace");
const downloadTracesButton = document.getElementById("download-traces");
// Default values for when the automation script isn't being used. When
// the script is in use, these values will get overwritten.
window.trace_length = 5000;
window.using_automation_script = false;
window.recording = false;
window.traces = [];
let traceIds = [];
worker.onmessage = (e) => {
window.recording = false;
const trace = JSON.parse(e.data);
window.traces.push(trace);
if (window.using_automation_script) {
// Don't display traces when automation script is in use
return;
}
// Create new trace div
const parent = document.getElementById("traces");
const div = document.createElement("div");
const traceId = "a" + Math.random().toString().substring(2, 10);
div.setAttribute("id", traceId);
div.className = "trace";
parent.appendChild(div);
traceIds.push(traceId);
// Trace dimensions
const width = parent.getBoundingClientRect().width;
const height = 64;
// Create div for new trace
const svg = d3
.select("#" + traceId)
.append("svg")
.attr("width", width)
.attr("height", height);
// Find largest value across all traces
const maxVal = d3.max(window.traces, (d) => d3.max(d));
for (let i = 0; i < window.traces.length; i++) {
// Re-visualize all traces each time in case maxVal changes
const x = d3
.scaleLinear()
.domain([0, window.traces[i].length])
.range([0, width]);
const color = d3
.scaleQuantize()
.range(["#0d0887", "#7e03a8", "#cc4778", "#f89540", "#f0f921"])
.domain([0, maxVal]);
svg
.selectAll()
.data(window.traces[i].map((x, i) => ({ index: i, value: x })))
.join("rect")
.attr("x", (d) => x(d.index))
.attr("y", 0)
.attr("width", x(1))
.attr("height", height)
.style("fill", (d) => color(d.value));
}
// Reset UI
collectTraceButton.innerText = "Collect trace";
collectTraceButton.className = "";
};
function collectTrace() {
collectTraceButton.innerText = "Collecting trace...";
collectTraceButton.className = "disabled";
window.recording = true;
worker.postMessage({
type: "start",
trace_length: window.trace_length,
});
}
collectTraceButton.onclick = () => {
if (window.recording) return;
window.recording = true;
collectTraceButton.innerText = "Starting in 3...";
collectTraceButton.className = "disabled";
setTimeout(() => {
collectTraceButton.innerText = "Starting in 2...";
setTimeout(() => {
collectTraceButton.innerText = "Starting in 1...";
setTimeout(collectTrace, 1000);
}, 1000);
}, 1000);
};
downloadTracesButton.onclick = () => {
const blob = new Blob([JSON.stringify({ traces: window.traces })], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const elem = document.createElement("a");
elem.href = url;
elem.download = "traces.json";
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
};
</script>
</body>
</html>

View File

@ -1,7 +1,11 @@
// Duration of your trace, in milliseconds
let TRACE_LENGTH;
// Number of sweep counts
// TODO: Choose an appropriate value!
let P;
// Array of length TRACE_LENGTH with your trace's values
// Number of elements in your trace
let K = 5 * 1000 / P;
// Array of length K with your trace's values
let T;
// Value of performance.now() when you started recording your trace
@ -9,7 +13,7 @@ let start;
function record() {
// Create empty array for saving trace values
T = new Array(TRACE_LENGTH);
T = new Array(K);
// Fill array with -1 so we can be sure memory is allocated
T.fill(-1, 0, T.length);
@ -17,7 +21,7 @@ function record() {
// Save start timestamp
start = performance.now();
// TODO (Exercise 2-2): Record data for TRACE_LENGTH seconds and save values to T.
// TODO: Record data for 5 seconds and save values to T.
// Once done recording, send result to main thread
postMessage(JSON.stringify(T));
@ -26,7 +30,6 @@ function record() {
// DO NOT MODIFY BELOW THIS LINE -- PROVIDED BY COURSE STAFF
self.onmessage = (e) => {
if (e.data.type === "start") {
TRACE_LENGTH = e.data.trace_length;
setTimeout(record, 0);
}
};

View File

@ -1 +0,0 @@
../index.html

185
part3/index.html Normal file
View File

@ -0,0 +1,185 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Website Fingerprinting Lab</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
padding: 64px;
}
h1 {
font-size: 32px;
margin-bottom: 16px;
}
p {
margin-bottom: 8px;
}
#buttons {
margin-bottom: 16px;
}
button {
background: #fff;
border: 2px solid #3498db;
border-radius: 4px;
color: #3498db;
cursor: pointer;
display: inline-block;
font-size: 16px;
margin-right: 16px;
padding: 8px 16px;
}
button.disabled {
background: #ccc;
border-color: #ccc;
color: #666;
cursor: default;
}
.trace {
margin-bottom: 16px;
}
</style>
</head>
<body>
<h1>Website Fingerprinting Lab</h1>
<div id="buttons">
<button id="collect-trace">Collect trace</button>
<button id="download-traces">Download traces</button>
</div>
<div id="traces"></div>
<script src="https://d3js.org/d3.v6.js"></script>
<script type="text/javascript">
const worker = new Worker("worker.js");
const collectTraceButton = document.getElementById("collect-trace");
const downloadTracesButton = document.getElementById("download-traces");
// Default values for when the automation script isn't being used. When
// the script is in use, these values will get overwritten.
window.trace_length = 5000;
window.using_automation_script = false;
window.recording = false;
window.traces = [];
let traceIds = [];
worker.onmessage = (e) => {
window.recording = false;
const trace = JSON.parse(e.data);
window.traces.push(trace);
if (window.using_automation_script) {
// Don't display traces when automation script is in use
return;
}
// Create new trace div
const parent = document.getElementById("traces");
const div = document.createElement("div");
const traceId = "a" + Math.random().toString().substring(2, 10);
div.setAttribute("id", traceId);
div.className = "trace";
parent.appendChild(div);
traceIds.push(traceId);
// Trace dimensions
const width = parent.getBoundingClientRect().width;
const height = 64;
// Create div for new trace
const svg = d3
.select("#" + traceId)
.append("svg")
.attr("width", width)
.attr("height", height);
// Find largest value across all traces
const maxVal = d3.max(window.traces, (d) => d3.max(d));
for (let i = 0; i < window.traces.length; i++) {
// Re-visualize all traces each time in case maxVal changes
const x = d3
.scaleLinear()
.domain([0, window.traces[i].length])
.range([0, width]);
const color = d3
.scaleQuantize()
.range(["#0d0887", "#7e03a8", "#cc4778", "#f89540", "#f0f921"])
.domain([0, maxVal]);
svg
.selectAll()
.data(window.traces[i].map((x, i) => ({ index: i, value: x })))
.join("rect")
.attr("x", (d) => x(d.index))
.attr("y", 0)
.attr("width", x(1))
.attr("height", height)
.style("fill", (d) => color(d.value));
}
// Reset UI
collectTraceButton.innerText = "Collect trace";
collectTraceButton.className = "";
};
function collectTrace() {
collectTraceButton.innerText = "Collecting trace...";
collectTraceButton.className = "disabled";
window.recording = true;
worker.postMessage({
type: "start",
trace_length: window.trace_length,
});
}
collectTraceButton.onclick = () => {
if (window.recording) return;
window.recording = true;
collectTraceButton.innerText = "Starting in 3...";
collectTraceButton.className = "disabled";
setTimeout(() => {
collectTraceButton.innerText = "Starting in 2...";
setTimeout(() => {
collectTraceButton.innerText = "Starting in 1...";
setTimeout(collectTrace, 1000);
}, 1000);
}, 1000);
};
downloadTracesButton.onclick = () => {
const blob = new Blob([JSON.stringify({ traces: window.traces })], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const elem = document.createElement("a");
elem.href = url;
elem.download = "traces.json";
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
};
</script>
</body>
</html>

View File

@ -1,7 +1,11 @@
// Duration of your trace, in milliseconds
let TRACE_LENGTH;
// Number of sweep counts
// TODO: Choose an appropriate value!
let P;
// Array of length TRACE_LENGTH with your trace's values
// Number of elements in your trace
let K = 5 * 1000 / P;
// Array of length K with your trace's values
let T;
// Value of performance.now() when you started recording your trace
@ -9,7 +13,7 @@ let start;
function record() {
// Create empty array for saving trace values
T = new Array(TRACE_LENGTH);
T = new Array(K);
// Fill array with -1 so we can be sure memory is allocated
T.fill(-1, 0, T.length);
@ -17,7 +21,7 @@ function record() {
// Save start timestamp
start = performance.now();
// TODO (Exercise 3-1): Record data for TRACE_LENGTH seconds and save values to T.
// TODO: Record data for 5 seconds and save values to T.
// Once done recording, send result to main thread
postMessage(JSON.stringify(T));
@ -26,7 +30,6 @@ function record() {
// DO NOT MODIFY BELOW THIS LINE -- PROVIDED BY COURSE STAFF
self.onmessage = (e) => {
if (e.data.type === "start") {
TRACE_LENGTH = e.data.trace_length;
setTimeout(record, 0);
}
};

View File

@ -1,27 +0,0 @@
import json
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
def eval():
y_pred_full, y_test_full = [], []
# Re-train 10 times in order to reduce effects of randomness
for i in range(10):
### TODO: Exercise 4-1
### 1. Load data from traces file
### 2. Split data into X_train, X_test, y_train, y_test with train_test_split
### 3. Train classifier with X_train and y_train
### 4. Use classifier to make predictions on X_test. Save the result to a variable called y_pred
# Do not modify the next two lines
y_test_full.extend(y_test)
y_pred_full.extend(y_pred)
### TODO: Exercise 4-1 (continued)
### 5. Print classification report using y_test_full and y_pred_full
if __name__ == "__main__":
eval()

Binary file not shown.

View File

@ -1,162 +0,0 @@
const domains = [
"https://www.google.com/",
"https://www.youtube.com/",
"https://www.tmall.com/",
"https://www.qq.com/",
"https://www.baidu.com/",
"https://www.sohu.com/",
"https://www.facebook.com/",
"https://www.taobao.com/",
"https://www.jd.com/",
"https://www.amazon.com/",
"https://www.yahoo.com/",
"https://www.wikipedia.org/",
"https://www.weibo.com/",
"https://sina.com.cn/",
"https://www.zoom.us/",
"http://www.xinhuanet.com/",
"https://www.live.com/",
"https://www.reddit.com/",
"https://www.netflix.com/",
"https://www.microsoft.com/",
"https://www.instagram.com/",
"https://www.office.com/",
"https://panda.tv/",
"https://www.zhanqi.tv/",
"https://www.alipay.com/",
"https://www.bing.com/",
"https://www.csdn.net/",
"https://www.vk.com/",
"https://www.myshopify.com/",
"https://www.naver.com/",
"https://www.okezone.com/",
"https://www.twitch.tv/",
"https://www.twitter.com/",
"https://www.ebay.com/",
"https://www.adobe.com/",
"https://www.tianya.cn/",
"https://www.huanqiu.com/",
"https://www.yy.com/",
"https://www.aliexpress.com/",
"https://www.linkedin.com/",
"https://www.force.com/",
"https://www.aparat.com/",
"https://www.mail.ru/",
"https://www.msn.com/",
"https://www.dropbox.com/",
"https://www.whatsapp.com/",
"https://www.apple.com/",
"https://www.1688.com/",
"https://www.wordpress.com/",
"https://www.canva.com/",
"https://www.indeed.com/",
"https://www.stackoverflow.com/",
"https://www.ok.ru/",
"https://www.so.com/",
"https://www.chase.com/",
"https://www.imdb.com/",
"https://www.slack.com/",
"https://www.etsy.com/",
"https://www.tiktok.com/",
"https://www.booking.com/",
"https://www.babytree.com/",
"https://rakuten.co.jp/",
"https://www.salesforce.com/",
"https://www.spotify.com/",
"https://www.tribunnews.com/",
"https://www.fandom.com/",
"https://www.tradingview.com/",
"https://www.github.com/",
"https://www.haosou.com/",
"https://www.paypal.com/",
"https://www.cnblogs.com/",
"https://www.alibaba.com/",
"https://www.kompas.com/",
"https://gome.com.cn/",
"https://www.walmart.com/",
"https://www.roblox.com/",
"https://www.6.cn/",
"https://www.zillow.com/",
"https://www.godaddy.com/",
"https://www.imgur.com/",
"https://www.espn.com/",
"https://www.bbc.com/",
"https://www.hao123.com/",
"https://www.pikiran-rakyat.com/",
"https://www.grammarly.com/",
"https://www.cnn.com/",
"https://www.telegram.org/",
"https://www.tumblr.com/",
"https://www.nytimes.com/",
"https://www.detik.com/",
"https://www.wetransfer.com/",
"https://www.savefrom.net/",
"https://www.rednet.cn/",
"https://www.freepik.com/",
"https://www.ilovepdf.com/",
"https://www.daum.net/",
"https://www.pinterest.com/",
"https://www.primevideo.com/",
"https://www.intuit.com/",
"https://www.medium.com/",
];
const loadTime = 5000;
let loading = false;
let startTime = 0;
function randomPing() {
const controller = new AbortController();
const id = setTimeout(
() => controller.abort(),
loadTime - (performance.now() - startTime)
);
fetch(
domains[Math.floor(Math.random() * domains.length)] +
"?" +
new Date().getTime(),
{
signal: controller.signal,
}
);
}
function activityBurst() {
switch (Math.floor(Math.random() * 2)) {
case 0:
let start = performance.now();
let counter = 0;
while (performance.now() - start < 5) {
counter += 1;
}
console.log(counter);
break;
case 1:
randomPing();
break;
}
}
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === "loading") {
if (loading) {
return;
}
startTime = performance.now();
loading = true;
for (let i = 0; i < 20; i++) {
randomPing();
}
for (let i = 0; i < loadTime / 10; i++) {
setTimeout(activityBurst, Math.random() * loadTime);
}
} else if (changeInfo.status === "complete") {
loading = false;
}
});

View File

@ -1,10 +0,0 @@
{
"name": "Lab Countermeasure",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"action": {},
"host_permissions": ["*://*/*"]
}

View File

@ -1 +0,0 @@
../index.html

View File

@ -1,32 +0,0 @@
// Duration of your trace, in milliseconds
let TRACE_LENGTH;
// Array of length TRACE_LENGTH with your trace's values
let T;
// Value of performance.now() when you started recording your trace
let start;
function record() {
// Create empty array for saving trace values
T = new Array(TRACE_LENGTH);
// Fill array with -1 so we can be sure memory is allocated
T.fill(-1, 0, T.length);
// Save start timestamp
start = performance.now();
// TODO (Exercise 4-4): Copy your solution from part 3. Optionally make changes to your part 3 solution if you need to.
// Once done recording, send result to main thread
postMessage(JSON.stringify(T));
}
// DO NOT MODIFY BELOW THIS LINE -- PROVIDED BY COURSE STAFF
self.onmessage = (e) => {
if (e.data.type === "start") {
TRACE_LENGTH = e.data.trace_length;
setTimeout(record, 0);
}
};