While doing recon for H1-4420, I stumbled upon a Wordpress blog that had a plugin enabled called SlickQuiz. Although the latest version 220.127.116.11 was installed and I haven’t found any publicly disclosed vulnerabilities, it still somehow sounded like a bad idea to run a plugin that hasn’t been tested with the last three major versions of Wordpress.
So I decided to go the very same route as I did already for last year’s H1-3120 which eventually brought me the MVH title: source code review. And it paid off again: This time, I’ve found two vulnerabilities named CVE-2019-12517 (Unauthenticated Stored XSS) and CVE-2019-12516 (Authenticated SQL Injection) which can be chained together to take you from being an unauthenticated Wordpress visitor to the admin credentials.
Due to the sensitivity of disclosed information I’m using an own temporarily installed Wordpress blog throughout this blog article to demonstrate the vulnerabilities and the impact.
CVE-2019-12517: Going From Unauthenticated User to Admin via Stored XSS
During the source code review, I stumbled upon multiple (obvious) stored XSS vulnerabilities when saving user scores of quizzes. Important side note: It does not matter whether “Save user scores” plugin option is disabled (default) or enabled, the pure presence of a quiz is sufficient for explotiation since this option does only disable/enable the UI elements.
The underlying issue is located in
php/slickquiz-scores.php in the method
generate_score_row() (lines 38-52) where the responses to quizzes are returned without encoding them first:
$score->score are use-controllable, a simple request like the following is enough to get three XSS payloads into the SlickQuiz backend:
As soon as any user with access to the SlickQuiz dashboard visits the user scores, all payloads fire immediately:
So far so good. That’s already a pretty good impact, but there must be more.
CVE-2019-12516: Authenticated SQL Injections To the Rescue
The SlickQuiz plugin is also vulnerable to multiple authenticated SQL Injections almost whenever the
id parameter is present in any request. For example the following requests:
/wp-admin/admin.php?page=slickquiz-scores&id=(select*from(select(sleep(5)))a) /wp-admin/admin.php?page=slickquiz-edit&id=(select*from(select(sleep(5)))a) /wp-admin/admin.php?page=slickquiz-preview&id=(select*from(select(sleep(5)))a)
all cause a 5 second delay:
The underlying issue of i.e. the
/wp-admin/admin.php?page=slickquiz-scores&id=(select*from(select(sleep(5)))a) vulnerability is located in
php/slickquiz-scores.php in the constructor method (line 20) where the GET parameter
id is directly supplied to the method
Whereof the method
get_quiz_by_id() is defined in
php/slickquiz-model.php (lines 27-35):
Another obvious one.
Connecting XSS and SQLi for Takeover
Now let’s connect both vulnerabilities to get a real Wordpress takeover :-)
First of all: Let’s get the essential login details of the first Wordpress user (likely to be the admin): user’s email, login name and hashed password. I’ve built this handy SQLi payload to achieve that:
This eventually returns requested data within an
Now changing the XSS payload to:
Will cause the XSS to fire and alert the Wordpress credentials:
From this point on, everything’s possible, just like sending this data cross-domain via another
Thanks Uber for the nice bounty!