eclipse-ecal / fineftp-server

📦 C++ FTP Server Library for Windows 🪟, Linux 🐧 & more 💾
MIT License
311 stars 64 forks source link

[Vulnerability] Path traversal results in an arbitrary file read/write #52

Closed cha512 closed 1 year ago

cha512 commented 1 year ago

Suppose an attacker has a credential to access FTP server.

And suppose a user using fineFTP wants to share only a specific directory, as shown below.

#include <fineftp/server.h>

#include <iostream>
#include <thread>
#include <string>

int main()
{

    const std::string local_root =  "/home/kong2204/Desktop/";

    fineftp::FtpServer server(2121);

    server.addUserAnonymous(local_root, fineftp::Permission::All);
    server.addUser         ("MyUser",   "MyPassword", local_root, fineftp::Permission::ReadOnly);
    server.addUser         ("Uploader", "123456",     local_root, fineftp::Permission::DirList | fineftp::Permission::DirCreate | fineftp::Permission::FileWrite | fineftp::Permission::FileAppend);

    server.start(4);

    for (;;)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    return 0;
}

In this case, an attacker can access unauthorized directories via path traversal.

Here's why.

The implementation of the toLocalPath function used by fineFTP to handle paths is shown below.

    std::string FtpSession::toLocalPath(const std::string& ftp_path) const
    {
        assert(logged_in_user_);

        // First make the ftp path absolute if it isn't already
        const std::string absolute_ftp_path = toAbsoluteFtpPath(ftp_path);

        // Now map it to the local filesystem
        return fineftp::Filesystem::cleanPathNative(logged_in_user_->local_root_path_ + "/" + absolute_ftp_path);
    }
    std::string FtpSession::toAbsoluteFtpPath(const std::string& rel_or_abs_ftp_path) const
    {
        std::string absolute_ftp_path;

        if (!rel_or_abs_ftp_path.empty() && (rel_or_abs_ftp_path[0] == '/'))
        {
            absolute_ftp_path = rel_or_abs_ftp_path;
        }
        else
        {
            absolute_ftp_path = fineftp::Filesystem::cleanPath(ftp_working_directory_ + "/" + rel_or_abs_ftp_path, false, '/');
        }

        return absolute_ftp_path;
    }

A logic is needed that checks whether the return string of toLocalPath is a subdirectory of the local root (or the local root itself). However, there isn't

Thus, if /../../../../ (or /..\..\..\..\..\ on Windows) is used, depending on the account's permissions, file reads or writes for unauthorized paths can become possible.

FlorianReimold commented 1 year ago

@cha5126568: Thanks for finding this vulnerability! This is going to get fixed ASAP!

Please keep in mind that in any situation where this vulnerability matters you probably shouldn't use plain unencrypted FTP anyways, but also that's not an excuse for this vulneratbility.

FlorianReimold commented 1 year ago

Solved by #58