3

I recently learned that in Windows a process is generally allowed to alter the the memory of another process as long as it is running under the same user. I tried running the following code with various pids:

#include <iostream>
#include <processthreadsapi.h>
#include <errhandlingapi.h>

int main()
{
    HANDLE handle = OpenProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION|PROCESS_QUERY_INFORMATION, FALSE, 1232);

    if (handle == NULL)
        std::cout << "DENIED " << GetLastError() << "\n";
    else
        std::cout << "ALLOWED\n";

    return 0;
}

I noticed that for the most part I was granted access to processes that were running as my own user. Processes running as System or Admin were denied access.

In contrast I found out that in Linux one needs to generally use ptrace to attach to another process for reading from and writing to its memory. However, by default only parents are allowed to attach to a child process.

Is there any reason Windows does not enforce a similar constraint where only a process that spawned another would have access to its memory?

2 Answers 2

3

The Windows (and, for that matter, Linux) security model generally holds that everything a user (in the "login account" sense) does is done at the same trust level. In other words, any two processes running with an identical security token are trusted exactly the same amount. If one process can access its own memory (which of course it can), so can the other.

If an attacker has arbitrary code execution on your account, it's already game over. Even if you don't let them access the memory of current processes, they can launch processes themselves under a debugger, overwrite scripts/libraries/executables stored in user-writable locations (such as your login/shell resource scripts), read or write all the data that a program running as you would have access to, and so on. In other words, there's very little value in creating a security boundary between a user's processes.

Now consider the advantages of not doing so. You don't need to be admin/root to debug a running process, which might be in a weird state that would be hard to re-create. You can write tools that modify or extend the functionality of programs installed in non-user-writable locations (this is heavily used in game modding, for example). Basically, you - the user - have more control over the software running on your session, and you can do it without needing elevated privileges. The only cost is that there are a few new ways that somebody an attacker running code as you can hurt you, and at that point, they don't really need any more.

Windows' Process objects do have ACLs, which can be used to do things like give another user access to your process' memory or allow certain access (such as the ability to tell when the process exits) without other access (such as the ability to write to the memory). You could also restrict the access to a process from its own user - the same way you can restrict access to a file you own - but the owner of any object can overwrite its ACL so another process also running as you could just overwrite that restriction (again, like marking a file you own "read-only").


In general, if you want to ensure other processes cannot tamper with a process you launched, you need to launch it as another user. There's a few ways to do this, such as CreateProcessWithLogon or creating a Windows Service. The Win32 API does not provide an equivalent of the "setuid" bit on a file, but a privileged service account can emulate that behavior. If you want to restrict what an app can do, you can easily create a low-privilege version of your own security token with CreateRestrictedToken and then create a sandboxed process using it (an open-source example of this is the Chrome sandbox on Windows).

3

Traditionally, it was possible in Linux to read the memory of your own processes, too.

Please note that being able to attach other processes is very useful when debugging, either directly for your own programs (e.g. attaching with gdb to the running binary you compiled a few minutes ago) as well as for some general knowledge of programs that don't seem to behave right (why is this program taking so long? what is it trying to open?).

Programs can opt-out from this by setting prctl(PR_SET_DUMPABLE, 0); and this is typically done by programs holding sensitive data (keyring, key agents...)

However, most users do not need or use it and this capability means that a malicious program on your user session could alter any other program -where most user data reside- and for instance extract the credentials being used in completely separate accounts.

In order to thwart this, some people wrote a Linux Security Module (LSM) called Yama that protected this debugging interface (ptrace syscall). It defines these modes:

  • 0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other process running under the same uid, as long as it is dumpable (i.e. did not transition uids, start privileged, or have called prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is unchanged.

  • 1 - restricted ptrace: a process must have a predefined relationship with the inferior it wants to call PTRACE_ATTACH on. By default, this relationship is that of only its descendants when the above classic criteria is also met. To change the relationship, an inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare an allowed debugger PID to call PTRACE_ATTACH on the inferior. Using PTRACE_TRACEME is unchanged.

  • 2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace with PTRACE_ATTACH, or through children calling PTRACE_TRACEME.

  • 3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via PTRACE_TRACEME. Once set, this sysctl value cannot be changed.

You can read the full document at https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/Documentation/security/Yama.txt?h=v4.4.199

Nowadays, you see most kernels using the option 1 by default, which allows some user tracing while blocking the above attack scenarios.

Still, it is not how it was traditionally done, and it can be annoying for developers. Similarly to *nix systems, Microsoft has also traditionally allowed attaching to any process. More recently (Windows Vista), it added Integrity Levels, which allows -on an opt-in basis- to run a program in with a different trust (such as a browser process) that will not be able to perform these actions.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .