Did I miss anything?
Did I miss anything?
That's why the bug says: "The overall impact of this bug is pretty minimal in our current set of supported products, since none support running untrusted native code, and if you can run your own code on the system, then (at present) you can also use other existing supported workflows to obtain kernel logs, but it does seem to be a useful stepping stone towards privilege escalation if you have already obtained code-exec in some process through another exploit."
So while not awesome, also not possible on a real device right now without a code-exec exploit.
The fact he got KASAN working and talks about fuzzing suggests he looked for one, but couldn't find one, which is a good sign.
I just assume C++ code is unsafe, because it's really really hard to make it safe.
However, at the same time, the privilege escalation issues would have happened in any language - if you don't implement the check, you don't implement the check.
(and you could make it equally automatic in most languages)
Yes, this particular one enabled a defeat of ASLR, but so what? Missing access checks enable privilege escalation no matter what the language.
Your claim that "has well-definable consequences" is equally true in C++ as anywhere else. Whether you miss your access check in rust, or C++, or python, or whatever, the definable consequences are "privilege escalation".
Let's not pretend memory safe languages solve logic problems. They help with memory safety - that's awesome but not a complete solution.
If we want better verification of access contracts, we'd need a language with contracts or some other verifiable mechanism.
Those exist, and i'd support their use in this sort of case.
There is no equivalence to make. Memory corruption bugs are unambiguously, unequivocally better exploitation primitives than logic bugs.
No one is claiming that memory-safe languages solve logic problems. The claim is that most memory corruption bugs are conveniently exploitable and any exploit can reach for anything in the address space, and logic bugs often can’t. You could as well say that you’d rather promote chess pawns to knights instead of queens. Like, that makes sense sometimes, but it’s a bad default.
The core idea of capabilities is more like having a URL to a Web page. Using the URL (the capability), you can access the contents of the page. Inside the contents, you can possibly find other URLs (more privileges granted to you). But the URL happens to be something like an UUID, or a short link; looking at it, you cannot derive another URL (discover another "capability", not granted to you).
In other words, a capability is like a key in a hash table, and unlike an index in an array.
That is -- are capabilities just pieces of data in a message you can detect and try to use, or do they have to be added explicitly to a message to send them
It uses a couple of techniques, like wide/tagged pointers, object ids, and a special hardware managed bit to track illegal modifications.
If I had to hazard a guess, those object ids are probably useful for general capability systems.
I think apple (maybe as just an arm feature?) can do encrypted pointers, with a per application key tracked by the kernel.
ARM has added both pointer authentication codes and memory tagging extensions to their ISA, from (I think) ARMv8.5-A. Apple are the only ones to implement silicon that supports PAC that anyone can buy today but a) this will change fast and b) I might've missed something else.
I wouldn't say "encrypt". You can still read what the pointer is, you just can't forge it. It is more like a message authentication code, or keyed hash. Confusingly, the ARM ISA suggests you use the block cipher QARMA, a lightweight block cipher of their design. This is not a mistake - from a crypto academic's perspective the distinction between block cipher and hash is not so easy to draw as it tends to be communicated: you can get a block cipher out of the SHA family of hash functions (called SHACAL) and so on. There was a line of work on finding a suitable family of permutations (e.g. Gimli) that could be hardware-accelerated and appropriate constructions built from that.
tl;dr it is kinda a "signed pointer", and if you forge the pointer but get it wrong you don't get another chance because the CPU triggers a fault, so you can use lower security lightweight ciphers and still make exploitation very unreliable.
Now Fuchsia:
As to how Fuchsia does it, we can just look at the source:
1) https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/z... 2) https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/z... 3) https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/z...
For example. I don't think there's any magic here: the kernel maintains some structure to reference jobs, tasks or whatever we are calling our isolated privilege boundaries, in which the capabilities are described. On the face of it this "looks" a lot like the traditional permission-style system, but the key difference is that the root job will hand out capabilities to processes it has created. An example is probably the best way to proceed: on a linux system, you might decide your process needs to read `/system/sensitive_file`. To allow this to happen, you might grant access to the user using some other command: chown .../chmod ..., or you might set an selinux policy, semanage fcontext -a -t myprocess_t /system/sensitive_file. In a capability-based system, chmod/chown and even running the process as a user don't exist as concepts. You would need to use a process authorized to transfer a capability to your target process to access this file. In some ways, this is closer to selinux, in the sense that semanage fcontext updates policy, except that the policy is somewhat dynamic here: if your controlling process provides the capability, then the child process gets it.
In a microkernel-based architecture, the idea is that "servers" (jobs that are arguably part of the system, but don't need to be in kernel) are similarly userspace processes subject to said capabilities also.
Of course, some part of the code still needs to be privileged here, let's call it the "executive". This code is doing the capability enforcement, and if that code contains bugs, all bets are off, as always. You can't defend such code from itself, even with capabilities.
The real disappointment is that Zircon is written in C++. Granted it was probably started before Rust was a thing, but while Ada/SPARK might be considered a bit unfashionable, it has some formal verification available, and if you're going for a software solution this should help reduce exploitable bugs. Personally I also wonder what about seL4 made it unsuitable for this: granted, it is not complete as a system, but neither is Zircon alone.
A lot of good stuff has come from SPARC :)
I'm not aware of other generally available chips with pointer authentication. The Qualcomm Snapdragon 8cx Gen 3 supposedly support PA. The Lenovo ThinkPad X13s supposedly contains it, but when will it actually be commercially available is anyone's guess.
Also, I think it's ARMv8.3.
We use (somewhat) large and non-dense numerical values for handle values to reduce the risk of accidental reuse of values.
In my comment I meant "in theory fuscia could have been architected/could be rearchitected that way", sorry should have been more clear.
File systems aren't actually capability based (generally, in practice) because you can 'ls' and 'cd ../'. Otherwise they could be.
Dropbox Paper is a good example of a capability based system. Anyone with a URL can perform actions on a page, but there is no way to derive a URL without already having access to it, you must be told what it is. This is because the urls are sufficiently random so as to be unguessable.
That's precisely how it works in FreeBSD (https://www.freebsd.org/cgi/man.cgi?capsicum).
There's also openat on linux. My point is that the general, practiced approach is not capability based.
(One process can request that the kernel transfer it to another process, which I guess you might be referring to? I'm not familiar with Fuchsia specifically here but generally speaking capability-based designs sometimes let you "revoke" a capability if that is something you need, though that's not really something you would need to do periodically.)
The Dropbox approach is sort of a fuzzy emulation of this, by necessity, because it's a public internet service.
No, this is incorrect. Let's just quote wikipedia,
> A capability (known in some systems as a key) is a communicable, unforgeable token of authority.
communicable. It is practically the whole point that you can say "hey, here's a capability I have, now you have it".
Yes, it matters deeply if a capability is leaked. To name something is to authorize something, in capabality land.
No, you do not need to ask for it to be transferred. As with file descriptors in linux you can fork to delegate (or otherwise pass the handle around). Again, it is fundamental to capabilities that the capability is the authorization.
Dropbox's approach is faithful. And, in order to deal with these limitations of capability systems, Paper supports ACLs as well as capabilities. I think it's going to be a huge problem if Fuschia doesn't, but I don't know what they do - certainly some sort of MAC is desirable.
Obviously if you communicate the actual capability itself to someone you didn't intend to, that would be bad. But that's more of a question of API design- even fork is, in this sense, asking for the capability to be transferred. This is a very different problem than the one Dropbox has- no amount of random guessing, or side channel information leaks, or anything else like that, is going to land a capability in another process's kernel table.
> you can in principle forge a URL in a way that you could never forge a file handle.
Maybe it would be better to not talk about fs apis, because they're not really capability based, so I suspect that's the confusion.
The point is that if someone has a capability, they have that capability. There is no additional access control or checking in a capability based system. You absolutely have to consider things like rotation and revocation.
In this sense, fs APIs are a fine example, as long as you keep the ls/.. caveats in mind. If you have a file descriptor for something inaccessible through the global file system namespace, then the only way to grant that to another process is via specifically-designed APIs like domain sockets or fork.
As I said in my first comment, there are of course other reasons you might want to revoke a capability. But unlike Dropbox URLs, true capabilities are unforgeable, so you don't need to rotate them out simply to make them harder to guess- they're already unguessable.
If I'm unable to convey authorization by giving you the token, it is not a capability. If I am able to guess the token, it is not a capability.
Delegation is not forgery. Forgery would be an entity creating a token without the token being delegated to that entity. Guessing is forgery, but you can "guess" a uuid in the same way that you can "break AES" with a bruteforce, which is to say, you can't.
The attack surface of "bit pattern" capabilities is much larger than that of "trusted state" capabilities- even if you can't practically guess one, all you need to break it is to discover its bit pattern somehow. Trick someone into printing it out, read it out of their memory space, leak it via a side channel, attack their UUID generator, etc. For this kind of capability, sure- periodic revocation might be a worthwhile mitigation technique.
But for "trusted state" capabilities, which is where this thread started, and what file descriptors exemplify, this all goes away. The attack surface is reduced to the kernel and the component's own API (nothing new here) and its use of a finite set of capability delegation APIs. Leaking an fd number does not leak the corresponding capability the way leaking a Dropbox URL does, so there is relatively little purpose in rotating those out.
Using your version of the word, since you appear incapable of operating in any other frame, Fuchsia does not use capabilities, and thus does not have the problem of leaking permissions via bit patterns. The original question of whether they need to be rotated periodically does not apply here.
Of course I've spent this entire thread explaining a clearly defined term, was that not obvious?
And yes, rotation is relevant to capabilities because leaking capabilities is a critical failure. It's not the only way to protect capabilities though, you can add ACLs or namespaces, which is what Fuschia seems to do.
You're taking "communicable" too literally. It doesn't mean "literally giving someone else this bytestring and now they can do it, too". It means "there is a way to give someone else the capability". SCM_RIGHTS is such a way to communicate capabilities between processes. Further up above you're also conflating files and file descriptors, which are very different things.
You can read more about it in Capability Based Computer Systems by Henry M Levy. The Hydra MMP, GE-645, and iAPX 432 are older implementions of this concept in hardware.
In both cases (TCB table enforced and crypto one way function enforced) the name is the capability, but in a kernel (or hardware) table enforced method that's interpreted to mean that you have permission to access the object by having a simply having reference to it in your descriptor table.
You can see this also in KeyKOS, EROS, Coyotos, XOK, and SeL4 among many other capability based kernels. They don't give you a capability just because you got access to a global name to the backing object; the whole shtick for them is there is no global names for objects, only descriptor tables.
> A capability thus provides addressing and access rights to an object.
> a program cannot access an object unless its capability list contains a suitably privileged capability for the object.
> Capability system integrity is usually maintained by prohibiting direct program modification of the capability list. The capability list is modified only by the operating system or the hardware.
> Thus, a program can execute direct control over the movement of capabilities and can share capabilities, and therefore, objects, with other programs and users.
You can think of capabilities lists as kernel managed namespaces if you'd like. Maybe that would help clear things up.
Crypto backed capabilities that merge the token with the capability itself are one morph of capability based systems, but that's not the scheme being described here. Leaking the name of a capability (the offset in the cap table) does not leak the capability itself in these TCB schemes. Capabilities are still communicable in these TCB schemes because they're explicitly supported in the IPC mechanisms like SCM_RIGHTS or seL4_Send along side normal data. The token naming a capability then is a per process concept and you can clone an identical capability into several tokens in the same cap table, unlike the crypto enforced scheme which are basically content addressed storage for a secret representing a capability.
Though, it is still worth thinking about how someone might accidentally leak a table-based capability via API misuse. For example, if table entries are reused over time you might get a sort of use-after-free situation where you mean to share an old capability but accidentally share the new one that replaced it.
1. Revocation is pretty critical
2. Bounding of capabilities is great - "You have this right for N seconds", meaning that a leak is less devastating
3. Not relying on capabilities is the best option. Capabilities are amazing, and a wonderful access control system. Their main benefit is that you can very naturally implement extremely fine grained access control. The downside is that it becomes hard to reason about that access control statically. ACLs are bad at super fine grained access control, but they're great for "I can look at a policy and know what this thing can/ can not do".
Layering ACLs and capabilities is a match made in heaven.
(caller_capabilities[WHATEVER_CAPABILITY])(...);
when the capability is not present, then you get no_method_found or something similar.