Facebook CTF 2019: Secret Note Keeper (Web 676)
Writeup
For TL;DR see below.
What We Got
The purpose of the web app was to store, view and search for notes. A user could log in (or register by logging in with new credentials), create notes, see their own notes, search their notes and report bugs.
A note consisted of a title and a body. The list of notes was displayed as a table below the creation form. To search for notes, the user could provide a string which is then searched in the bodies of their notes. If one or more notes matched, the results are each displayed in an own iFrame which showed the noteโs title and body. To report a bug, the user could provide a title, an explanation and a link.
First Observations ๐
Since there was a report functionality, this looked like we had to exploit a client-side vulnerability, such as XSS or CSRF. I checked if we could provide any link or just links to the web app itself, but it turned out that a bot would visit any link. Looking at the user agent, that bot was a HeadlessChrome
.
Next, I tried to trigger any kind of XSS but had no luck. I also checked if one user could visit another userโs notes but got a No notes available!
when visiting /note/${id}
, which is what was loaded in the iFrames in the search results.
So we had to do a client side attack, but had no possibility to send another user a link to a page on the web app that has data controlled by us? ๐ค
That smelled like an XS-Leak!
Leaking a Note ๐ฐ
So I checked out the XS-Leaks GitHub repo, which has a nice list of browser side channels. I checked them out and literally the first one in the list was a perfect match for our case: Window.frames.length
tells us how many frames a page contains!
I quickly checked if there was a X-Frame-Options
header, but there was none. This meant we could load a search in an iFrame and check if our query matched any note by checking if there are any frames in the result page.
The Exploit ๐ฃ
Having all that information, it was quite straightforward to come up with the following:
1 |
|
Since the timout of the bot was only 5 seconds, I had to submit a report several times. In retrospective I should have automated that, but copy-pasting PoW challenges and solutions to and from my terminal made me feel like I contributed something and did not leave all the work to my script.
After some bug fixes, like excluding %
from the alphabet because the search is implemented with a LIKE
SQL statement, I finally got the flag:fb{cr055_s173_l34|<5_4r4_c00ool!!}
TL;DR
Use iframe.contentWindow.frames.length
as an XS-Leak oracle ๐ฎ.