Brandon T. Elliott
Experimenting with Burp AT and Achieving RCE in Unintended Ways on the OWASP Juice Shop
Preface
This blog post is about experimenting with Burp AT to find a few unintended RCE (remote code execution) vulnerabilities in the OWASP Juice Shop, which is an intentionally vulnerable web app. Please do not use this post as inspiration to spam the maintainers of it with issues/pull requests for vulnerabilities that are intended. You’d be (un)surprised by how often that happens, and I assure you this blog post is not that.
Burp AT
A couple of months ago, at the end of July 2026, PortSwigger released Burp AT into public beta, officially bringing agentic AI testing to Burp Suite.
As an avid Burp Suite Pro user, I wasted absolutely no time seeing what it was all about.
I quickly spun up a Docker instance in my homelab running the OWASP Juice Shop, which is an intentionally vulnerable web app that is commonly used for testing security tools and CTFs, etc.
I went in with zero expectations, and came out with three unintended RCEs in total, the last of which resulted in CVE-2026-106445, a critical vulnerability in handlebars.js, which is a widely used templating engine that is downloaded roughly 190 million times per month as of the writing of this post.
Let The Burping Begin
After a short crawl of my Juice Shop instance, I got Burp AT running and started out initially with a basic feeler prompt:

It tried some path traversals on the ftp directory but to no avail, and I eventually told it to move on. It then quickly identified an XXE (XML External Entity) vulnerability in a file upload route and successfully retrieved /etc/passwd from the Docker container.

However, to my dismay, I then decided to visit the scoreboard and saw that the XXE challenge was not solved, but was actually disabled due to my instance being ran inside of a Docker container.

Hmm. So… why did the XXE exploit still work with the challenge disabled? (More on this later)
Safe Mode
In the OWASP Juice Shop documentation, they detail “potentially dangerous challenges”, which can be guarded behind a safety mode (along with their underlying vulnerabilities). This safety mode can be set manually, and is automatically set if a containerized environment like Docker is detected.

To be honest, I didn’t even know about this before I started this endeavor, probably because I never ran the OWASP Juice Shop in Docker before. I also think the idea of a “safe mode” in an intentionally vulnerable web app seems extremely ripe for issues, but nevertheless, this actually gives us some sort of a threat model to work against.
With this in mind, I quickly changed my target away from the intended challenges and set my sights on a more interesting one: with all the “potentially dangerous” challenges disabled, can we still achieve RCE using our new friend Burp AT?

Pug (RCE 1/3)
Next, I prompted Burp AT to get command execution without referring to challenge information or other Juice Shop knowledge.
Shortly after, the first RCE came from an SSTI (Server-Side Template Injection) vulnerability in the /profile endpoint.

The interesting part here is that it thought it performed a WAF bypass.
In Burp AT’s defense, the userProfile route does return Blocked illegal activity by {ip_address} type responses depending on the code branch a request hits, so it wasn’t really wrong for thinking there was a WAF at play here, and for subsequently trying the WAF bypasses.
However, when I tracked the issue down in the code, it became apparent that it had accidentally hit a vulnerable code path due to it adding “ZZZ” as markers at the start and end of the SSTI payloads, and the WAF bypasses, although nice and functional, weren’t actually necessary.
The underlying issue can be seen in the following code section of userProfile.ts, namely lines 66 and 73:

Although the intended SSTI vulnerability is guarded in safe mode, as can be seen on line 54, the else branch effectively only escapes expressions if they’re at the beginning of the username. As part of the testing, Burp AT added “ZZZ” markers and more or less ended up achieving SSTI by ensuring the payload wasn’t at index 0 of the username.
Nevertheless, the WAF bypass attempts by Burp AT were genuinely impressive.
Additionally, the Docker image was fairly hardened and only contained the node binary, so I decided to see if Burp AT could use the SSTI vulnerability to write a simple static binary to /tmp/rce using raw machine code:

Upon loading the profile page, the SSTI payload executed successfully:

For all of the RCEs I found, I even went so far as to manually verify that the static binary was present in the Docker container and actually worked, in the name of anti-hallucination:

Ultimately, the intended SSTI challenge was found to be another challenge that was disabled in safe mode.

I detailed this discovery in my first email to Björn, the maintainer of the OWASP Juice Shop, who quickly fixed the issue with this commit.
VM Sandbox (RCE 2/3)
After the first RCE, I was already pretty happy with the capabilities I observed from Burp AT and was ready to start drafting this blog post, but then I got to thinking… you know what’s better than one unintended RCE?
Two unintended RCEs, of course.

A few days later, I put my new friend Burp AT back to work, and told him I needed a new code execution vector now that the SSTI vulnerability was patched.
After testing a handful of endpoints, it eventually revisited the file upload endpoint with the inclination that it may perform parsing of uploaded YAML files since it already knew it parsed XML files from when it discovered the XXE vulnerability previously.
This, of course, turned out to be correct, and eventually led to an RCE by escaping the vm sandbox used by the YAML parser, and again we prove it by writing a static binary to /tmp/rce and executing it:

The underlying vulnerability can be seen in this code section, namely lines 107-109:

The sandbox escape is possible due to a documented limitation of Node.js’s vm module where functions created with new Function() inside the vm execute in the outer global scope, not the sandboxed scope:

