Skip to content

Reduce a vehicle-supplied MAVFTP file name to a file name before writ… - #3774

Open
nicholasaleks wants to merge 1 commit into
ArduPilot:masterfrom
nicholasaleks:fix/mp-01-mavftp-filename-traversal
Open

nicholasaleks wants to merge 1 commit into
ArduPilot:masterfrom
nicholasaleks:fix/mp-01-mavftp-filename-traversal

Conversation

@nicholasaleks

Copy link
Copy Markdown

A MAVLink-FTP directory listing is attacker-controlled: MAVLink has no authentication, so whatever answers ListDirectory chooses the entry names. Both download handlers passed that name straight to Path.Combine() as the local file name. Path.Combine() returns path2 when path2 is rooted, and keeps ".." for the filesystem to resolve, so an entry named @"....\plugins\evil.cs" or @"C:\Program Files (x86)\Mission Planner\plugins\evil.cs" writes outside the folder the operator picked. Anything landing in plugins\ is Roslyn-compiled and executed by PluginLoader.LoadAll() on the next start.

The upload path in this same file already calls Path.GetFileName() on its name for exactly this reason. The download path, which is the direction carrying untrusted input, did not. Apply the same reduction there, and skip an entry that cannot be represented as a file name rather than writing it somewhere else.

The remote path is deliberately left alone: it is built separately from the tree node plus the entry name, and sanitising it would change which file is fetched.

Related to: https://github.com/nicholasaleks/infected-drones/tree/main/MP-01_mavftp_plugin_rce

…ing it

A MAVLink-FTP directory listing is attacker-controlled: MAVLink has no
authentication, so whatever answers ListDirectory chooses the entry names. Both
download handlers passed that name straight to Path.Combine() as the local file
name. Path.Combine() returns path2 when path2 is rooted, and keeps ".." for the
filesystem to resolve, so an entry named @"..\..\plugins\evil.cs" or
@"C:\Program Files (x86)\Mission Planner\plugins\evil.cs" writes outside the
folder the operator picked. Anything landing in plugins\ is Roslyn-compiled and
executed by PluginLoader.LoadAll() on the next start.

The upload path in this same file already calls Path.GetFileName() on its name
for exactly this reason. The download path, which is the direction carrying
untrusted input, did not. Apply the same reduction there, and skip an entry that
cannot be represented as a file name rather than writing it somewhere else.

The remote path is deliberately left alone: it is built separately from the tree
node plus the entry name, and sanitising it would change which file is fetched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant