brief intro first, will slowly update details once have time lol.

## Unlock Me(solved)

jwt algo change.

We were given account and password, minion:banana, when pressed login, it says Only admins are allowed into HQ!, check the frontend source code for login logic:

token based authentication:

(I was asked about jwt last month in interview, and I didn’t answer very well, so I went to read some attacks on jwt after the interview. So I get triggered when I saw eyJ lol)

And the comment // TODO: Add client-side verification using public.pem also hints me to do a jwt signing algorithm changing attack:

## Logged In(solved)

nodejs prototype pollution.

## You shall not pass!(solved)

postMessage -> origin bypass -> CSP bypass -> AngularJS sandbox escape -> XSS steal cookie

This challenge is very similar(about 80%) to the Bug Poc XSS 2 challenge, and my payload was mainly modified from this post: https://medium.com/@osama.alaa/bug-poc-xss-2-challenge-writeup-429790119912. To get a better understanding of this challenge, I would recommend you to read that post instead of the current post.

I’ve mirrored the website and you can download the front-end source code here: you_shall_not_pass.tar.gz

At first glance, I thought it was about SSRF, since I can post the link and ask the backend to visit. But I have no idea what are the attack surface at the backend, and seems that the headless browser will close itself in a few seconds.

So I decided to take a look at the front-end source code:

the broadcast page is in an iframe, with a suspicious form, and the javascript:

eventlistener is registered on the broadcastForm, when pressing submit, fetch will post content to /broadcast, if the returned status code is 200, it will then use postMessage to post message to the iframe of broadcasts. Let’s check what does /broadcasts do(don’t be confused with /broadcast):

let’s check frame.js:

an obvious bug was spotted on the regex(where is my $?): I just need to add one A record of yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg to my own domain, then I can bypass this origin check. A rough idea: I create a page hosted on my yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg.fakedomain , with an iframe of the actual http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41011/broadcasts, then I use postMessage to post a XSS payload to the iframe, and the iframe will process it, and add to its html source, leading to XSS, on the yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg domain, which allows me to do something evil. There are a few obstacles to overcome: • CSP: With help of this tool: https://csp-evaluator.withgoogle.com/, I realized that I can execute the scripts on its own origin • innerHTML: Notice that our post message was appended to the innerHTML, and the <script tag inserted into innerHTML after page finishes loading will not get executed. However, I can insert a new iframe, with custom content using srcdoc attribute as shown below: This will bypass innerHTML security feature and load script.js • AngularJS sandbox escape: Honestly speaking, I have no idea how this work, I just refer to the payload here: https://medium.com/@osama.alaa/bug-poc-xss-2-challenge-writeup-429790119912, and composed my final payload: The payload above would steal the cookie on http://yhi8bpzolrog3yw17fe0wlwrnwllnhic.alttablabs.sg:41011/ which turned out to be flag. ## Ransom Me This(solved) I’ve mirrored the website and you can download the front-end source code here: ransom_me_this.tar.gz Another challenge on postMessage. Same as the previous challenge, Hacked Website URL would force the backend to visit, and the Hacked Websites is an iframe: and another event listener was register as below: so the [object object] will be replaced by the number of results, and to figure out where is the message coming from, we check the source code of /search: notice that window.parent.postMessage(numResults, '*'); is used, targetOrigin is set to *, we can create our own webpage again. But how to exploit this feature? Notice that in the search text bar: Search by website title, URL, or ransom key (admin only), we know that the flag starts with govtech-csg{, and that is probably the value of Flag of our Fathers, we can try to search different combination of govtech-csg{*, and based on the number of results posted from the iframe to deduce if we have made the correct guess, and to bruteforce the next character.(sounds like boolean-based SQL injection?) Writing POC was straightforward and self-explanatory. ## Feed the Beast(not solved) blind SQLi(should be) Sadly the challenge server is down and I cannot attempt this unsolved challenge anymore. But anyway, life is not always perfect. ## Breaking Free(solved) Express.JS features in handling HEAD/GET -> variable override -> ssrf We were only giving one JS file, which seems to be much easier lol: The logic is very clear, first vulnerability was immediately spotted as below: in my request body, if I set another url parameter, it is probably going to overwrite the original url value COVID_BACKEND, and when payload is passed to fetchResource function, it will leads to SSRF. However, I need to make dbController.changeBotID(payload.oldBotID, payload.newBotID) return true, and I don’t know the logic of changeBotID. One reasonable assumption is that oldBotID must exist. Bruteforcing seems to be infeasible, const COVID_BOT_ID_REGEX = /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}$/g , the space is too large, tried a few special BotID all does not exist, then I gave up on bruteforcing.

Another inconsistency here:

inconsistency leads to bugs and then leads to vulnerability, I tried HEAD method, surprisingly, it will be handled by router.get, but in the first validation process, it is going to the //Handle POST code block.

I noticed this issue and referred to the HTTP/1.1 RFC 2616:

The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. The metainformation contained in the HTTP headers in response to a HEAD request SHOULD be identical to the information sent in response to a GET request.

It seems to be an intended behavior.

So it is easy now: I can issue HEAD request to register a BotID, then issue a POST request to change the BotID, and overwrite the url to web_challenge_5_dummy/flag/42#, exploiting SSRF to get the flag.

For some reason I don’t know(might be because //We enroll a maximum of 100 UUID at any time!!), the success rate is very low, but still working with burp intruder:

#### End Note

In my personal point of view, the difficulty level should be logged in < unlock me < breaking free < ransom me this < you shall not pass, this ranking is very subjective, given my poor JS knowledge. However, breaking free is 3000 points while you shall not pass is only 1000 points.