zer0pts CTF 2021 - Author's writeup for PDF Generator
zer0pts CTF 2021 was held from March 6th to March 7th. I've created the challenge named PDF Generator for the CTF, this is the first time I am creating a challenge for a CTF, I've learned so many things and looking forward to creating awesome challenges in the future.
Prototype pollution in the location.search parameters parsing, finding a script gadget in Vue.js to get XSS. And interesting part is to read the flag in the PDF using postMessage supported by chrome pdf_viewer.
parseQuery function is vulnerable to prototype pollution which is a similar implementation of canjs-deparam module.
I've added a little filter to restrict prototype pollution by blocking "__proto__" as a property. But, we can pollute the prototype of Object by polluting "constructor.prototype" property of an object.
So visiting the following url pollutes the prototype https://pdfgen.ctf.zer0pts.com:8443/text?text=text&a[constructor][prototype][pollute]=polluted.
Next step is to find a script gadget to get XSS. As I am using Vue.js in the application, its obvious to find a script gadget in Vue.js. Afaik, there are no public script gadgets for vue, one has to find the script gadget. There are quiet a few script gadgets in vue, below are the few gadgets.
Read Flag in the PDF using pdf_viewer postMessage
This is the interesting part of the challenge, where the solver needs to read the flag(contents) in the PDF from the DOM.
There is an interesting feature in chrome pdf_viewer using postMessage we can select text of the PDF and read the selected text.
Using this feature we can read the flag.
The final payload looks like
Unintended Solution 1
Because of my ignorance, people found unintended solutions but they are really good.
I fixed a bug which uses fetch to read flag PDF url and curl to send "sec-fetch-dest: embed" which bypasses the server side check and released Not PDF Generator.
Unintended Solution 2
I fixed the bug with the below code by allowing only local host requests to read the flag.
Then all teams except K-Students solved with one more unintended solution which uses fetch with force-cache to read the flag without hitting the server.
I have mixed feelings about the unintended solutions, they are so interesting at the same time I am not able to make them solve with intended solution.