Skip to content

Path traversal via binary reference in node import #12142

@rymsha

Description

@rymsha

Summary

When importing nodes from an export, a node's binary reference is read verbatim from the export's node.xml and joined into a path that NodeImporter resolves against the export location to locate the binary attachment. The reference is not validated (BinaryReference.from only checks for null/empty), so a crafted reference such as ../../../../etc/passwd can steer a filesystem-backed VirtualFile outside the export root and read an arbitrary file into the imported node's binary attachment.

Affected code

com.enonic.xp.core.impl.export.NodeImporter#tryFindBinaryFile joins the attacker-controlled reference:

final String binaryReferenceAsString = binaryReference.toString();
final VirtualFile binaryOriginal =
    nodeFile.resolve( nodeFile.getPath().join( SYSTEM_FOLDER_NAME, BINARY_FOLDER, binaryReferenceAsString ) );

Attack vector

An import is performed (admin-triggered) over an export archive authored elsewhere. The node.xml is outside the importing operator's control, so traversal sequences in a <binaryReference> value cross a trust boundary.

Fix

Validate the binary reference as a safe, single file name before resolving it, reusing the existing FileNames.isSafeFileName guard already applied to content attachment names (validateCreateAttachments) and to other import file names (exportName, xsltFileName). The reference is normalized to NFC first so the existing UTF-8-MAC/NFD fallback still works. Invalid references are refused and recorded as an import error.

Related (not addressed here)

The node-level API (CreateNodeCommand, lib-node) does not validate binary reference names — only the content layer does. Consequently NodeExporter#exportNodeBinaries, which builds a path from reference.toString(), could write outside the export directory if a node carrying an unsafe reference (created via the node API) is exported. Reaching that requires node-API access, which in XP's trust model implies installed-application code, so it is lower severity than the cross-boundary import case. A defense-in-depth guard at the export write boundary can be considered as a follow-up.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSecurityFix for something unsafe

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions