Update (2022-02-26): the tool is now public: https://github.com/kazet/wpgarlic

WordPress plugins expose a number of interfaces, such as:

  • AJAX endpoints (/wp-admin/admin-ajax.php)
  • Admin menu pages (/wp-admin/admin.php?page=...)
  • PHP files (in the /wp-content/plugins/ directory),
  • REST routes (/wp-json/...).

These interfaces have a consistent trust boundary: we know where the untrusted input goes and can detect what operations are executed on that input.

For instance, if you visit a .php file, provide appropriate parameters, and cause a file to be removed, you know that it is a vulnerability. You know what parameters you control and what ones you don’t – for example, you may redirect a logged-in admin to an admin menu page with arbitrary GET parameters, but you don’t control their cookies.

Therefore it is possible to semi-automatically scan for multiple classes of vulnerabilities in all WordPress plugins.

I have written a tool that:

  • executes each AJAX endpoint, menu page, REST route, or file multiple times,
  • injects payloads into the GET, POST, etc. arrays or REST parameters (more on how it’s done in the next section),
  • analyses the outputs with an ugly pile of regular expressions1 to detect:
    • calls to WordPress functions (such as wp_delete_post),
    • crashes (“No such file or directory”, “You have an error in your SQL syntax”, …),
    • XSS (echoing a known payload containing " or <),
    • etc.

This method is transferable to other CMS plugin ecosystems but not directly e.g. to Python packages. If a Python package allows you to remove arbitrary files, it may or may not be a vulnerability depending on the package role and your particular setup.

Injecting parameters

In PHP, the _GET, _POST, _SERVER, _COOKIE, and _REQUEST arrays contain various request parameters (e.g. GET and POST data, cookies, server configuration, and headers). I have replaced them with mock objects that allow access to any key - and with defined probability return a payload from a predefined payload list.

A simple mock $_POST array could be created using the following code:

<?php

class Mock implements ArrayAccess {
    function offsetGet($offset) {
        return "payload";
    }

    function offsetExists($offset) {
        return true;
    }

    function offsetSet($offset, $value) { }

    function offsetUnset($offset) { }
}

$_POST = new Mock();

echo $_POST["parameter_name"];

The above snippet will print payload.

Let’s assume that the $_REQUEST array has been mocked in a way similar to the one described above and that an AJAX route is handled by the following function:

public function delete_saved_block() {
	$block_id = (int) sanitize_text_field($_REQUEST['block_id']);
	$deleted_block = wp_delete_post($block_id);
	wp_send_json_success($deleted_block);
}

When $_REQUEST['block_id'] gets accessed, the mock will return a payload, thus allowing to detect that wp_delete_post was called on an attacker-controlled value.

This approach allowed to easily inject payloads even if the parameter name was hard to guess – the tool didn’t distinguish between id and secret_parameter_65e3c14a1d.

Some of the keys needed to be excluded manually (for example $_SERVER['HTTP_AUTHORIZATION'] or $_GET['doing_wp_cron']) because their values were handled by WordPress, and providing them caused plugin code to not be reached.

Besides, with some probability, a random type of array was returned instead of a string payload:

  • a singleton array with a string payload,
  • recursively, an object that allows access to any key,
  • a singleton array: random payload → random payload.

Detecting vulnerabilities

The tool contained checks to detect:

  • various kinds of crashes,
  • potentially dangerous operations,
  • information leaks.

Some of these checks led to a large number of CVEs (such as the XSS checks), some didn’t (e.g. the checks for syntax errors designed to catch eval() on untrusted code).

XSS

To detect XSS, checks were implemented that detected payloads being echoed back (or echoed back with escaping that didn’t prevent XSS, such as prefixing " with \).

Crashes

The following types of crashes were detected:

  • fopen() / file_get_contents() / require() / require_once() / include() / include_once() errors and “No such file or directory” or “failed to open stream” error messages,
  • unlink() error messages,
  • crashes related to call_user_func(),
  • SQL error messages,
  • unserialize() errors,
  • parse / syntax errors to detect eval() calls,
  • “command not found” error message,
  • simplexml_load_string() error messages2.

Information leaks

The output was analysed to observe whether known user e-mails or file names are displayed.

WordPress operations

WordPress has been instrumented to detect:

  • calls to maybe_unserialize,
  • calls to update_option/update_site_option/delete_option,
  • calls to wp_insert_user,
  • calls to wp_insert_post/wp_update_post/wp_delete_post,
  • calls to wp_mail,
  • calls to query (this one yielded an especially large number of false positives that needed additional filtering),
  • calls to get_users (this one has been added after accidentally discovering CVE-2021-25110 where an attacker can leak arbitrary user e-mails via a crafted user search query).

Additional checks

After fuzzing, the admin panel, the homepage, and the post pages were crawled to find occurrences of known payloads. That allowed for instance to detect CVE-2021-24975 in social-networks-auto-poster-facebook-twitter-g.

Update (2022-02-26): additionally, any attempts to access uploaded files are logged, so that they may be checked manually.

Changes to PHP

Patched equality

I have patched PHP so that equality comparison between any value and a known payload returned true with 1/3 probability. Forgive me about this one.

With this patch, I was able to detect vulnerabilities such as:

if ($_GET['action'] == 'please-remove-post') {
    wp_delete_post($_GET['id']);
}

Unfortunately, this resulted in a large number of false positives as well. The false positives were e.g. in the form of:

if (in_array($order, array("ASC", "DESC"), true)) {
    query("(...) ORDER BY $order");
}

The only solution for this problem I have used was browsing through these false positives and cursing. Further research can lead to coming up with other solutions.

Other changes

Besides, I have patched the PHP interpreter so that:

  • When base64_decode was performed on a known payload, this payload was returned again,
  • When json_decode was performed on a known payload, an object was returned that returns payloads when any key was accessed. These were the same objects that served as e.g. $_GET arrays,
  • when a redirect was performed, relevant information was displayed so that Open Redirect vulnerabilities could be detected.

Testing

A test-driven approach was critical during development. Tests checked that the tool would find a known vulnerability. For example, I could write a test to check that:

when fuzzing the wp_ajax_heateor_sss_import_config endpoint of the sassy-social-share plugin in version 3.3.23, the tool should detect that maybe_unserialize() gets called on an attacker-controlled payload.

False positives vs false negatives

This approach yielded a large number of false positives. It was a deliberate decision because I wanted to sort through multiple reports instead of missing vulnerabilities.

An alternative could be to write additional filtering logic. For example, there were multiple reports where an HTML payload was echoed back – but when checking them, I’ve observed that a correct JSON Content-Type header is added. This was one of the cases that could be checked automatically.

Other

Fuzzing was performed inside Docker containers, re-created for every plugin.

It was important to disconnect the network, because a lot of plugins call other web services, and I wanted to avoid sending random payloads there.

I found it also helpful to separate plugin fuzzing and analysis of the outputs. Because the outputs were analyzed by a lot of regular expressions, bugs happened. Therefore a relatively quick rescan allowed to speed up development.

Automating the fuzzing Added: 2022-02-16

Scanning thousands of plugins would not be possible without automating the job. Fortunately, WordPress plugins use consistent interfaces to integrate with WordPress, for example:

  • all REST routes are collected in a central registry, accessible via: rest_get_server()->get_routes(),
  • AJAX actions are created by adding a hook with a name starting with wp_ajax_,
  • there exists one registry with all admin menu pages.

Therefore all REST routes, AJAX actions, and menu actions can be enumerated in the same way regardless of which plugin is scanned. Of course, all PHP files can be easily listed as well.

All plugins can be installed in the same way: I have used WP-CLI – a tool that allows to install, activate, deactivate or delete a plugin from the command-line. The list of plugins could be downloaded automatically from the WordPress plugin registry API.

All of the above techniques made it possible to create a tool that doesn’t require any plugin-specific code.

Results Last updated: 2022-04-04

Because of time constraints, I have focused only on the most popular plugins. As of this moment, the following bugs found by the tool have already been fixed and published:

ID Plugin CVE Number of active installations Type Link
1 woocommerce CVE-2022-0775 5,000,000 Arbitrary comment deletion WPScan
2 updraftplus CVE-2021-25022 3,000,000 Reflected XSS WPScan
3 code-snippets CVE-2021-25008 500,000 Reflected XSS WPScan
4 woocommerce-pdf-invoices-packing-slips CVE-2021-24991 300,000 Reflected XSS WPScan
5 ad-inserter CVE-2022-0288 200,000 Reflected XSS WPScan
6 complianz-gdpr CVE-2022-0193 200,000 Reflected XSS WPScan
7 custom-facebook-feed CVE-2021-25065 200,000 Reflected XSS WPScan
8 favicon-by-realfavicongenerator CVE-2022-0471 200,000 Reflected XSS WPScan
9 loginpress CVE-2022-0347 200,000 Reflected XSS WPScan
10 popup-builder CVE-2022-0479 200,000 Reflected XSS WPScan
11 use-any-font CVE-2021-24977 200,000 Arbitrary CSS append + stored XSS WPScan
12 white-label-cms CVE-2022-0422 200,000 Reflected XSS WPScan
13 wp-cerber CVE-2022-0429 200,000 Stored XSS WPScan
14 wp-gdpr-compliance CVE-2022-0147 200,000 Reflected XSS WPScan
15 capability-manager-enhanced CVE-2021-25032 100,000 Arbitrary settings update WPScan
16 chaty CVE-2021-25016 100,000 Reflected XSS WPScan
17 cmp-coming-soon-maintenance CVE-2022-0188 100,000 Possibility to add arbitrary CSS WPScan
18 download-manager CVE-2021-24969 100,000 Stored XSS WPScan
19 download-manager CVE-2021-25069 100,000 Reflected XSS WPScan
20 email-subscribers CVE-2022-0439 100,000 Blind SQL Injection WPScan
21 learnpress CVE-2022-0271 100,000 Reflected XSS WPScan
22 menu-image CVE-2022-0450 100,000 Stored XSS WPScan
23 modern-events-calendar-lite CVE-2021-24925 100,000 Reflected XSS WPScan
24 modern-events-calendar-lite CVE-2021-24946 100,000 Blind SQL injection WPScan
25 modern-events-calendar-lite CVE-2021-25046 100,000 Stored XSS WPScan
26 paid-memberships-pro CVE-2021-25114 100,000 Blind SQL Injection WPScan
27 squirrly-seo CVE-2021-25019 100,000 Reflected XSS WPScan
28 ti-woocommerce-wishlist CVE-2022-0412 100,000 Blind SQL Injection WPScan
29 webp-converter-for-media CVE-2021-25074 100,000 Open redirect WPScan
30 woocommerce-products-filter CVE-2021-25085 100,000 Reflected XSS WPScan
31 wpvivid-backuprestore CVE-2021-24994 100,000 Stored XSS WPScan
32 wpvivid-backuprestore CVE-2022-0531 100,000 Reflected XSS WPScan
33 advanced-cf7-db CVE-2021-24905 90,000 Arbitrary file removal WPScan
34 kingcomposer CVE-2021-25048 90,000 Stored XSS WPScan
35 kingcomposer CVE-2022-0165 90,000 Open redirect WPScan
36 social-networks-auto-poster-facebook-twitter-g CVE-2021-24975 90,000 Stored XSS WPScan
37 social-networks-auto-poster-facebook-twitter-g CVE-2021-25072 90,000 CSRF post removal WPScan
38 themify-portfolio-post CVE-2022-0200 80,000 Reflected XSS (logged-in POST 3) WPScan
39 woo-product-feed-pro CVE-2021-24974 80,000 Stored XSS WPScan
40 woo-product-feed-pro CVE-2022-0426 80,000 Reflected XSS (logged-in POST 3) WPScan
41 booking CVE-2021-25040 60,000 Reflected XSS WPScan
42 interactive-3d-flipbook-powered-physics-engine CVE-2022-0423 60,000 Stored XSS WPScan
43 mappress-google-maps-for-wordpress CVE-2022-0208 60,000 Reflected XSS WPScan
44 permalink-manager CVE-2022-0201 60,000 Reflected XSS WPScan
45 post-grid CVE-2022-0447 60,000 Reflected XSS (logged-in POST 3) WPScan
46 powerpack-lite-for-elementor CVE-2021-25027 60,000 Reflected XSS WPScan
47 real-cookie-banner CVE-2022-0445 60,000 CSRF settings reset and deleting all GDPR consents WPScan
48 wd-instagram-feed CVE-2021-25047 60,000 Reflected XSS WPScan
49 woocommerce-currency-switcher CVE-2021-25043 60,000 Reflected XSS WPScan
50 woocommerce-currency-switcher CVE-2022-0234 60,000 Reflected XSS WPScan
51 wp-responsive-menu CVE-2021-24971 60,000 Stored XSS WPScan
52 wp-rss-aggregator CVE-2021-24988 60,000 Stored XSS WPScan
53 wp-rss-aggregator CVE-2022-0189 60,000 Reflected XSS (logged-in POST 3) WPScan
54 ditty-news-ticker CVE-2022-0533 50,000 Reflected XSS WPScan
55 event-tickets CVE-2021-25028 50,000 Open redirect WPScan
56 nimble-builder CVE-2022-0314 50,000 Reflected XSS WPScan
57 simple-membership CVE-2022-0328 50,000 CSRF member deletion WPScan
58 super-socializer CVE-2021-24987 50,000 Reflected XSS WPScan
59 bnfw CVE-2022-0345 40,000 E-mail leak WPScan
60 thirstyaffiliates CVE-2022-0398 40,000 Arbitrary affiliate link creation WPScan
61 tutor CVE-2021-25017 40,000 Reflected XSS WPScan
62 advanced-cron-manager CVE-2021-25084 30,000 Arbitrary cron configuration change WPScan
63 contact-form-7-skins CVE-2021-25063 30,000 Reflected XSS WPScan
64 easy-paypal-donation CVE-2021-24989 30,000 CSRF post removal WPScan
65 futurio-extra CVE-2021-25110 30,000 E-mail leak WPScan
66 google-pagespeed-insights CVE-2022-0431 30,000 Reflected XSS (logged-in POST 3) WPScan
67 insight-core CVE-2021-24950 30,000 Stored XSS + object injection WPScan
68 lead-form-builder CVE-2021-24967 30,000 Stored XSS WPScan
69 master-addons CVE-2022-0327 30,000 Reflected XSS WPScan
70 meks-easy-instagram-widget CVE-2021-24958 30,000 Stored XSS WPScan
71 my-calendar CVE-2021-24927 30,000 Reflected XSS WPScan
72 notificationx CVE-2022-0349 30,000 Blind SQL Injection WPScan
73 photo-gallery CVE-2022-0169 30,000 SQL Injection WPScan
74 protect-wp-admin CVE-2021-24906 30,000 Disabling of plugin security features WPScan
75 pz-linkcard CVE-2021-25012 30,000 Reflected XSS WPScan
76 site-reviews CVE-2021-24973 30,000 Stored XSS WPScan
77 ultimate-faqs CVE-2021-24968 30,000 Possibility to add arbitrary FAQs WPScan
78 video-conferencing-with-zoom-api CVE-2022-0384 30,000 E-mail leak WPScan
79 woo-smart-wishlist CVE-2022-0397 30,000 Reflected XSS (logged-in POST 3) WPScan
80 wp-user-frontend CVE-2021-25076 30,000 SQL injection in admin panel leading to reflected XSS WPScan
81 ad-invalid-click-protector CVE-2022-0190 20,000 SQL injection WPScan
82 advanced-product-labels-for-woocommerce CVE-2022-0399 20,000 Reflected XSS (logged-in POST 3) WPScan
83 asgaros-forum CVE-2022-0411 20,000 Blind SQL Injection WPScan
84 bwp-google-xml-sitemaps CVE-2022-0230 20,000 Stored XSS WPScan
85 crazy-bone CVE-2022-0385 20,000 Stored XSS WPScan
86 event-calendar-wd CVE-2021-25025 20,000 Possibility to add arbitrary events WPScan
87 float-menu CVE-2022-0313 20,000 CSRF menu deletion WPScan
88 gmap-embed CVE-2021-25011 20,000 Arbitrary post removal, plugin settings update WPScan
89 gmap-embed CVE-2021-25081 20,000 Arbitrary post removal, plugin settings update via CSRF WPScan
90 image-hover-effects-ultimate CVE-2021-25031 20,000 Reflected XSS WPScan
91 material-design-for-contact-form-7 CVE-2022-0404 20,000 DoS WPScan
92 miniorange-2-factor-authentication CVE-2022-0229 20,000 DoS WPScan
93 mycred CVE-2021-25015 20,000 Reflected XSS WPScan
94 mycred CVE-2022-0287 20,000 E-mail leak WPScan
95 mycred CVE-2022-0363 20,000 Arbitrary post creation WPScan
96 mystickyelements CVE-2022-0148 20,000 Reflected XSS WPScan
97 navz-photo-gallery CVE-2021-24909 20,000 Reflected XSS WPScan
98 newstatpress CVE-2022-0206 20,000 Reflected XSS WPScan
99 page-views-count CVE-2022-0434 20,000 SQL injection WPScan
100 restaurant-reservations CVE-2021-24965 20,000 Stored XSS WPScan
101 woocommerce-product-addon CVE-2021-25018 20,000 Stored XSS WPScan
102 wp-accessiblity-helper CVE-2022-0150 20,000 Reflected XSS WPScan
103 wp-stats-manager CVE-2021-24750 20,000 SQL injection WPScan
104 wp-stats-manager CVE-2021-25042 20,000 Stored XSS WPScan
105 wp-stats-manager CVE-2022-0410 20,000 Blind SQL Injection WPScan
106 wplegalpages CVE-2021-25106 20,000 Stored XSS WPScan
107 advanced-page-visit-counter CVE-2021-24957 10,000 Blind SQL injection WPScan
108 affiliates-manager CVE-2021-25078 10,000 Stored XSS WPScan
109 akismet-privacy-policies CVE-2021-25071 10,000 Reflected XSS WPScan
110 ari-fancy-lightbox CVE-2022-0161 10,000 Reflected XSS WPScan
111 business-profile CVE-2021-25060 10,000 Stored XSS WPScan
112 coming-soon-page CVE-2022-0164 10,000 Sending any e-mail to all subscribers WPScan
113 coming-soon-page CVE-2022-0199 10,000 Sending any e-mail to all subscribers via CSRF WPScan
114 dropdown-menu-widget CVE-2021-25113 10,000 Stored XSS WPScan
115 duplicate-page-or-post CVE-2021-25075 10,000 Stored XSS WPScan
116 easy-pricing-tables CVE-2021-25098 10,000 CSRF post removal WPScan
117 english-wp-admin CVE-2021-25111 10,000 Open redirect WPScan
118 ibtana-visual-editor CVE-2021-25014 10,000 Stored XSS WPScan
119 ip2location-country-blocker CVE-2021-25095 10,000 Banning arbitrary countries WPScan
120 ip2location-country-blocker CVE-2021-25096 10,000 Ban circumvention WPScan
121 ip2location-country-blocker CVE-2021-25108 10,000 Banning countries via CSRF WPScan
122 link-library CVE-2021-25091 10,000 Reflected XSS WPScan
123 link-library CVE-2021-25092 10,000 CSRF settings reset WPScan
124 link-library CVE-2021-25093 10,000 Arbitrary link removal WPScan
125 modal-window CVE-2021-25051 10,000 CSRF RCE WPScan
126 page-builder-add CVE-2021-25067 10,000 Reflected XSS WPScan
127 portfolio-wp CVE-2021-25090 10,000 Stored XSS WPScan
128 powerpack-addon-for-beaver-builder CVE-2022-0176 10,000 Reflected XSS WPScan
129 qubely CVE-2021-25013 10,000 Arbitrary post removal WPScan
130 rearrange-woocommerce-products CVE-2021-24928 10,000 SQL injection WPScan
131 registrations-for-the-events-calendar CVE-2021-24943 10,000 SQL injection WPScan
132 registrations-for-the-events-calendar CVE-2021-25083 10,000 Reflected XSS WPScan
133 secure-copy-content-protection CVE-2021-24931 10,000 SQL injection WPScan
134 smart-forms CVE-2022-0163 10,000 Downloading form data WPScan
135 spider-event-calendar CVE-2022-0212 10,000 Reflected XSS WPScan
136 stopbadbots CVE-2021-25070 10,000 Blind SQL injection WPScan
137 ultimate-product-catalogue CVE-2021-24993 10,000 Possibility to add arbitrary products WPScan
138 whmcs-bridge CVE-2021-25112 10,000 Reflected XSS WPScan
139 wicked-folders CVE-2021-24919 10,000 SQL injection WPScan
140 woo-orders-tracking CVE-2021-25062 10,000 Reflected XSS WPScan
141 woocommerce-exporter CVE-2022-0149 10,000 Reflected XSS WPScan
142 woocommerce-store-toolkit CVE-2021-25077 10,000 Reflected XSS WPScan
143 wp-booking-system CVE-2021-25061 10,000 Reflected XSS WPScan
144 wp-coder CVE-2021-25053 10,000 CSRF RCE WPScan
145 wp-photo-album-plus CVE-2021-25115 10,000 Stored XSS WPScan
146 wp125 CVE-2021-25073 10,000 CSRF ad deletion WPScan
147 wpcargo CVE-2021-25003 10,000 RCE WPScan
148 events-made-easy CVE-2021-25030 7,000 SQL injection WPScan
149 likebtn-like-button CVE-2021-24945 7,000 Sensitive data exposure WPScan
150 wp-email-users CVE-2021-24959 7,000 SQL injection + object injection WPScan
151 responsive-vector-maps CVE-2021-24947 6,000 Arbitrary file read WPScan
152 button-generation CVE-2021-25052 5,000 CSRF RCE WPScan

Not all of the vulnerabilities were found directly by the fuzzer. For example, CVE-2021-25096 was found accidentally when writing a PoC for CVE-2021-25095. For some other vulnerabilities, the tool alerts were only part of the vulnerability information – for example, the tool notified that a WordPress option can get updated by any user - and finding the consequences (whether it can lead e.g. to stored XSS) required manual work.

Findings worth mentioning

I won’t make fun of any particular plugin author, however, I think some findings are worth sharing.

is_admin

The WordPress is_admin() function, as you may probably have guessed:

Determines whether the current request is for an administrative interface page.

(from https://developer.wordpress.org/reference/functions/is_admin/)

The documentation warns as well, that it:

Does not check if the user is an administrator; use current_user_can() for checking roles and capabilities.

As you may probably have guessed, it was a source of a couple of vulnerabilities in the form of:

if (is_admin()) {
    /* dangerous action */
}

REST route URLs

Let’s consider the following code:

register_rest_route((...), '/(...)/(?P<id>[\d]+)', array(
    array(
        'methods' => WP_REST_Server::READABLE,
        'callback' => array($this, 'callback'),
        'permission_callback' => '__return_true',
    ),
));

/* ... */

function callback($request) {
    $id = $request['id'];
}

What ID values could be passed to the handler?

The correct answer is: all of them – just use /?rest_route=/(...)/1&id=hehehe.

get_users()

Some plugins allow searching for users by providing a part of an e-mail address. That allows to leak any user’s e-mail using the following steps:

  • Bruteforcing the first letter of the domain name (searching for @a, @b, etc., and checking when the user’s name appears in search results).
  • Remembering the first letter and using it to guess the second letter. Let’s assume the user’s e-mail domain name starts with g. You can then brute force the second letter (@ga, @gb, …).
  • Repeating the above steps for the rest of the e-mail address.

Because of that, I have added a check that alerts when get_users() gets called. Unfortunately, besides finding vulnerabilities of this type, it led to numerous false positives as well.

XSS protection

Don’t do the following:

if (/* potential XSS in $parameter detected */) die('Invalid parameter: ' . $parameter);

Several XSS vulnerabilities were also caused by debugging helpers in the form of:


echo "<!--";

var_dump($_POST);

echo "-->";

CAPTCHA verification

Don’t do this:

if (isset($_POST['captcha'])) {
    /* verify captcha */
}

/* do action that should be CAPTCHA-protected */

I have observed this pattern multiple times, both for CAPTCHAs and nonces.

Conclusions

This was just a proof-of-concept to check whether automatic techniques are a viable method to find WordPress plugin bugs. I am sure it can be improved by e.g.:

  • adding checks to detect other types of dangerous operations,
  • attempting to decrease the number of false positives without a large loss of true positives. The percentage of false positives was one of the main obstacles in this project.

This technique can also be implemented for other plugin ecosystems.

Many of the vulnerabilities I found were easily preventable by modern software engineering practices. In many WordPress plugins, HTML is built using an error-prone pile of echo statements, instead of a template language. Similarly, AJAX endpoints are by default available for all logged-in users or all not logged-in users, instead of requiring the developer to provide a fixed allowlist of roles or permissions (so that they would have to explicitly mark a route as available to all logged-in users). Introducing techniques that make it harder to make mistakes and promoting their use is, unfortunately, something only the WordPress team, not plugin developers, can do.

Footnotes

  1. In retrospect, using a pile of regular expressions to detect crashes in the output wasn’t the best idea. Now I would try to do this differently. 

  2. In retrospect, it is obvious that it doesn’t cover all ways to load XML. This should have been done differently. 

  3. This type of reflected XSS requires cookies to be sent with a POST request, therefore would be harder to exploit due to the SameSite-by-default behavior.  2 3 4 5 6 7