17 Mar

Persistent Cross-Site Scripting (XSS) Vulnerability in Resume Submissions & Job Postings

Our second publicly disclosed vulnerability report follows our first in that in both cases we found the vulnerabilities while reviewing reports of another vulnerability, which might be a good indication of the state security for WordPress plugins. In this case, while we trying to trying to set up plugin Resume Submissions & Job Postings plugin to test the vulnerability we ran across a forum post indicating that was some form of cross-site scripting (XSS) vulnerability in the resume form. After a little testing we were able to confirm there was in fact a persistent XSS vulnerability in the plugin.

Another thing worth noting with this is the importance of testing out vulnerabilities instead of just looking at the code (something we see in some of the false reports of vulnerabilities we have looked at), as the following code shows. Below are the lines that take user input from a resume submission and bring it into the plugin:

$action = $_POST['action'];
$fname = esc_html( $_POST['fname'] );
$lname = esc_html( $_POST['lname'] );
$address = esc_html( $_POST['address'] );
$address2 = esc_html( $_POST['address2'] );
$city = esc_html( $_POST['city'] );
$state = $_POST['state'];
$zip = esc_html( $_POST['zip'] );
$pnumber = esc_html( $_POST['pnumber'] );
$pnumbertype = $_POST['pnumbertype'];
$snumber = esc_html( $_POST['snumber'] );
$snumbertype = $_POST['snumbertype'];
$email = esc_html( $_POST['email'] );
$job = $_POST['job'];
$attachment = array($_FILES['attachment']);
$cover = $_POST['cover'];
$resume = $_POST['resume'];
$fromPosting = $_POST['fromPosting'];

You can see that most inputs are being run through the esc_html function, which sanitizes them, but a few are not. But if you were to assume all of those non-sanitized inputs were susceptible to being used for cross-site scripting you would be mistaken.

For two of the non-sanitized inputs pnumbertype and snumbertype, only the first 15 characters are stored in the database, which is too short to be useful.

For the state input instead of being outputted directly it used in a function:

<?php echo arrayToSelect( $theStateList['list'], $single->state ); ?>

The inputs cover and resume are echoed out directly in the admin section of the plugin though. On lines 550

<td style="background-color:#FFFFFF; border:1px solid #CCC; padding:5px;"><p><?php echo html_entity_decode( $single->cover ); ?></p></td>

and 566

<td style="background-color:#FFFFFF; border:1px solid #CCC; padding:5px;"><p><?php echo html_entity_decode( $single->cover ); ?></p></td>

of the file /resume-submissions-job-postings/includes/submissions.php.

Proof of Concept

  1. Add and activate the plugin.
  2. Add the shortcode [resumeForm] to a new post.
  3. Log out of WordPress (just to confirm that cross-site scripting is not being allowed due to your user’s role).
  4. Visit the new post.
  5. Enter “‘><script>alert(‘xss’);</script>” (without the surrounding double quotes) in the Cover Letter or Resume textareas.
  6. Enter any values for other the form fields.
  8. View the submission from http://[path to WordPress]/wp-admin/admin.php?page=rsjp-submissions.

Leave a Reply

Your email address will not be published. Required fields are marked *