Apache Struts2 Tiles Plugin <=7.4.0 and >=6.3.0 Preparer Cache Vulnerabilities
Full Disclosuremailing list archivesFrom: Daniel Owens via Fulldisclosure <fulld 2026-11-5 04:29:58 Author: seclists.org(查看原文) 阅读量:29 收藏

fulldisclosure logo

Full Disclosure mailing list archives


From: Daniel Owens via Fulldisclosure <fulldisclosure () seclists org>
Date: Wed, 4 Nov 2026 22:34:51 +0000

The Apache Struts2 Tiles plugin version <=7.4.0 (and >=6.3.0) codebase includes a `ViewPreparer` construct, replete 
with multiple ViewPreparer factories, that contains several important vulnerabilities including unsynchronised, 
unbounded caches.  The lack of proper synchronisation means that certain factories will create caches that race, 
risking lost cache entries, corrupted internal bucket states under load (this can result in infinite loops that consume 
large amounts of compute or time, among other things), expensive duplicate `put(Object, Object)` calls, and similar 
issues.  The lack of an eviction process means that the cache can grow to arbitrary lengths, consume excessive amounts 
of memory, especially since the information system fails to utilise a starting size.  These problems are compounded by 
the cache being `protected`, directly exposing it across the package and any inheriting classes, as well as attackers 
being able to control the key (if, for example, the cache is dynamically managed and attackers can set part of the key 
or leveraging the vulnerability discussed in `Apache Struts2 Core Arbitrary Code Execution via OGNL Sandbox Escape` (01 
November 2026) to call to create preparers, call to getPreparer, etc.).

Importantly, this file appears to have come from an older, known vulnerable, known buggy version of Tiles that was 
placed into the Apache Struts2 codebase, instead of the Tiles 3 version, when the Apaches Tiles project was retired and 
archived into the Apache Attic and, as such, when the Apache Struts2 maintainers decided to import at least a subset of 
the Tiles codebase into their own.  By sucking in the buggy version, however, the Apache Struts2 Tiles plugin contained 
within said codebase inherits all issues that the imported code contained.

Examples
BasicPreparerFactory Race and Lack of an Eviction Process
The Apache Struts2 Tiles container is initialised as an application-scoped singleton resource.  Each request to the 
server utilises a separate thread, with each thread having access to the Tiles singleton.  If the requests execute 
Tiles definitions that require view preparers, they will simultaneously invoke the factory instance being used, so it 
is important that the factory utilise thread-safe constructs.  The `BasicPreparerFactory`, which is the default Tiles 
`ViewPreparer` factory, however, leverages an unsynchronised `HashMap` keyed by the preparer name.

At issue are a couple of things.  First, there is a time-of-check, time-of-use (TOCTOU) race condition in 
`getPreparer(String, Request)` -- this requires a proper lock or an atomic/locking call or, in a multi-threaded 
context, which is what happens in a Tiles workflow, it is prone to racing.  Second, there simply isn't a limit to the 
size of the Map and the default capacity is used.

This was the original design of Tiles and was a known issue, which manifested itself in denial of service (DoS) 
conditions in certain use cases, but was generally "mitigated" by having Tiles initialised and setup as part of the 
server start up routine (although plenty of systems still leveraged a more dynamic approach) -- the service would read 
the tiles configuration at start up, create and stuff the cache then, using a serial process.  This design, however, 
was still suboptimal, buggy, and problematic, even when used in that way, resulting in several overhauls and, 
eventually, settling in Tiles 3.  The old process, regardless, was suboptimal even from the most basic of perspectives 
-- since it was a one-time, start-up cost (typically -- again, developers and the like were free to, and often did, 
perform dynamic building), there was little reason to utilise the default capacity -- the desired size and capacity was 
trivially calculable and known, so it could and should have been set thereto to prevent excessive memory use, with a 
better-tuned automatic resizing threshold.  That, of course, however, was the least of the problems, which is why Tiles 
3 shifted to a `ConcurrentHashMap` (realistically, a sub-optimal solution, as it is generally best to control your own 
resources since such structures can still race via TOCTOU -- e.g., ` if (!map.containsKey(key)) map.put(key, value);`-- 
and similar coding constructs).

```java
    /**
     * Maps a preparer name to the instantiated preparer.
     */
    protected Map<String, ViewPreparer> preparers;

    /**
     * Constructor.
     */
    public BasicPreparerFactory() {
        this.preparers = new HashMap<>();
    }


    /**
     * Create a new instance of the named preparerInstance.  This factory
     * expects all names to be qualified class names.
     *
     * @param name    the named preparerInstance
     * @param context current context
     * @return ViewPreparer instance
     */
    public ViewPreparer getPreparer(String name, Request context) {
        if (!preparers.containsKey(name)) {
            preparers.put(name, createPreparer(name));
        }

        return preparers.get(name);
    }
```
Code snippet from struts2-tiles-plugin's 
plugins/tiles/src/main/java/org/apache/tiles/core/prepare/factory/BasicPreparerFactory.java, lines 44-71
_______________________________________________
Sent through the Full Disclosure mailing list
https://nmap.org/mailman/listinfo/fulldisclosure
Web Archives & RSS: https://seclists.org/fulldisclosure/


Current thread:

  • Apache Struts2 Tiles Plugin <=7.4.0 and >=6.3.0 Preparer Cache Vulnerabilities Daniel Owens via Fulldisclosure (Nov 04)

文章来源: https://seclists.org/fulldisclosure/2026/Nov/11
如有侵权请联系:admin#unsafe.sh