Re: [VULN 4/4] Process auth man-in-the-middle

2021-11-05 Thread Sergey Bugaev
Disclaimer: while I'm a fan of capabilities / object capabilities /
capability-based security, and also a "Unix person", I (obviously)
wasn't among those who have designed Mach, or Hurd, or Unix. So I
cannot speak authoritatively, I can only attempt to share what my
understanding is.

On Fri, Nov 5, 2021 at 3:52 PM William ML Leslie
 wrote:
>
> On Fri, 5 Nov 2021 at 22:17, Sergey Bugaev  wrote:
>> The Hurd is a capability system, but not a *pure* capability system:
>> it implements Unix semantics on top of Mach/capabilities. File
>> descriptors, exec, setuid, etc. are all Unix concepts.
>
>
> Indeed, and the reason I ask is because I'd like to enable more capability 
> patterns, even though Mach and The HURD don't protect ports like they are 
> capabilities (c.f. hurd/utils/msgport.c for some really cool and impressive 
> "look at how much power you have!" moments).

I don't think I understand what you mean by "don't protect ports like
they are capabilities". Surely Mach ports are capabilities!

You can get the ports that a task has if you have its task port, but
in that case you already have full control over the task, so you could
make it send the ports to you explicitly. msg_get_init_port ()
alternatively lets you specify the auth port as a credential, but if
you have that you could trivially get the task port too.

Capabilities can grant different access to the actual object they
reference. In _object_ capability environments (think E or Java), this
is not true, you either have a capability to the object or not. But
then there are objects/capabilities whose only purpose is to provide
limited access to another object/capability, so it's effectively the
same thing.

In Apple's Mach, there's a "task info port", task_info_t, that only
grants you basic inspect-only access to a task. But actual Mach task
ports (and the only kind that GNU Mach has) grant you complete, full
access to the task. And if you have full access to the task, there's
nothing insecure about getting the ports that the task has.

> One wonderful property of capabilities is that delegating them works.  You 
> should be able to weaken them deliberately, but having them weakened for you 
> is a bit weird.  The stranger thing is having them become more powerful 
> without an explicit action.  Implicitly gaining more power on what is 
> supposed to be a capability sounds like a new way to introduce a confused 
> deputy vulnerability.

Well, yes, sure, setuid is the textbook example of how to get confused
deputies: a setuid executable serves two masters (the invoker and the
file owner), most of its context is set up by the invoker, so it needs
to ignore things like LD_LIBRARY_PATH, but you can never remember all
the things you have to ignore, and so on.

But setuid is also the only way in Unix to raise privileges! Plan 9,
for instance, has /dev/capuse, which lets you raise your privileges at
run time (after authenticating). The Hurd does the same — you can
authenticate to the password server and get a new auth port; and using
the msg.defs you can even change auth of other processes this way,
without them having to do anything explicitly. But the Hurd still has
to keep support for the (arguably inferior) Unix setuid mechanism too.

If strict Unix compatibility was not a concern, could addauth &
friends replace setuid completely? I believe this would require
converting all sorts of setuid utilities into privileged servers. The
password server is essentially 'su' turned into a server; all the
other setuid utilities would need similar treatment too. So for
instance there'd be a ping server that does the privileged operation
(using raw sockets) at the request of unprivileged clients.

> Or alternatively, ignore glibc exec() and talk to the exec and proc servers 
> directly.

Sure, you could do that, but then you wouldn't get the actual setuid
behavior. /bin/su would still run as your user, and would not be able
to give you a root shell.

Sergey



Re: [VULN 4/4] Process auth man-in-the-middle

2021-11-05 Thread William ML Leslie
On Fri, 5 Nov 2021 at 22:17, Sergey Bugaev  wrote:

