It’s hard to believe that the 5th annual HackMIT puzzle has come to an end! In the past, we have dealt with lots of memes and movies, but this year, we decided to take a more serious approach to today’s issues with the h a c k m i r r o r (jk we all just like TV and tech).
We’d like to give a special shout out to all our puzzle makers and administrators: Pat, Shreyas, Matt, Anton, Rahul, Toru, Claire, and Noah!
The entry point to our puzzle is hidden in our splash page, as always. So where is it?
Not here either.
The answer is… The entry point is hidden at the very bottom of HackMIT splash page because we want you to “explore” our cityscape till the very end (pun intended). If you mouse over the div at the bottom, you will see a faint purple glow on it.
(See the difference?)
The wording of this counter should come as a strong hint. If you still haven’t realized what’s happening, another hint is embedded in the HTML source of our splash page:
<! — Show Valid ID to Go Underground →
So, yup, keep clicking until you’ve reached 21, and welcome to the world of HACK MIRROR 😈.
For the first time in HackMIT puzzle history, we built a ~ s o c i a l ~ command center where you can interact with your fellow puzzlers and… just kidding, we have Slack for that.
You won’t see any cute cat pics from your friends on this site, just your own posts and the occasional contribution from your friendly Coach. However, it provides the standard functionality puzzlers might expect — it presents a link to each puzzle as you progress and lets you submit your answers in the form of social media posts.
Veteran puzzlers may have noticed that after solving the first puzzle this year, two puzzles unlocked. This ensured that even if you got stuck on one puzzle, you had a second to work on. This also let you set a puzzle aside for a while and come back to it at the end — though we tried to order the puzzles by difficulty, we were surprised to see all the different orders they were actually solved in.
Along with not-so-subtly ripping off everyone’s favorite evil friendly social network, we embedded a few fun extras into the command center — can you figure out how your rating is calculated? How about the number of likes on the posts?
The first puzzle leads the hackers to an innocuous-looking dashboard that looks like the ArkAngel user interface from the Black Mirror episode.
Once they click on the sight stream button, the puzzlers are greeted by a video of a pretty normal day, with anything that increases the viewer’s cortisol levels blurred out for safety.
Oh no! The button to switch of the blur filter is broken. Many puzzlers quickly realized that they had to figure out a way to un-censor the blur.
The solution was to simply inspect the HTML and delete the blur overlay video, which reveals the raw footage underneath.
We always try to keep the first puzzle on the simpler side. This allows the puzzlers to learn the mechanics of how the puzzles are supposed to work, as well as give something solvable for people with little technical background.
As always, we tried to make the puzzle answers unique to each user. This was particularly interesting to automate, since the answer is motion-tracked to the signboard. We generated a bunch of answers and wrote a janky After Effects script to automate rendering out a number of different videos.
Nosedive was the second puzzle in the HackMIT 2018 admissions puzzle sequence. It was based off the identically named Black Mirror episode “Nosedive”, where every interact between two people can be rated, and that rating determines your status in society.
Upon entering the puzzle, we’re greeted with a simple page we a few apparent features: a photo, a rating system protected by a Captcha, and a sidebar with our current rating, some more profile information, a “Prime Influencers Program” advertisement, and a help chat.
The first thing to try is the rating system. We find that we can change the rating that we want to give from 1 to 5. After we rate a picture, we see that our rating in the top right changes, depending on how many “stars” we gave the other picture.
From this, we also realize that the captcha system being used is Google’s latest version of reCaptcha, and thus is unlikely that the goal of the puzzle is to bruteforce your rating. In fact, it was impossible to reach a 5.0 by repeatedly rating other photos, because the formula used server-side to compute the new ratings limited values to be below 4.7. It was necessary to find some vulnerability in the site in order to get impossible 5.0 score.
Instead, we turn our attention to the user’s profile. We see that we can set our biography, get more information about the Prime Influencer’s Program, and type messages to a very unhelpful chatbot.
Next, we find a button tagged “Boost”. Unfortunately, clicking the button just results in a very unhelpful “Sorry, this feature is still in beta”. Continuing down the sidebar, we find a link to more information about the “Prime Influencers Program”. This link leads us to a static website with two more links: a signup page, and an API documentation.
The signup page doesn’t have anything interesting; it’s just a simple form that, once submitted, tells us whether or not we qualify for the Prime Influencer’s Program.
However, the API documentation is quite interesting. In particular, the final REST API endpoint,
/beta/boost_rating, seems to be what we want: we want our rating to be artificially boosted. Sadly for us, sending a POST request to that endpoint by ourselves results in a “bad permissions” message, which makes sense: only support is allowed to access that endpoint.
There’s only one way to interact with support: through the chatbox. After sending just a few messages, we come across the following response: “Sorry I don’t understand what your problem is. Maybe if you gave me a link I could see.”
That’s it! If we can get the support bot to visit
/beta/boost_rating on our behalf, then we could raise our rating artificially! Had the API endpoint been a GET endpoint, we would be done; we could just supply the link to
/beta/boost_rating with our username in the URL and send it to the bot. Unfortunately, we have to get the bot to send a POST request — how can we manage that?
There’s one piece of the puzzle that we haven’t played around with much: the biography. Whenever there’s arbitrary text input that can be saved or submitted, there’s potential for it be vulnerable to cross-site scripting, or XSS. Trying out the most simple type of XSS, though, is a no-go.
If we look into why, though, we can see the problem: the server is wrapping our biography in quotes, which prevents our XSS from being interpreted as HTML tags. But that means we can escape out of the quotes by purposefully inserting our own quotation marks.
Now that we can get arbitrary code to run when someone visits our profile page, we need set up that code to send a POST request to the
$.post() function, which we luckily have because jQuery is included on the site (if it wasn’t, we could use an
XMLHttpRequest to accomplish the same task).
The final thing that’s left to do is send the link to our profile to the chatbot. If we do so…
Thanks for reading so far! Check out Part 2 to see some ~ b l o c k c h a i n ~