بسم الله الرحمن الرحيم

On this web application, there are two ways to add an image to media library, first one is using local file upload and the second one is remote file upload from a Stock Photo website.

The first thing that came to my mind was that there might have SSRF vulnerability through the remote file upload feature. But I think it worth to test the local file upload first.

After several test on the local file upload, I can concluded that the app only allows image files (.jpg, .png, .gif and .svg), the .svg one could leads to XSS.

Then I moved on to remote file upload from a Stock Photo website feature, tried add an image and intercept the request. I found out there are 3 POST form-data that will be send to server: name, url and photoId.

I tried to change the Url to my server, but got Internal Server Error response and no any incoming connection on my server. Then I tried to remove the PhotoId data and repeat the request, again got Internal Server Error response but I got incoming connection on my server. I just point the url to my host without valid image file, so this may cause the Internal Server Error.

Later, I tried to provide a url with valid jpg image file and it successfully uploaded to media library. Then with the same image file, I modify the “name” form-data to image.html and image.php and surprisingly both successfully uploaded.

Long story short, I realized that I can’t inject php script into image exif metada because the app recreate the fetched image file with PHP-GD lib, after image re-creation, the exif metada will be removed.

I found several articles told that we still can inject PHP script into image and will not removed after image processing.

https://github.com/fakhrizulkifli/Defeating-PHP-GD-imagecreatefromjpeg

According to three articles above, we still can inject php script to the image but only with limited characters.

Also there is payload injector created by dlegs.

First attempt, I use dlegs tool to inject payload to .jpg image. You need to recreate a .jpg image with php-gd first then inject the payload with gd-jpeg.py

$ php gd.php image.jpg image-gd.jpg

Then inject the payload to image-gd.jpg

$ python gd-jpeg.py image-gd.jpg ‘<?php phpinfo()?>’ image-gd-poc.jpg

To make sure the injected payload working, try re-create the image-gd-poc.jpg with php-gd.

$ php gd.php image-gd-poc.jpg image-gd-poc-1.jpg

Then compare binary image-gd-poc.jpg and image-gd-poc-1.jpg

I use vbindiff to compare both image.

$ vbindiff image-gd-poc.jpg image-gd-poc-1.jpg

The injector writed by dlegs will inject your provided payload into a .jpg image file. This is only working if your target app recreate the image with default quality (default -1). However on my targeted app, it convert the image with quality 90, so the payload injected on image will removed.

The first attempt with payload on .jpg image was failed, so I try payload by @ABOUL3LA with .gif image file. On his article he provided POC.gif image that already injected with <?phpinfo()?>.

Finally, this is what I got after upload POC.gif and renaming the form-data “name” to test.php

If you want to use POC.gif payload by @ABOUL3LA the target app must have short_open_tag=On set in php.ini, otherwise the php script won’t executed and you have to modify the payload with <?php ?> tag to make it work.

Reported this to the bug bounty program and get triaged in just few minutes, and they made quick patch in few hours.