> On Fri, Nov 5, 2021 at 1:41 PM Samuel Thibault 
> wrote:
> >
> > William ML Leslie, le ven. 05 nov. 2021 21:18:50 +1100, a ecrit:
> > > > which makes the root filesystem reauthenticate all of the
> > > > processes file descriptors.
> > >
> > > It seems to eliminate a rather convenient method of delegation; a
> > > process opening a descriptor, forking and executing a child, and
> > > dropping privileges, while retaining access to that one resource.
> >
> > reauthenticating doesn't mean closing. File permissions for open are
> > checked at the open step, not later on. But then there are other things
> > than just opening a file, such as starting a translator, which we don't
> > necessarily want to let the unprivileged-with-one-opened-file do.
>
> If I may add to what Samuel has said...
>
> The Hurd is a capability system, but not a *pure* capability system:
> it implements Unix semantics on top of Mach/capabilities. File
> descriptors, exec, setuid, etc. are all Unix concepts.


Indeed, and the reason I ask is because I'd like to enable more capability
patterns, even though Mach and The HURD don't protect ports like they are
capabilities (c.f. hurd/utils/msgport.c for some really cool and impressive
"look at how much power you have!" moments).


> And in a Unix
> system, you definitely expect that a change in a process' authority
> applies to things like which files it is allowed to open through a
> file descriptor that refers to a directory, which is why all the file
> descriptors have to be reauthenticated when the auth changes. The file
> descriptor table is really meant to hold I/O objects, not arbitrary
> ports; and I/O objects are expected to behave in a predictable way
> when reauthenticated: you keep access to the object, but the object
> reconsiders what further things you can do to it /  access through it,
> according to your new authority.


I'm still not confident this is the right approach, but if that's limited
to changing the behaviour of translator nodes that is much less scary.

One wonderful property of capabilities is that delegating them works.  You
should be able to weaken them deliberately, but having them weakened for
you is a bit weird.  The stranger thing is having them become more powerful
without an explicit action.  Implicitly gaining more power on what is
supposed to be a capability sounds like a new way to introduce a confused
deputy vulnerability.  I have read access to this, I exec you, and you have
write access.  If you were fishing this file out of the file system you'd
expect it to have your authority, but if you were passed it from another
program, you expect to use the authority that the caller provided and no
higher.


> As another example, you can open a
> device while you have root access, then drop privileges; you'll still
> be able to read or write the file descriptor, but you won't be able to
> fchown/file_chown it.
>
>
Righto yes, a port to an open file is a port to an io object, and io
objects can implement messages like io_mod_owner and so.  I'd missed that
these were not distinct responsibilities.

> If you ignore the Unix personality of the system, you can indeed pass
> any ports to an otherwise unprivileged task, but you better do it
> outside of the file descriptors table. The Mach mechanisms for that
> are the bootstrap port and mach_ports_register ().
>

Or alternatively, ignore glibc exec() and talk to the exec and proc servers
directly.

-- 
William ML Leslie


Re: [VULN 4/4] Process auth man-in-the-middle

2021-11-05 Thread Sergey Bugaev
On Fri, Nov 5, 2021 at 1:41 PM Samuel Thibault  wrote:
>
> William ML Leslie, le ven. 05 nov. 2021 21:18:50 +1100, a ecrit:
> > > which makes the root filesystem reauthenticate all of the
> > > processes file descriptors.
> >
> > It seems to eliminate a rather convenient method of delegation; a
> > process opening a descriptor, forking and executing a child, and
> > dropping privileges, while retaining access to that one resource.
>
> reauthenticating doesn't mean closing. File permissions for open are
> checked at the open step, not later on. But then there are other things
> than just opening a file, such as starting a translator, which we don't
> necessarily want to let the unprivileged-with-one-opened-file do.

If I may add to what Samuel has said...

The Hurd is a capability system, but not a *pure* capability system:
it implements Unix semantics on top of Mach/capabilities. File
descriptors, exec, setuid, etc. are all Unix concepts. And in a Unix
system, you definitely expect that a change in a process' authority
applies to things like which files it is allowed to open through a
file descriptor that refers to a directory, which is why all the file
descriptors have to be reauthenticated when the auth changes. The file
descriptor table is really meant to hold I/O objects, not arbitrary
ports; and I/O objects are expected to behave in a predictable way
when reauthenticated: you keep access to the object, but the object
reconsiders what further things you can do to it /  access through it,
according to your new authority. As another example, you can open a
device while you have root access, then drop privileges; you'll still
be able to read or write the file descriptor, but you won't be able to
fchown/file_chown it.