You may have also noticed from the code snippet that this code section seems to be guarded via && utils.isChallengeEnabled(challenges.deprecatedInterfaceChallenge).
However, after tracking this issue down further, in the challenges.yml file, the deprecated interface challenge was missing the disabledEnv section required for Docker and Heroku, so the guard that seemed to be present essentially always returned true.
After emailing Björn, this was quickly fixed with this commit, which also happened to solve the issue with the unintended XXE from before (which is intended when not in safe mode), as the XML parsing function was also guarded by the deprecated interface challenge.
Handlebars (RCE 3/3)
After the second RCE, I was like, okay, that was pretty cool, but two is an extremely unsatisfying number, when the number three is so close by…
And thus, about 90% of my testing with Burp AT falls into finding this one final vulnerability. For hours, I switched between attempts at motivating, gaslighting, and feeding Burp AT source code and ideas, hoping to find just one final RCE.
I even went back and re-watched previous James Kettle conference talks for inspiration. For awhile, I explored race condition ideas and was dead set on breaking out of Safe Mode entirely. However, none of the ideas panned out.
Eventually, after feeding Burp AT the source code for some of the routes, it identified a sliver of a potential issue in the /dataerasure route, which uses hbs as a view engine to render the page.
The idea that Burp AT eventually concocted, with my assistance, was using a file write primitive via the profile image upload feature to first upload an attacker controlled hbs layout. Although the file extensions for profile image uploads were strictly enforced, it found that you could use something like layout.gif as a layout file, as it didn’t care if it ended in .hbs or not, so this file write primitive was suitable for our needs.
The only issue was… a prototype-access deny list in handlebars (which hbs is just a wrapper of) was explicitly denying access to constructor.
After a lot of testing, and occasionally telling Burp AT things like “You’re SO close. Try harder”, it identified a way to bypass the deny list in order to access Function.

The technique worked because it bypassed the prototype-access deny list in handlebars by taking advantage of an own property branch that didn’t consult the deny list, along with the fact that constructor is an own property of prototype objects:
Function.prototype.constructor === Function
In the proof of concept, this is leveraged here, where __ can be any accessible function in the template context, and captchaBypassReqTimes is an accessible array that’s used as a sink to store the recovered Function constructor:
{{@root.captchaBypassReqTimes.push (lookup (lookup __ "__proto__") "constructor")}}
In the POST to the dataerasure route, we could specify the malicious layout file we uploaded, along with the javascript we wanted to run specified in the c parameter, and achieve RCE yet again.

With the third RCE officially proven, I let Björn know about a missing guard for the Local File Read challenge in dataerasure.ts and he quickly fixed it with this commit, which at least prevented the vulnerability in Safe Mode.
However, it was readily apparent that the underlying issue was an issue within handlebars itself, because the proof of concept involved bypassing the deny list which denied access to constructor by utilizing own properties of prototypes.
I quickly reported the vulnerability to the maintainer of handlebars.js, through this GitHub Security Advisory. The vulnerability was subsequently patched in handlebars 4.7.10, and CVE-2026-106445 was published with a CVSS score of 9.2 (critical).
It should be noted that although the vulnerability technically has a requirement of allowProtoMethodsByDefault: true being set, the hbs package, which is a wrapper for handlebars that is commonly used in express, sets this runtime option by default. And, of course, the OWASP Juice Shop utilizes hbs in some routes such as the dataerasure route, which enabled the finding of the vulnerability.
Is The Juice Worth The Burp?
All in all, I spent about $400 in AI credits with Burp AT for everything detailed here, with the vast majority of that being spent on the last vulnerability. To be fair, I hoarded context for the entirety of my testing, so part of the expense was due to me not wanting to relinquish the context window and start new chats.
After everything was patched and published, Jay, the maintainer of handlebars, also pointed out something interesting to me, which is that a separate critical vulnerability patched in handlebars 4.7.10 (CVE-2026-106446) was reported by over 20 users over the span of a couple months.
To me, this highlights how vulnerability research can often result in many researchers finding the same vulnerabilities over and over.
And this also presents an interesting question: why was I the lone reporter of CVE-2026-106445?
Was it the combination of source code review and dynamic testing I did with Burp AT?
Was it the unique vantage point created by testing an intentionally vulnerable web app, which offered up some useful gadgets?
Was it because I gaslit Burp AT into thinking there was a vulnerability present that could be exploited to achieve RCE and didn’t let it stop until it discovered it?
All in all, I think it’s important now more than ever to be able to take the road less traveled (whatever that may be), or otherwise be subjected to duplicates.
And ultimately, the answer to the question, “Is The Juice Worth The Burp?” is a definitive yes for me. A critical vulnerability in a package that’s downloaded roughly 190 million times a month is well worth the time, effort, and money spent.
Final Thoughts
The main advantage that Burp AT has is that Burp Suite is widely used and well trusted, and Burp AT integrates seamlessly with Burp’s existing tooling and functionality, instead of being just another chat interface to an LLM.
It can look at existing data in the project such as the sitemap, responses, and existing issues, in order to enrich and inform its testing. When necessary, it can invoke other functionality within Burp such as a crawl, a turbo intruder script, or many other tools, in addition to the basic crafting of requests and analyzing the responses.
As a full time web app pentester who practically lives inside of Burp Suite, I was happy to see the addition of Burp AT, and am looking forward to seeing the improvements that PortSwigger implements based on the community’s feedback.
It is readily apparent to me that Burp AT is quite capable already on its own, but becomes extremely powerful when it is led correctly by a human’s expertise and intuition.
Looking at the bigger picture, I think we all either consciously or subconsciously fear the day when AI fully “replaces” us.
Until then, I suppose I can keep riding my bike with no handlebars.