20 Oct

Local File Inclusion (LFI) Vulnerability in InPost Gallery

One of the ways we keep track of vulnerabilities in WordPress plugins to provide our customers with the best data is by monitoring our websites for apparent activity by hackers. We recently had a request for a file from the plugin InPost Gallery, /wp-content/plugins/inpost-gallery/js/front.js. We don’t have that plugin installed on the website, so the request would likely be from someone probing for usage of the plugin. In looking over the plugin for something that hackers might target, we found a couple of vulnerabilities and some additional security issues. We are not sure if either of the vulnerabilities we found are are what the hacker was looking for or if there is still some other issue lurking in the plugin.

The more serious of the two vulnerabilities was a local file inclusion (LFI) vulnerability that would have allowed the hacker to cause a specified PHP file to to be included. That type of vulnerability could be used to get around protection in place in a file that restricts it from being loaded directly.

The plugin registers the function get_gallery_by_shortcode() to be accessible to anyone logged in or not through WordPress’ AJAX functionality (through the file /index.php):

94
95
add_action('wp_ajax_inpost_gallery_get_gallery', array(__CLASS__, 'get_gallery_by_shortcode'));
add_action('wp_ajax_nopriv_inpost_gallery_get_gallery', array(__CLASS__, 'get_gallery_by_shortcode'));

As of version 2.1.2, that function in turns passed a user specified value, “popup_shortcode_key”, in to the function render_html() without doing any validation:

762
763
764
765
766
767
768
769
public static function get_gallery_by_shortcode()
{
	$attributes = (array) json_decode(base64_decode($_REQUEST['popup_shortcode_attributes']));
	$attributes['show_in_popup'] = 0;
	$shortcode_key = $_REQUEST['popup_shortcode_key'];
	echo self::render_html("views/" . self::get_shortcode_key_folder($shortcode_key) . "/" . $shortcode_key . ".php", $attributes);
	exit;
}

Which in turn passes the value to the include() function:

418
419
420
421
422
423
424
425
public static function render_html($pagepath, $data = array())
{
	$pagepath = self::get_application_path() . '/' . $pagepath;
	@extract($data);
	ob_start();
	include($pagepath);
	return ob_get_clean();
}

After we notified the developer of the issue, version 2.1.2.1 was released, which checks if the value of the user input “popup_shortcode_key” is one of its intended values using the function get_shortcode_key_folder() before allowing it to be included:

772
773
774
775
if (self::get_shortcode_key_folder($shortcode_key))
{
	echo self::render_html("views/" . self::get_shortcode_key_folder($shortcode_key) . "/" . $shortcode_key . ".php", $attributes);
}

Proof of Concept

The following proof of concept will cause the a specified .php file from the root directory of the website to be included.

Make sure to replace “[path to WordPress]” with the location of WordPress and “[PHP filename]” with the name of the file without the “.php” portion (as that is already included).

http://[path to WordPress]/wp-admin/admin-ajax.php?action=inpost_gallery_get_gallery&popup_shortcode_key=../../../../[PHP file name]

Timeline

  • 10/16/2016 – Developer notified.
  • 10/18/2016 – Version 2.1.2.1 released, which fixes the issue.

Concerned About The Security of the Plugins You Use?

Through the end of the year you can get a free security review of a plugin or theme when you protect 100 websites with our service.

7 thoughts on “Local File Inclusion (LFI) Vulnerability in InPost Gallery

  1. Hi, i’m trying to reproduce this exploitation but i’ve always a blank page, have you got i idea?
    (To do it, i’ve got checkout from the author’s repository a the revision r1477954 and checked that the code is the same as you… and it is.)

    Thx

    • We just tried the proof of concept again and it worked. If we didn’t specify a file that should be included or included “.php” in the [PHP file name] we get a blank page, so if you are deviating from the proof of concept in some way, you could get a blank page.

  2. Thx for your answer.

    I’ve created a file ‘a.php’ (just echo a phpinfo() to test), saved on “/” of the disk and so my resquet was:
    http://localhost/wp-admin/admin-ajax.php?action=inpost_gallery_get_gallery&popup_shortcode_key=../../../../../../../../../../../a

    (many “../” to be sure to be on “/” of the disk)

    So i think i’m not deviating from the POC.

    Could you send me an archive of the plugin that you used please (there is no archive directory on the editor’s plugin)

  3. But this LFI seems to be quite limited, we can’t load files other than .php file, php seems to escape %00 in include file path:

    [Wed Dec 20 10:40:38.425746 2017] [:error] [pid 4539] [client 192.168.8.100:37124] PHP Warning: include(): Failed opening ‘/var/www/html/wp-content/plugins/aaa//views//../../../../../../../../../../../a.log\\0.php’ for inclusion (include_path=’.:/usr/share/php:/usr/share/pear’) in /var/www/html/wp-content/plugins/aaa/index.php on line 423

    • That seems like a good thing, as that would have limited what a hacker could have done with this before it got fixed.

Leave a Reply

Your email address will not be published. Required fields are marked *