The grinch likes to keep lists of all the people he hates. This year he’s gone digital but there might be a record that doesn’t belong!

People Rater

People Rater was the first “real” application in this challenge.

Flag3 Landing

As you can see in the picture above it was a single page with Buttons. A click on any Button showed an customized alert-box for the specific user.

Flag3 Button alert box

Thats basically what the application was made for. So but where is the vulnerability? What does it mean “there might be a record that doesn’t belong”? Let’s check the page source to get a better understanding.

<body>
<div class="container" style="margin-top:20px">
    <div class="text-center"><img src="/assets/images/grinch-networks.png" alt="Grinch Networks"></div>
    <h1 class="text-center">Grinch People Rater</h1>
    <div class="row">
        <div class="col-md-6 col-md-offset-3 text-center thelist"><div style="margin-bottom:15px"><a class="btn btn-info" data-id="eyJpZCI6Mn0=">Tea Avery</a></div><div style="margin-bottom:15px"><a class="btn btn-info" data-id="eyJpZCI6M30=">Mihai Matthews</a></div><div style="margin-bottom:15px"><a class="btn btn-info" data-id="eyJpZCI6NH0=">Ruth Ward</a></div><div style="margin-bottom:15px"><a class="btn btn-info" data-id="eyJpZCI6NX0=">Calvin Hogan</a></div><div style="margin-bottom:15px"><a class="btn btn-info" data-id="eyJpZCI6Nn0=">Reilly Cervantes</a></div></div>
        <div class="col-md-6 col-md-offset-3 text-center">
            <input type="button" class="btn btn-default loadmore" value="Load More">
        </div>
    </div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script>
    $('.thelist').on("click", "a", function(){
        $.getJSON('/people-rater/entry?id=' + $(this).attr('data-id'), function(resp){
            alert( resp.rating );
        }).fail(function(){
            alert('Request failed');
        });
    });
    var page = 0;
    $('.loadmore').click( function(){
        page++;
        $.getJSON('/people-rater/page/' + page, function(resp){
            if( resp.results.length < 5 ){
                $('.loadmore').hide();
            }
            $.each( resp.results, function(k,v){
                $('.thelist').append('<div style="margin-bottom:15px"><a class="btn btn-info" data-id="' + v.id + '">' + v.name + '</a></div>')
            });
        });
    });
    $('.loadmore').trigger('click');
</script>

</body>

Oh yes, there is a peace of JavaScript handling the button clicks with an click listener $('.thelist').on("click", "a", function(). In general words:

“Dear JavaScript, please execute the following function anytime a users clicks on a element within the div with class thelist. Thank you.”

This function calls an other HTTP-Endpoint to get the data displayed in the alert box. So lets have a look on that call.

/people-rater/entry?id= $(this).attr('data-id')

The data-id is a special attribute appended to the html a-tag holding some data. This is a common technique and always worth to inspect. In our case it contains a base64 encoded JSON. So how do we know it is base64 and a JSON? Let me tell you a little secret.

A valid JSON starts with { and some data next to it. If we encode this { "something" to base64 the result will be eyAic29tZXRoaW5nIg==. The first to characters ey are the same as we see in our People Rater web app. This is a very simple trick to get a first impression of the data encoded with base64 without even decoding it. Cool isn’t it? And the really cool thing about it: The only tool we need for this is our brain! ;)

Let’s decode the base64 encoded data-id to see what JSON payload is behind it.

To decode / encode JSON you can use a couple of tools:

Tool Command
Linux-Shell echo "eyJpZCI6Mn0=" | base64 --decode
Browser JavaScript Console atob("eyJpZCI6Mn0=")
PHP (php -a) echo base64_decode('eyJpZCI6Mn0=');
Burp Decoder See Screenshot at the bottom of the Post
Online kk.lol -> Base64 Decode

Finally, the base64 encoded string is: eyJpZCI6Mn0= -> {"id":2}. Let’s check another one: eyJpZCI6M30= -> {"id":3}.

So we have 2 and 3. But what about 1? Where is the user with the id 1? To find it out we need to create a new base64 encoded string from {"id":1} and send it to the people rater info endpoint /people-rater/entry?id={base64-encoded string}

Encoding works quite in the same way then decoding:

Tool Command
Linux-Shell echo "{"id": 1}" | base64 --encode
Browser JavaScript Console btoa('{"id": 1}')
PHP (php -a) echo base64_encode('{"id": 1}');
Burp Decoder See Screenshot at the bottom of the Post
Online kk.lol -> Base64 Encode

So our string {"id": 1} becomes eyJpZCI6IDF9. Lets send it! You can use curl or simply open the link in your browser

https://hackyholidays.h1ctf.com/people-rater/entry?id=eyJpZCI6IDF9

The result:

id	"eyJpZCI6MX0="
name	"The Grinch"
rating	"Amazing in every possible way!"
flag	"flag{b705fb11-fb55-442f-847f-0931be82ed9a}"

Our 3rd Flag! Congratulations!

We were able to get this flag just by analyzing the applications code, basic knowledge about base64 and JSON and our web-browser. No other tools are needed here. Let’s jump into challenge number 4. Follow me!

Burp

Using Burp-Suite can help to identity the different calls to the server and intercept / change it. Personally, for this challenge a browser was more then enough. But here are some screenshots from Burp to get an impression. Burp will become more interesting and important within the next challenges.

Flag3 Landing Flag3 Landing Flag3 Landing