DEV Community

SorrelThorncroft
SorrelThorncroft

Posted on

An External Verification Checklist for App-Layer RCE in a File-Sync Stack

An External Verification Checklist for App-Layer RCE in a File-Sync Stack

Why a checklist rather than a narrative

Every incident produces a runbook specific to it, and most of those runbooks are not reusable. The useful residue is the part that transfers to the next unauthenticated RCE in a self-hosted file-sharing stack.

This checklist comes from handling CVE-2026-110447 and is written so the same five steps apply to the next one.

Step one: identify the vulnerable component

Establish which component holds the flaw, because the product name is rarely the right unit. Here the vulnerable code is proxy.php, delivered by the optional app richdocumentscode, not by Nextcloud itself and not by standalone Collabora Online servers.

Record three facts from the advisory verbatim. The exact package name. The version range affected. The version that patches it. Then record the deployments the advisory excludes, since those exclusions let a large part of the estate exit the process early.

Step two: measure your own exposure

Describe your own estate from outside using queries anchored on the vulnerable component rather than the platform. For this incident, http.body="/apps/richdocumentscode" returned 119149 matches on 2026-10-20 while app="Nextcloud" returned 1775791, and only the first describes the relevant population.

Restrict the query to your own ranges with org or cidr so the result is an inventory rather than a report about the internet. Record the query text, the date and the sub_type so the next measurement is comparable.

Step three: separate presence from patch state

Write down what the external measurement cannot see, before somebody asks. Body searches cannot read a version, cannot tell whether a feature was disabled, and cannot distinguish a patched installation from a vulnerable one, because the app directory remains in both cases.

Then close the gap with configuration data. For this incident, any installed version below 26.4.303 is the failure condition, and removal counts as a pass. Treating 'disabled' as a pass is wrong here, because the advisory says the file is served directly by the web server.

Step four: look for the artifact

Search for the artifact the exploit leaves behind. The reported files match shx*.php, with examples including shrf4645.php, shxa35d0.php and shx13a86.php, and the recovered payload has the SHA-256 hash 4b9fbd0663fbfd6028fc216ed18e0d6ef3ae498a7fdc97083e4b0ce3cec40562.

Do not stop at the directory the advisory names. One administrator found the file through Nextcloud's integrity check, and another learned of the intrusion from failed sudo emails by the web server account. Both paths found the same compromise through different evidence.

Step five: check the logs for attempts, not just successes

Attempts matter as much as successes. Look for requests to /apps/richdocumentscode/proxy.php?status whose paths contain %2e%2e, $( or ${IFS}, and for the source addresses 139.28.18.122 and 89.38.98.50.

One of the three administrators logged another attempt on September 14 against a host already updated to 26.4.303. No file appeared because the fix held, and only the log recorded that the host was targeted. A checklist stopping at file discovery would have missed it.

What the checklist refuses to do

The checklist does not produce an affected-host count, because external data cannot supply that number. It does not present asset totals as compromise figures. And it does not accept a feature toggle as remediation when the vulnerable file sits outside the application layer.

What it produces is a defensible sequence: identify the component, measure your own surface, separate presence from patch state, look for the artifact, and read the logs for attempts. Each step has evidence behind it, and each can be shown to an auditor later.

References

Top comments (0)