Skip to content

Commit edd67f8

Browse files
author
Justin Angel
committed
adding b64 obfuscation
1 parent dc8aad4 commit edd67f8

File tree

4 files changed

+225
-6
lines changed

4 files changed

+225
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.swp

server.py

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,23 +85,60 @@ def list_directory(self, path):
8585
f.write(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
8686
f.write(("<html>\n<title>Directory listing for %s</title>\n" % displaypath).encode())
8787
f.write(("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath).encode())
88+
89+
if self.B64_ENCODE_PAYLOAD:
90+
# Insert JavaScript for decoding
91+
f.write("<script type='text/javascript'>\n{}</script>\n".format(
92+
CorsHandler.B64_JS_TEMPLATE).encode('utf8')
93+
)
94+
8895
f.write(b"<hr>\n")
89-
f.write(b"<form ENCTYPE=\"multipart/form-data\" method=\"post\">")
96+
if self.B64_ENCODE_PAYLOAD:
97+
f.write(b"<form ENCTYPE=\"multipart/form-data\" onsubmit=\"return encoder(2);\">")
98+
else:
99+
f.write(b"<form ENCTYPE=\"multipart/form-data\" method=\"post\">")
90100
f.write(b"<input name=\"file\" type=\"file\"/>")
91101
f.write(b"<input type=\"submit\" value=\"upload\"/></form>\n")
92102
f.write(b"<hr>\n<ul>\n")
103+
104+
# Generate download links
93105
for name in list:
106+
94107
fullname = os.path.join(path, name)
95108
displayname = linkname = name
109+
96110
# Append / for directories or @ for symbolic links
97111
if os.path.isdir(fullname):
98112
displayname = name + "/"
99113
linkname = name + "/"
100114
if os.path.islink(fullname):
101115
displayname = name + "@"
102116
# Note: a link to a directory displays with @ and links with /
103-
f.write(('<li><a href="%s">%s</a>\n'
104-
% (urllib.parse.quote(linkname), html.escape(displayname))).encode())
117+
118+
linkname = urllib.parse.quote(linkname)
119+
displayname = html.escape(displayname)
120+
121+
# TODO: Handle insertion of JS links
122+
if self.B64_ENCODE_PAYLOAD:
123+
# implenet call to JS via onClick
124+
125+
f.write(
126+
'<li><a href="javascript:decoder(\'{}\')">{}</a>\n'.format(
127+
linkname,
128+
linkname,
129+
displayname).encode('utf8')
130+
)
131+
132+
else:
133+
134+
# Non-JS links
135+
f.write(
136+
'<li><a href="{}">{}</a>\n'.format(
137+
linkname,
138+
displayname
139+
).encode('utf8')
140+
)
141+
105142
f.write(b"</ul>\n<hr>\n</body>\n</html>\n")
106143
length = f.tell()
107144
f.seek(0)
@@ -113,6 +150,9 @@ def list_directory(self, path):
113150

114151
class CorsHandler(http.server.SimpleHTTPRequestHandler):
115152

153+
B64_ENCODE_PAYLOAD = False
154+
B64_JS_TEMPLATE = None
155+
116156
@property
117157
def client_ip(self):
118158
return self.client_address[0]
@@ -206,6 +246,14 @@ def send_head(self):
206246
# Adding Access-Control-Allow-Origin header
207247
self.send_header('Access-Control-Allow-Origin','*')
208248
self.end_headers()
249+
250+
if CorsHandler.B64_ENCODE_PAYLOAD:
251+
encoded = f.read()
252+
# TODO: Make iterations configurable
253+
for i in range(0,2):
254+
encoded = b64encode(encoded)
255+
return BytesIO(encoded)
256+
209257
return f
210258
except:
211259
f.close()
@@ -298,12 +346,15 @@ def deal_post_data(self):
298346
remainbytes -= len(line)
299347
line = self.rfile.readline()
300348
remainbytes -= len(line)
349+
350+
# Prepare the output file
301351
try:
302352
out = open(fn, 'wb')
303353
except IOError:
304354
self.log_error("Cannot write to target directory. (Permissions Problem)")
305355
return (False, "Can't create file to write, do you have permission to write?")
306-
356+
357+
# Read file content
307358
preline = self.rfile.readline()
308359
remainbytes -= len(preline)
309360
while remainbytes > 0:
@@ -315,6 +366,20 @@ def deal_post_data(self):
315366
preline = preline[0:-1]
316367
out.write(preline)
317368
out.close()
369+
370+
# TODO: Handle decoding of uploads
371+
if CorsHandler.B64_ENCODE_PAYLOAD:
372+
373+
# Read in the file content
374+
with open(fn,'rb') as f: data = f.read()
375+
376+
# Decode the data
377+
for i in range(0,2):
378+
data = b64decode(data)
379+
380+
# Open and write the decoded content
381+
with open(fn,'wb') as f: f.write(data)
382+
318383
self.log_message("File '%s' uploaded by '%s:%d'",fn, self.client_ip, self.client_port)
319384
return (True, "File '%s' upload success!" % fn)
320385
else:
@@ -333,7 +398,7 @@ def do_HEAD(self):
333398

334399
def do_AUTHHEAD(self):
335400
self.send_response(401)
336-
self.send_header('WWW-Authenticate', 'Basic realm="simple_https_server", charset="UTF-8"')
401+
self.send_header('WWW-Authenticate', 'Basic realm="internal", charset="UTF-8"')
337402
self.send_header('Content-Type', 'text/html')
338403
self.end_headers()
339404

@@ -362,7 +427,21 @@ def do_basic_POST(self):
362427
self.wfile.write(bytes('Not authenticated','utf-8'))
363428

364429
def run_server(interface, port, keyfile, certfile,
365-
webroot=None, enable_uploads=False, *args, **kwargs):
430+
webroot=None, enable_uploads=False, enable_b64=False,
431+
*args, **kwargs):
432+
433+
# ============================
434+
# CONFIGURE BASE64 OBFUSCATION
435+
# ============================
436+
437+
CorsHandler.B64_ENCODE_PAYLOAD = enable_b64
438+
439+
if enable_b64:
440+
441+
# Get the JavaScript template
442+
with open(str(Path(__file__).resolve().parent.absolute()) + \
443+
'/templates/b64_obfuscation.js','r') as infile:
444+
CorsHandler.B64_JS_TEMPLATE = infile.read()
366445

367446
webroot=webroot or '.'
368447

@@ -500,6 +579,16 @@ def generate_certificate(certfile, keyfile):
500579
auth_arg_group.add_argument('--basic-password','-bp',
501580
help='Password for basic authentication')
502581

582+
# obfuscation
583+
obf_group = parser.add_argument_group('Obfuscation',
584+
'''Configure the server to implement file obfuscation.
585+
JavaScript is injected into the browser to handle
586+
obfuscation at the client.
587+
''')
588+
obf_group.add_argument('--enable-b64',
589+
help='Enable double base 64 obfuscation of files.',
590+
action='store_true')
591+
503592
args = parser.parse_args()
504593

505594
# handle basic auth credentials

templates/b64_obfuscation.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
function contentToBuffer(b64){
2+
var len = b64.length;
3+
bytes = new Uint8Array(len);
4+
for(var i=0; i<len; i++){
5+
bytes[i] = b64.charCodeAt(i);
6+
}
7+
return bytes.buffer;
8+
}
9+
10+
function decode64(b64,iterations){
11+
if(!iterations) {iterations = 1};
12+
for(;iterations>0;iterations--){
13+
b64 = window.atob(b64);
14+
}
15+
buff = contentToBuffer(b64);
16+
var blob = new Blob([buff], {type: 'octet/stream'});
17+
return blob;
18+
}
19+
20+
21+
// For uploads
22+
function encoder(iterations){
23+
var form = document.getElementsByName("form");
24+
var input = document.querySelector('input[type="file"]')
25+
var file = input.files[0];
26+
if(!iterations) {iterations = 1};
27+
var reader = new FileReader();
28+
var data;
29+
reader.onloadend = function() {
30+
31+
// Process the file upload
32+
var data = reader.result;
33+
for(;iterations>0;iterations--){
34+
data = window.btoa(data);
35+
}
36+
37+
console.log(data);
38+
39+
// Create new form data
40+
var formData = new FormData();
41+
42+
formData.append("file", new Blob([data],{type: "text/base64"}), file.name);
43+
44+
console.log(formData);
45+
46+
// Make the HTTP request
47+
var xhttp = new XMLHttpRequest();
48+
xhttp.open("POST", "/", true);
49+
xhttp.send(formData);
50+
document.location.reload();
51+
}
52+
reader.readAsBinaryString(file);
53+
return false;
54+
}
55+
56+
// For downloads
57+
function decoder(fname){
58+
console.log('decoder called');
59+
var xhttp = new XMLHttpRequest();
60+
xhttp.open("GET",fname,true);
61+
xhttp.onreadystatechange = function() {
62+
if(this.readyState == 4 && this.status == 200){
63+
var blob = decode64(this.responseText, 2);
64+
var a = document.createElement('a');
65+
var u = window.URL.createObjectURL(blob);
66+
document.body.appendChild(a);
67+
a.style = 'display:none';
68+
a.href = u;
69+
a.download = fname;
70+
a.click();
71+
window.URL.revokeObjectURL(u);
72+
}
73+
}
74+
xhttp.send();
75+
}

templates/obfuscation.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
function contentToBuffer(b64){
2+
var len = b64.length;
3+
bytes = new Uint8Array(len);
4+
for(var i=0; i<len; i++){
5+
bytes[i] = b64.charCodeAt(i);
6+
}
7+
return bytes.buffer;
8+
}
9+
10+
function decode64(b64,iterations){
11+
if(!iterations) {iterations = 1};
12+
for(;iterations>0;iterations--){
13+
b64 = window.atob(b64);
14+
}
15+
buff = contentToBuffer(b64);
16+
var blob = new Blob([buff], {type: 'octet/stream'});
17+
return blob;
18+
}
19+
20+
21+
// For uploads
22+
function encoder(blob,iterations){
23+
if(!iterations) {iterations = 1};
24+
var reader = new FileReader();
25+
var data;
26+
reader.onloadend = function() {
27+
var data = reader.result;
28+
for(;iterations>0;iterations--){
29+
data = window.btoa(data);
30+
// TODO: Perform POST request
31+
}
32+
}
33+
reader.readAsbinaryString(blob);
34+
}
35+
36+
// For downloads
37+
function decoder(fname){
38+
var xhttp = new XMLHttpRequest();
39+
xhttp.open("GET",fname,true);
40+
xhttp.onreadystatechange = function() {
41+
if(this.readyState == 4 && this.status == 200){
42+
var blob = decode64(this.responseText, 2);
43+
var a = document.createElement('a');
44+
var u = window.URL.createObjectURL(blob);
45+
document.body.appendChild(a);
46+
a.style = 'display:none';
47+
a.href = u;
48+
a.download = fname;
49+
a.click();
50+
window.URL.revokeObjectURL(u);
51+
}
52+
}
53+
xhttp.send();
54+
}

0 commit comments

Comments
 (0)