If you ignore the Unix personality of the system, you can indeed pass
any ports to an otherwise unprivileged task, but you better do it
outside of the file descriptors table. The Mach mechanisms for that
are the bootstrap port and mach_ports_register ().

Sergey



Re: [VULN 4/4] Process auth man-in-the-middle

2021-11-05 Thread William ML Leslie
On Fri, 5 Nov 2021 at 21:41, Samuel Thibault 
wrote:

> William ML Leslie, le ven. 05 nov. 2021 21:18:50 +1100, a ecrit:
> > > which makes the root filesystem reauthenticate all of the
> > > processes file descriptors.
> >
> > It seems to eliminate a rather convenient method of delegation; a
> > process opening a descriptor, forking and executing a child, and
> > dropping privileges, while retaining access to that one resource.
>
> reauthenticating doesn't mean closing. File permissions for open are
> checked at the open step, not later on. But then there are other things
> than just opening a file, such as starting a translator, which we don't
> necessarily want to let the unprivileged-with-one-opened-file do.
>
> Samuel
>

I see, thank you!

-- 
William ML Leslie


Re: [VULN 4/4] Process auth man-in-the-middle

2021-11-05 Thread Samuel Thibault
William ML Leslie, le ven. 05 nov. 2021 21:18:50 +1100, a ecrit:
> > which makes the root filesystem reauthenticate all of the
> > processes file descriptors.
> 
> It seems to eliminate a rather convenient method of delegation; a
> process opening a descriptor, forking and executing a child, and
> dropping privileges, while retaining access to that one resource.

reauthenticating doesn't mean closing. File permissions for open are
checked at the open step, not later on. But then there are other things
than just opening a file, such as starting a translator, which we don't
necessarily want to let the unprivileged-with-one-opened-file do.

Samuel



Re: [VULN 4/4] Process auth man-in-the-middle

2021-11-05 Thread Samuel Thibault
William ML Leslie, le ven. 05 nov. 2021 21:18:50 +1100, a ecrit:
> I've been meaning to ask: Why does the hurd attempt to re-authenticate open
> file descriptors during exec?

That's done only when the auth port changes, i.e. uid/gid etc. following
a setuid/setgid/etc. trigger.

Samuel



Re: [VULN 4/4] Process auth man-in-the-middle

2021-11-05 Thread William ML Leslie
CC list reduced considering I'm going to ask about a slightly different
topic.

This is fantastic research Sergey, this vuln especially so.

On Wed, 3 Nov 2021 at 03:49, Sergey Bugaev  wrote:

>
> To get someone privileged to authenticate to me, I went with the same
> exec(/bin/su) trick, which makes the root filesystem reauthenticate all of
> the
> processes file descriptors. If we place our own port among the file
> descriptors,
> we'll get a io_reauthenticate () call from the root filesystem on it, which
> we'll forward to the proc server, pretending to reauthenticate our process.
>
>
I've been meaning to ask: Why does the hurd attempt to re-authenticate open
file descriptors during exec?  It seems to eliminate a rather convenient
method of delegation; a process opening a descriptor, forking and executing
a child, and dropping privileges, while retaining access to that one
resource.  I realise you can still do this by manipulating ports directly
(this only applies specifically to the contents of the descriptor table).
Is it required for posix compliance somehow, or was there some other
interesting use case?

-- 
William ML Leslie


[VULN 4/4] Process auth man-in-the-middle

2021-11-02 Thread Sergey Bugaev
Short description
=

The use of authentication protocol in the proc server is vulnerable to
man-in-the-middle attacks, which can be exploited for local privilege escalation
to get full root access to the system.


Background: authentication
==

Here, the word "authentication" refers not to a human user signing in to the
system, but rather to a component of the system communicating and proving its
authority to another component of the system. For example, to be able to open
and read a file, a client process may need to convince the translator which
provides the file that the client has the appropriate UIDs to be allowed to
access the file. Essentially, the Hurd authentication mechanism serves to bridge
the capability system of Mach with the *ambient authority* system of Unix UIDs.

To make the rest of the description easier to follow, I'm going to name the
involved actors, as is commonly done in literature [0]:

* Alice is a client process who wishes to authenticate itself
* Bob is a server process who's accepting authentication
* Carol is the Hurd auth server

[0]: https://en.wikipedia.org/wiki/Alice_and_Bob

The Hurd represents authority as _auth handles_, which are ports to the auth
server (Carol); each auth handle corresponds to a set of UIDs (and GIDs)
maintained by Carol. For Alice to authenticate itself to Bob means her
demonstrating (and proving) to Bob that she has an auth handle with a given set
of UIDs. A straightforward way to do that would be for Alice to send her auth
handle to Bob, letting Bob inspect it (by asking Carol what UIDs it represents).
However, giving Bob direct access to the auth handle is completely unacceptable,
because Alice may actually be more privileged than Bob: for instance, Alice may
be a root-owned process who reads a file from a file system implemented by Bob,
an unprivileged translator. The mere act of Alice authenticating herself should
not result in Bob getting root access.

So the Hurd authentication mechanism is instead designed as a three-way
handshake between Alice, Bob, and Carol:

1. First, Alice and Bob "shake hands" by agreeing on a "rendezvous" port right;
   this port right does not have to be anything special, but the two sides need
   to be in agreement about what it is. The typical way this works is that Alice
   creates a fresh new port to serve as the rendezvous port, and initiates the
   authentication process by sending the rendezvous port to Bob in a
   foo_reauthenticate () RPC call.

2. Next, Alice "shakes hands" with Carol the auth server by sending her the
   rendezvous port in a auth_user_authenticate () RPC call on her auth handle.

3. Concurrently with that, upon receiving the rendezvous port from Alice, Bob
   also "shakes hands" with Carol by also sending her the rendezvous port in a
   auth_server_authenticate () RPC call.

Carol matches up the two calls by the rendezvous port and returns Alice's UIDs
(but not her handle!) to Bob. Provided Bob trusts Carol (as he should, since
she's the trusted system auth server), he now reliably knows Alice's UIDs, but
he never got access to her auth handle.

Note: the role the rendezvous port plays in this is in a way similar to a
single-use read-only auth handle.


Background: man-in-the-middle attacks
=

The design described above still has a fatal flaw: the possibility of
man-in-the-middle attacks. Let's imagine there's another process, Eve, who
stands in between Alice and Bob; so Alice is not talking to Bob directly, but
rather to Eve, while Eve is trying to impersonate Alice to Bob. (It would
perhaps be more correct to name the attacker Mallory rather than Eve, but I've
been thinking of her as of Eve for multiple years now, so I'll stick with that
name.)

Alice sends her rendezvous port to Eve in a foo_reauthenticate () RPC call. Eve,
instead of sending the port to Carol the auth server in a
auth_server_authenticate () call, forwards the port to Bob in her own
foo_reauthenticate () call. Bob then asks Carol about this rendezvous port, and
gets Alice's UIDs in response, since it's Alice (and not Eve) who passes the
rendezvous port to Carol on the client side. Yet, Bob believes the UIDs to
belong to Eve, since it's her who has been interacting with him. And so, Eve has
now effectively stolen Alice's identity.

Knowing that this could happen, Bob has to be aware that the UIDs Carol tells
him about may not, in fact, belong to the client who has initiated the
authentication process with him (Eve), they may instead belong to someone else
(Alice) who's being man-in-the-middle-attacked.

To make this work, the Hurd authentication protocol has one more feature: the
_new port_ mechanism. This new port is a port right that Bob may pass back to
Alice through Carol. Bob passes this new port to auth_server_authenticate (),
and Alice receives it from auth_user_authenticate (). In case of a
man-in-the-middle attack, it is Alice -- the actu