Context
The backstory on this event was that my friend Firdaus recently was awarded CVE-2024-21544 in the browsershot package. I was amazed and reached out to know more as CVE hunting was never on my mind as it seemed too daunting. He shared to not limit yourself and just try, which got me motivated to just do it.
I then peeked at the latest package version and noticed that it still seemed vulnerable - which was the case and the birth of CVE-2024-21547. Essentially, this package converts a webpage to an image or pdf with the help of headless chrome (a browser). Users can pass in URLs such as “https://google.com“ and it would render it as an image/pdf.
The developer introduced certain checks to ensure that local file reads were not possible. The code responsible is shown below:
1 | public function setUrl(string $url): static |
However, it only ensures that the URL does not start with “file://“ or “file:/“. As the URL would be fed into headless chrome, the developer did not account for browser normalisation. An example payload “file:\/etc/passwd” can be used to read the local passwd file if this package is used on the internet and accepts user input. When the browser receives “file\/etc/passwd”, it would be normalised to the regular “file:///etc/passwd”.
A local PoC is given as :
1 | <?php |

Reporting
After emailing the security vendor as mentioned on their GitHub, I filled in a form by Snyk to facilitate the CVE disclosure process. After patching the vulnerability and pushing it to GitHub, Snyk awarded the CVE.
Twist
After looking at the patched code, I then was also curious at Firdaus’s CVE and took a look at the fix for it. It added the PHP trim function to prevent spaces or new lines in the URL which would also be normalised by the browser and bypass checks.
I read into the documentation and noticed that the trim function only strips whitespace from the beginning and end of the string. It does not remove characters from any other position.

As browsers are user friendly, they tend to normalise characters to provide better user experience and conform to relevant RFCs. I thought of putting the new line character (%0A) into the middle of the URL such as “file:
///etc/passwd” which actually works as it was normalised.
Thus, CVE-2025-1026 was born 💀.