Title: PHAR Deserialization in Snappy
Advisory ID: NBS-2023-0003
Affected Version: <= 1.4.1
Patched Version: >=1.4.2
CVE ID: CVE-2023-28115
Author: Ahmad Shauqi Bin Gushadi | NetbyteSEC
Snappy is a PHP library allowing thumbnail, snapshot or PDF generation from a url or a html page. It uses the excellent webkit-based wkhtmltopdf and wkhtmltoimage available on OSX, linux, windows.
Snappy is vulnerable to PHAR deserialization due to a lack of checking on the protocol before passing it into the file_exists() function. If an attacker can upload files of any type to the server he can pass in the phar:// protocol to unserialize the uploaded file and instantiate arbitrary PHP objects. This can lead to remote code execution especially when snappy is used with frameworks with documented POP chains like Laravel/Symfony vulnerable developer code. If user can control the output file from the generateFromHtml() function, it will invoke deserialization.
Phar deserialization happens when user passed phar file (including phar:// wrapper) with malicious serialized metadata to vulnerable function such as file_exists, file_get_contents, or unlink (any function that related to I/O filesystems). In this journey, sink to source was the chosen method to find the vulnerability. Snappy provides multiple features such as display pdf in the browser, merge multiple urls into one pdf and generate local pdf file. The vulnerability lies on generate local pdf file when user give the file name, Snappy will check if the file is already exists using file_exists in ./vendor/knplabs/knp-snappy/src/Knp/Snappy/AbstractGenerator.php by passing whole user input to the function. This result to deserialization and could leads to remote code execution depends on what framework it integrated. Below is the stack trace for the bug.
The interested function lies on AbstractGenerator file which is prepareOutput function. It takes filename and overwrite as arguments, then checks if the filename given is exists using file_exists function as shown as figure 2 below.
On line 2, snappy does not validate user input and pass it to file_exists function. Deserialization will occur once user use phar wrapper in file_exists. Depends on the implementation and which framework it integrated, it could leads to arbitrary file read, arbitrary file write or remote code execution. Snappy can be integrate with three framework which are Laravel, Symfony and Zend Framework. In order to create the exploit, the challenge is finding correct gadget, which can be time consuming. PHPGGC solved the challenge and it is open source.
Proof of Concept
Vendor Contact Timeline
NetbyteSEC Sdn Bhd was incorporated under the Malaysian Companies Act 1965 in 2013.
NetbyteSEC is privately owned and is based in Cyberjaya, Selangor, Malaysia.
More information about NetbyteSEC Sdn Bhd can be found at: