>The proc tools tend to work by using something called an "agent thread"
>to execute code in the victim process.  The execution environment of
>agent thread code is very, very, very barebones: there's no linker, no
>loader, no allocator, nothing, and you have to download the code to run
>to the agent thread and such code usually just makes system calls (with
>all other threads in that process stopped).  All this is so that the
>proc tool can run code in the victim without having to trust any code in
>the victim's address space, which is very important from a security
>point of view.

I hadn't looked at the security issue of the agent thread from that
perspective; I find the whole "agent thread" a bug, not a feature.

The agent thread was fine in the time of uid 0 but with privileges
it no longer works so well.

Typically, the agent thread is used in the following manner:

        - stop lwps
        - bump privileges/effective uid to 0 [NOTE: this no longer works
          with the limit set]
        - insert agent thread.
        - run it
        - remove privileges
        - start lwps again

Clearly, there is plenty of room for serious issues here, including
what Nicolas says: the agent thread being at the mercy of the context.

But the basic flaw is that in order to perform the agent thread insertion
your process needs to "dominate" the target process.

And then it becomes clear why the mechanism is flawed:

        - the priocntl(2) system call works on any process so all you
          need is a restricted set of privileges to change any process'
          priority

        - the setrctl() system call works on the current process only;
          in order to affect other processes we need to use an agent
          thread.  Potentially, we need all privileges if we want to
          be able to affect any odd process

I maintain that the mechanism is broken and that we should not insert
agent threads; rather, we should defined proper mechanisms to set
parameters on other processes; we should pick the one we like best
from the current mechanisms and, going forward, only allow new mechanisms
to conform to that standard.

To me, that means no more agent lwps.

(Current mechanisms are: writes to /proc/<pid>/ctl (ppriv, pcred),
system interfaces (priocntl()) or agent lwps (newtask, prctl))

>The most obvious way to make psetenv(1) work would require enhancing the
>agent thread environment so that for _some_ tasks (where security is not
>an issue, and I think modifying the victim's environ is one such task)
>one could load code and link it with the application's link map list.

I fear dragons:

        - the agent lwp must in this case run concurrently with the other
          threads in the process because the environment and heap
          locking protocols must be honored

        - single threaded processes have some optimizations required to
          prevent performance regressions (severe enough to be patched
          back into S10; these optimizations can only be done when there is
          NO WAY that a thread gets magically created through a means
          other than *thread_create.

        - applications may assume getenv("PATH") will always return the
          same value

        - it's unlikely that psetenv will work on any shell because they
          are likely doing special environment handling.

and all the usual "must dominate process" issues.

>(BTW, a special rtld for agent threads would make it a lot easier to
>write proc tools too, but wouldn't help in this case since modifying the
>victim's environ kinda requires being linked with the victim's libc.)

Ceterum Censeo, agent threads must die.

Casper

Reply via email to