WEBVTT

00:00.000 --> 00:11.680
So, hello everyone, welcome our next speaker Adam, who will be talking about cup

00:11.680 --> 00:13.880
stroke and second.

00:13.880 --> 00:23.520
Good afternoon, I apologize if I panicked, I've just unsavvile staircases trying to get

00:23.520 --> 00:25.840
to the right part of the U building.

00:25.840 --> 00:29.680
As it says on the screen, my name is Adam Harvey, I am a security oriented software

00:29.680 --> 00:31.680
developer at the Rust Foundation.

00:31.680 --> 00:36.480
I've been there for a couple of years now, it's fun, I like it.

00:36.480 --> 00:40.520
And I'm here today to talk about a project called caps lock, which I will introduce in a

00:40.520 --> 00:45.080
second, and how we can use it to in conjunction with set comp to potentially do some

00:45.080 --> 00:47.240
interesting things.

00:47.240 --> 00:49.600
So let's begin with what is caps lock?

00:49.600 --> 00:53.920
So caps lock is an open source project, originally created by Google, that as it says on

00:53.920 --> 00:59.280
the screen, a copy paste from their read me, is capability analysis, CLI for go packages,

00:59.280 --> 01:03.920
and informs users of which privilege operations are given package accesses.

01:03.920 --> 01:08.680
It classifies those capabilities by following the call graph and into the standard library

01:08.680 --> 01:10.080
operations.

01:10.080 --> 01:14.120
So cool, this is useful in a couple of ways.

01:14.120 --> 01:18.280
Firstly, it gives you a sense of what is actually within your project.

01:18.280 --> 01:22.840
Practically, most of us are not auditing every single line of every single dependency, if

01:22.840 --> 01:28.520
you are a security developer, thank you so much, most of us aren't realistically, including

01:28.520 --> 01:29.520
me.

01:29.520 --> 01:32.240
So, yeah, I have no moral high ground here.

01:32.240 --> 01:36.680
So capability analysis gives you a quick way of looking for glaring red flags.

01:36.680 --> 01:40.000
If you're writing a microservice, it's just cases something in memory, and you see something

01:40.000 --> 01:45.200
that's called graph is making a file, I owe call to ECC password, probably that.

01:45.200 --> 01:47.960
So that's useful.

01:47.960 --> 01:53.080
Secondly, and we're actually thinking more of the utility is, it gives you a baseline.

01:53.080 --> 01:57.360
Projects tend to start small and well-scogged with few dependencies, and then I think any

01:57.360 --> 02:01.360
of us who have worked on projects have grown over time, you tend to accumulate more

02:01.360 --> 02:06.680
dependencies, and at a certain point you look out and you're like, why are there 700 things

02:06.680 --> 02:10.080
in my lock file now?

02:10.080 --> 02:13.880
Having run a capability analysis over the lifetime of a project gives you a baseline

02:13.880 --> 02:14.880
at the start.

02:14.880 --> 02:19.880
So you know what things are actually, what things are required for the project to work,

02:19.880 --> 02:21.880
and then you can see how those change over time.

02:21.880 --> 02:25.040
And if they change unexpectedly, then there's something you can investigate.

02:25.040 --> 02:28.640
That's useful from a change manager, management perspective.

02:28.640 --> 02:31.120
Okay, so that's caps lock.

02:31.120 --> 02:36.120
Now, you may remember from two slides ago that I actually work on rust, and this is for

02:36.120 --> 02:37.120
going.

02:37.120 --> 02:42.800
So into Alphoremago, they are an associated project of the open SSF, and if you know what

02:42.800 --> 02:48.080
that means, that would be great, please come find me after and tell me because I have no idea.

02:48.080 --> 02:53.200
Alphoremago's role is basically to catalyze, which really means fund, meaning false

02:53.200 --> 02:56.640
sustainable security improvements in the world of open source.

02:56.640 --> 03:01.600
They contacted a few bodies last year, including the Rust Foundation, to gauge interest

03:01.600 --> 03:06.000
and extending caps lock functionality to other programming languages.

03:06.000 --> 03:12.560
In the end, Rust, I, and Java via a survey edition of Code Intelligence were chosen

03:12.560 --> 03:16.960
to prototype caps lock implementations via grads.

03:17.040 --> 03:18.560
This work is ongoing.

03:18.560 --> 03:25.360
I would say, broadly, we're at the interesting tech demo stage of maturity right now.

03:25.360 --> 03:26.520
So hi, this is where we are.

03:26.520 --> 03:29.080
Hope you find it interesting.

03:29.080 --> 03:30.080
There's still lots to be done.

03:30.080 --> 03:34.000
I'm going to talk at the end more about what the next steps for this project are.

03:34.000 --> 03:40.720
The initial grants, catalyzation for this project, basically words in prototype, which

03:40.720 --> 03:42.640
is the stage we're at.

03:42.640 --> 03:46.960
The next step is really going to be deciding where to go from here in terms of making

03:46.960 --> 03:48.480
a more production ready.

03:48.480 --> 03:51.600
So consider this to be kind of a sneak peek of something that I hope will be coming later

03:51.600 --> 03:54.520
in 2026.

03:54.520 --> 03:59.760
So based on this aforementioned grant, I have been working with Walter Piers, who is my colleague

03:59.760 --> 04:01.640
on the security team at the Rust Foundation.

04:01.640 --> 04:03.320
He is the actual security engineer.

04:03.320 --> 04:06.880
I am just the dumb person who turns the ideas into code.

04:06.880 --> 04:09.560
To implement caps lock support for Rust.

04:09.560 --> 04:13.880
So we've implemented this to start with as a plugin for the cargo build system, cargo

04:13.880 --> 04:19.160
is the standard build system ship with Rust, called cargo caps lock.

04:19.160 --> 04:22.960
This tool right now provides two different ways of doing the analysis.

04:22.960 --> 04:27.680
The static analysis, I mentioned earlier, and runtime analysis, and we're going to start

04:27.680 --> 04:31.120
just quickly by diving into what the runtime analysis is because it's a little easier to

04:31.120 --> 04:36.240
kind of wrap your head around and then we'll get more into the static analysis side of things.

04:36.240 --> 04:39.280
The same analysis, of course, isn't really language-specific.

04:39.280 --> 04:43.640
What you do, you run a binary, you see what it does.

04:43.640 --> 04:48.120
Obviously, if you've ever played with S-Trace or the P-Trace APIs, you're going to be

04:48.120 --> 04:50.800
generally familiar with what this looks like.

04:50.800 --> 04:56.400
So in this case, we'll run LS, we'll output it to LS.json, and right now, it outputs

04:56.400 --> 04:57.400
something like this.

04:57.400 --> 05:01.080
This format is definitely not what this is going to look like in the long term.

05:01.080 --> 05:04.320
This is just something I threw together because I needed something.

05:04.320 --> 05:09.600
So LS, shockingly, requires the file capability.

05:09.600 --> 05:13.720
So it's going to access stuff on the file system and do things.

05:13.720 --> 05:14.720
That's pretty much it.

05:14.720 --> 05:15.720
Okay, cool.

05:15.720 --> 05:19.160
We've reinvented S-Trace, not very well, with more JSON.

05:19.160 --> 05:20.160
Terrific.

05:20.160 --> 05:22.200
Let's talk about static analysis.

05:22.200 --> 05:29.240
So at the simplest level what we're doing here is we hook into compilation, and we're

05:29.240 --> 05:34.000
going to look at the call graph for a binary, a library, or a crate, or whatever.

05:34.000 --> 05:36.920
And then we're going to map the function calls to capabilities.

05:36.920 --> 05:43.200
So for example, if something calls Rust standard file open method, then it seems fairly

05:43.200 --> 05:49.120
obvious that we're going to require the file capability, just as LS did.

05:49.120 --> 05:52.920
Getting the call graph and getting that information, fortunately, is quite easy in Rust.

05:52.920 --> 05:55.360
Rust uses L-L-VM under the hood.

05:55.400 --> 06:00.480
L-L-VM has a very well specified, even though it changes every version, intermediate

06:00.480 --> 06:06.760
representation that you can get in text or binary form, and Rust wraps this in this Rust

06:06.760 --> 06:10.920
flag that you can basically use to say, hey, please generate the bit code or the intermediate

06:10.920 --> 06:14.680
representation, and then you can obviously do analysis on that.

06:14.680 --> 06:19.560
So in this case, we're generating IR bit code.

06:19.560 --> 06:23.400
This is a binary format that can then be passed by various things.

06:23.400 --> 06:27.440
In the Rust, in the Rust ecosystem, we have a couple of options for this.

06:27.440 --> 06:32.440
Craig Disilkern has written a crate called L-VM IR, which, and then on top of that, has

06:32.440 --> 06:38.400
another crate called L-VM IR analysis, which does various analysis passes on that.

06:38.400 --> 06:41.160
That's what we're going to use for the things you're going to see.

06:41.160 --> 06:44.840
There's also another project called Inquill, which I actually think is much more interesting

06:44.840 --> 06:48.360
and probably cooler, but it's not quite as complete.

06:48.360 --> 06:56.080
So I would have had to have implemented more bits into the L-VM API, and if you can possibly

06:56.080 --> 07:00.560
get other people to write your L-VM API abstractions, I highly recommend it rather than

07:00.560 --> 07:02.760
doing it yourself.

07:02.760 --> 07:05.800
So, great, we have a call graph.

07:05.800 --> 07:08.120
How deep can we go?

07:08.120 --> 07:12.200
In Rust, the answer is that the boundary is basically the standard library.

07:12.200 --> 07:17.760
The way Rust generally works until David Wood's build standard project is accepted and

07:17.760 --> 07:23.720
stabilized is Rust chips with binary static libraries for the standard library alok call,

07:23.720 --> 07:27.560
which, together, make the standard library.

07:27.560 --> 07:31.880
So which means basically, we don't necessarily have visibility all the way down to this

07:31.880 --> 07:33.840
is where the Cisco happens, right?

07:33.840 --> 07:39.160
We only know to the point that standard FS file open is called.

07:39.160 --> 07:46.400
Now, go is actually similar in this regard, the GoCapsLot tool also has the same thing.

07:46.400 --> 07:48.640
Then go, this was relatively trivial.

07:48.640 --> 07:54.120
Go standard library has lots of batteries, but the surface area of the Go standard library

07:54.120 --> 07:57.680
is actually reasonably tractable.

07:57.680 --> 08:02.880
The GoCapsLot source includes this file which I've extracted from, which is a list of

08:02.880 --> 08:05.400
810 functions in their capabilities.

08:05.400 --> 08:09.400
The Go standard library does have more than 910 functions, but this turns out to basically

08:09.400 --> 08:11.480
cover all the interesting bits.

08:11.480 --> 08:13.960
And the vast majority of those are actually defined as safe.

08:13.960 --> 08:17.160
So there's one at the top that's called safe, but there's actually, I've omitted about

08:17.160 --> 08:19.640
700 that are just marked as safe.

08:19.640 --> 08:27.200
So they can't have an effect outside of whatever the main thing they're trying to do is.

08:27.200 --> 08:30.440
Rust is a bit tricky.

08:30.440 --> 08:33.800
A conservative estimate on how many functions are contained within the Rust standard

08:33.800 --> 08:39.000
library would be between 10 and 11,000 as of the last version.

08:39.000 --> 08:45.120
This is largely because of Rust rate system, because every type of implement a trait

08:45.120 --> 08:50.360
basically have an M times N thing going on where every type in the standard library

08:50.360 --> 08:55.680
that's saying implement clone, which or D bar, which the vast majority of types do, each

08:55.680 --> 08:58.680
has an implementation and that each turns into a function.

08:58.680 --> 09:03.400
Oh, drop implementations are the other big things, almost everything requires drop.

09:03.400 --> 09:08.040
So for the purposes of the experiment, because I did not have several months to actually

09:08.040 --> 09:13.760
spend just annotating Rust standard library functions, I used the runtime analyzer that I

09:13.760 --> 09:19.200
mentioned a few minutes ago and ran the Rust standard library test suite repeatedly under

09:19.200 --> 09:20.200
that.

09:20.200 --> 09:23.520
And then just looked at what the terminal nodes were, looked at what this calls were made and

09:23.520 --> 09:27.400
attempted to make reasonable guesses as to how those mapped.

09:27.400 --> 09:28.640
This is sufficient for a prototype.

09:28.640 --> 09:32.040
This is absolutely insufficient for a production quality system.

09:32.040 --> 09:35.360
So this is one of the compromises in the caveats right here.

09:35.400 --> 09:40.640
I expect eventually an actual squishy human brain is going to have to go through everything

09:40.640 --> 09:46.400
if and when this becomes, if and when this becomes a full-blown production thing, maybe

09:46.400 --> 09:50.720
I guess, but anyway, some combination of them.

09:50.720 --> 09:54.200
So as I said, I just ran through the runtime thing.

09:54.200 --> 09:59.320
We got this is again just very heavily exerted, but you can kind of see file functions,

09:59.320 --> 10:03.440
require file capabilities, network functions, require network capabilities, you get the general

10:03.440 --> 10:07.520
idea and you can even see, I realize I'm going out of range, you can even kind of see

10:07.520 --> 10:09.960
like the Cisco that we're actually invoked.

10:09.960 --> 10:15.560
File open invokes the open app, Cisco, shocking.

10:15.560 --> 10:20.160
So there is another problem known as well, which code doesn't have to the same extent,

10:20.160 --> 10:25.120
which is, it's really easy to invoke a Cisco in Rust without actually going through

10:25.120 --> 10:26.960
the standard library.

10:26.960 --> 10:30.440
You can just link to Lipsy and just call whatever you like there, right?

10:30.440 --> 10:34.680
You can write straight up in line assembly and just make a Cisco up.

10:34.680 --> 10:38.720
You can call the Cisco function that's helpfully exposed by Lipsy.

10:38.720 --> 10:41.440
You can call, you know, in this case, break.

10:41.440 --> 10:43.600
So you get the general idea.

10:43.600 --> 10:47.880
Now we could also extend that annotation out to include Lipsy functions, but there's only

10:47.880 --> 10:49.440
so far we can go, right?

10:49.440 --> 10:54.200
Like, if somebody is literally calling Lipsy Cisco with a variable for the op code,

10:54.200 --> 11:00.560
I please don't do that, and we can't really track what happens at that point.

11:00.560 --> 11:05.640
That said, practically, I don't think this is a huge issue because I think of those

11:05.640 --> 11:09.920
use cases I mentioned up front for the baseline case that doesn't really matter.

11:09.920 --> 11:13.800
You measure it at the start of your project or as early as you can and you say, these

11:13.800 --> 11:15.880
capabilities are required.

11:15.880 --> 11:20.520
If in future, you know, if in later versions, when you run your tests, you know, fails

11:20.520 --> 11:25.960
because it's expecting it requires a capability that wasn't declared, you can go investigate

11:25.960 --> 11:26.960
that.

11:26.960 --> 11:27.960
That's actually fine.

11:27.960 --> 11:28.960
It's not that hard.

11:28.960 --> 11:34.720
So although this is a theoretical problem and I'm sure it will be a problem in some

11:34.720 --> 11:39.920
cases, it's just, it's more in the, you just have to know about a category rather than

11:39.920 --> 11:42.680
this breaks the entire thing category.

11:42.680 --> 11:47.880
All right, so given that, what is a capability?

11:47.880 --> 11:54.120
So capabilities are one of those fuzzy weird words that can mean different things to

11:54.120 --> 11:55.720
different people.

11:55.720 --> 11:59.360
If you, I was going to pull my phone out but I'm going to drop the microphone attachment

11:59.360 --> 12:00.360
if I do that.

12:00.360 --> 12:04.280
You know, I have an Android phone when you install stuff, it tells you these permissions

12:04.280 --> 12:05.280
are required.

12:05.280 --> 12:08.400
Congratulations, those are capabilities.

12:08.400 --> 12:13.440
We have just seen very, very coarse grain files, network, etc capabilities, congratulations,

12:13.440 --> 12:16.200
those are capabilities.

12:16.200 --> 12:21.680
What exactly capability is is awkward and difficult to define and I'm not going to attempt

12:21.680 --> 12:22.680
to.

12:22.680 --> 12:27.520
What we've done here, because we're building on top of an existing project, I've just taken

12:27.520 --> 12:31.520
for the time being the go-set of capabilities that would find a couple of years ago by

12:31.520 --> 12:34.280
the team at Google who were working on this.

12:34.280 --> 12:35.840
Not all of the merit appropriate.

12:35.840 --> 12:40.200
One of the capabilities is literally called CGO, which means basically that a CGO call

12:40.200 --> 12:44.840
is being made in a native code, obviously not applicable to Rust, this doesn't matter

12:44.840 --> 12:45.840
for us.

12:45.840 --> 12:50.840
I think the current set to be honest with you is too cool, I think we're going to have

12:50.840 --> 12:51.840
to be a bit better.

12:51.840 --> 12:56.920
One obvious problem right now is that there's no real way to track, are you opening a file

12:56.920 --> 13:03.080
in, you know, say, the working directory versus not, but, you know, for the time being,

13:03.080 --> 13:05.120
we kind of have what we have for the prototype.

13:05.120 --> 13:07.280
So, there's 14 of them.

13:07.280 --> 13:10.320
I'm not going to show you the more these are the only interesting ones for the rest

13:10.320 --> 13:11.320
of this talk.

13:12.280 --> 13:18.040
All right, so this is the point where the ergonomics of this set up start becoming a little

13:18.040 --> 13:22.760
bit of a problem because I'm going to have to actually see that screen a little bit.

13:22.760 --> 13:25.040
We are going to see how we go here.

13:25.040 --> 13:28.440
I do actually have static slides showing everything, this is more fun if I actually type

13:28.440 --> 13:30.520
stuff and it breaks and you'll get to laugh at me.

13:30.520 --> 13:33.880
So, we're going to see how this goes.

13:33.880 --> 13:41.280
All right, so I have written in Rust the worst JSON format in history.

13:42.240 --> 13:45.600
Source main.rs.

13:45.600 --> 13:49.280
We're using third-day JSON create so I haven't actually implemented any logic

13:49.280 --> 13:54.240
lots of it because that seems really hard and we're just going to read in something

13:54.240 --> 13:58.080
from standing in a JSON document, hopefully, and then we're going to write it back

13:58.080 --> 14:00.480
out with third-day JSON's pretty mode.

14:00.480 --> 14:05.000
So, that seems fairly straightforward, I would hope.

14:05.000 --> 14:11.240
All right, let's do some static analysis and we're going to see where we get

14:11.720 --> 14:14.920
and we're also going to see how accurately I can type.

14:14.920 --> 14:20.440
So, that cap stop JSON thing is the file that you sort of few slides go where it just says,

14:20.440 --> 14:23.240
this standard library function maps to this capability.

14:23.240 --> 14:25.000
So, we're going to do that.

14:25.000 --> 14:28.520
We are going to get some JSON out the other end so we're going to pretty print it

14:28.520 --> 14:29.560
and we're just going to see what we get.

14:29.560 --> 14:32.600
I really hope it matches what I have in my notes because if it doesn't, this is going

14:32.600 --> 14:35.400
to get real awkward real fast.

14:35.400 --> 14:36.680
It does, lovely.

14:36.680 --> 14:41.160
So, the path is empty because we're doing static analysis and it was

14:41.160 --> 14:41.880
too hard.

14:41.880 --> 14:46.600
Wait, because we have to analyze a bit co-falf every single thing that's included,

14:46.600 --> 14:48.520
it's just wanting to disappear.

14:48.520 --> 14:50.680
Hopefully, you know, you generate it off.

14:50.680 --> 14:54.280
Capabilities require read system state and then there's just stuff.

14:54.280 --> 14:58.760
Basically, there's function by function detail on kind of what was actually called,

14:58.760 --> 15:01.160
where it's been called from, where it's declared.

15:01.160 --> 15:05.320
There is actually a full call graph at the end of this, which you can see there

15:05.320 --> 15:07.800
call a call e-location, etc.

15:07.800 --> 15:11.000
Not actually useful for the capability analysis, but probably useful

15:11.000 --> 15:14.120
for other things like finding bugs.

15:14.120 --> 15:16.920
Why do we require read system state for this?

15:16.920 --> 15:21.480
And the answer, I'm just going to strap spoil it, is that one of the limitations

15:21.480 --> 15:23.480
is static analysis.

15:23.480 --> 15:27.160
L of M and Rust try pretty hard to do dead code elimination.

15:27.160 --> 15:29.640
You know, if you don't need it, you don't have it in your binary.

15:29.640 --> 15:32.840
This is not an exact science.

15:32.840 --> 15:36.200
This includes, because indeed, by mode, a whole bunch of D bug printing functionality,

15:36.200 --> 15:39.480
it turns out at some point this is low-cal specific, which means you have to

15:39.480 --> 15:41.720
go find out what the current low-cal is, which means you have to look at

15:41.720 --> 15:45.320
environment variables, and that's a system state array.

15:45.320 --> 15:49.000
So we do require read system state, but we don't require anything else.

15:49.000 --> 15:51.720
We don't need a file capability, because it's all coming in on standard,

15:51.720 --> 15:53.400
and going to standard out.

15:53.400 --> 15:55.720
So at least that makes sense.

15:57.080 --> 16:00.120
Just for fun, let's actually do the runtime version as well.

16:02.520 --> 16:08.920
Firstly, here is a not very pretty input file, as you can see, that is JSON.

16:09.720 --> 16:11.160
And then we're going to run this.

16:11.160 --> 16:15.080
Actually, let me just make sure it builds its perfect

16:15.080 --> 16:20.680
kind of caps lock runtime, target the root time.

16:20.680 --> 16:22.120
That's helpful, isn't it?

16:22.120 --> 16:25.240
I have a framework, and the N key is just starting to go a little bit,

16:25.240 --> 16:26.680
and this is not at all annoying.

16:26.680 --> 16:33.800
Um, debug, certifier, hopefully.json, and same thing.

16:33.800 --> 16:36.200
We get some stuff out the other end.

16:36.200 --> 16:37.480
Cool, so we ran it.

16:37.560 --> 16:39.160
We also actually have the output of the thing.

16:39.160 --> 16:40.840
So it made it pretty.

16:40.840 --> 16:42.120
That's lovely.

16:42.120 --> 16:46.200
But more importantly, we don't require any capabilities, because in practice,

16:46.200 --> 16:48.520
that debug path never actually got caught.

16:48.520 --> 16:49.560
That debug code path.

16:49.560 --> 16:52.840
So we never have to go find out what a locale is.

16:52.840 --> 16:54.440
Understand locales.

16:54.440 --> 16:57.560
Anybody who has stared at the units of lib c, locale handling,

16:57.560 --> 16:59.160
has seen madness, and I'm sorry.

16:59.160 --> 17:01.160
Um, so that's good.

17:01.160 --> 17:02.120
Didn't require anything.

17:02.120 --> 17:03.720
That's actually fairly straightforward.

17:03.800 --> 17:13.960
All right, so we are now going to go back up there, and I'm going to talk about one

17:13.960 --> 17:16.680
that I'm not going to do any analysis for live.

17:16.680 --> 17:19.560
Um, and the reason for that is it takes several minutes on my laptop,

17:19.560 --> 17:22.200
where it's plugged in and in full power mode,

17:22.200 --> 17:25.720
and God, I don't even know how long it will take if I try and do it while it's on a battery.

17:25.720 --> 17:30.120
So what I've done here is I've done analysis of the crate.io service.

17:30.120 --> 17:33.400
I'm also a member of the crate.io team in the Rust project.

17:33.400 --> 17:38.680
Uh, crate.io has unsurprisingly a back-end with a rest API and open API specs and stuff.

17:38.680 --> 17:43.080
Um, so I took the API server and ran it through the static analyzer.

17:43.080 --> 17:47.240
Um, the output, because it includes the full call graph or the function information,

17:47.240 --> 17:51.080
location information, is about four gigs of JSON uncompressed.

17:51.080 --> 17:54.360
Um, it's a reasonably decent size service.

17:55.400 --> 17:59.080
But if we look at the top level capabilities, four of them look really obvious,

17:59.160 --> 18:02.600
files, unsurprisingly has to be able to read configurations and stuff like that.

18:02.600 --> 18:05.480
It has to be able to write to stores network.

18:05.480 --> 18:07.880
Well, we'd like to respond to HTTP requests.

18:07.880 --> 18:12.200
So, probably good, uh, read and modify system state has to be able to read.

18:12.200 --> 18:14.440
Environment variables has to be a set environment variable.

18:14.440 --> 18:18.120
So, can spawn other things to do help a task like rendering readmees, no problem.

18:18.840 --> 18:22.120
Um, arbitrary execution, interesting.

18:23.000 --> 18:27.320
So, this is where having all that function level information actually really helps,

18:27.400 --> 18:31.000
because we can eventually go down and find out what's actually going on there.

18:31.800 --> 18:36.440
And the answer, as it turns out, is we pull in a crate called file time.

18:36.440 --> 18:39.080
I think it's a transitive dependency. I don't think it's a direct one.

18:39.080 --> 18:46.760
Um, that eventually on Gnulibc makes a direct sis call to the U times in fact, uh,

18:46.760 --> 18:52.680
uh, sis call because the wrapper in G libc is buggy and can't be changed now because of backward

18:52.760 --> 18:59.720
compatibility reasons. So, that is technically arbitrary execution because it's literally calling

18:59.720 --> 19:05.480
libc sis call. So, this is again, the thing I mentioned earlier, where it's like, okay,

19:06.280 --> 19:10.600
when now we know this, knowledge is power. Now, it turns out,

19:10.600 --> 19:15.800
U time NS at is actually part of the, the sis calls that are allowed by the file capability anyway.

19:15.800 --> 19:19.960
So, in practice, this doesn't really matter. But the point is, there was a weird thing,

19:19.960 --> 19:25.800
we got to investigate it now we know. Um, it's also likely that, you know, we can,

19:25.800 --> 19:30.040
we can definitely extend this analyzer to be able to take more of this, look at the constants

19:30.040 --> 19:34.600
of the going into the sis call and so on. So, I, I think that's a fairly obvious next step here.

19:36.200 --> 19:40.840
So, I wrote a small tool called caps lock set comp, which is currently on a branch of that

19:40.840 --> 19:45.320
of that cargo caps lock repo that I am going to merge back in at some point when I have some sleep

19:45.400 --> 19:51.640
in the next couple of days. Um, the basically just takes that JSON that you just saw and turns it into

19:51.640 --> 19:59.240
this JSON, it's JSON all the way down, um, which is a set comp JSON profile and this is accepted

19:59.240 --> 20:05.400
by basically every containerization system, Kubernetes, whatever. Um, as a way of basically saying

20:06.440 --> 20:09.640
these are the sis calls that you should allow, these are the sis calls you shouldn't allow.

20:09.640 --> 20:14.600
Or more accurately, really just what you should do when you see specific sis calls. So, the default

20:14.680 --> 20:19.240
action here is we're going to error, um, we're going to just straight up not allow the sis call,

20:19.240 --> 20:22.840
we're going to return an arbitrary number, which is useful because you can go find that new journal,

20:22.840 --> 20:27.240
or demessage, or whatever. Um, and then we've got, you know, a list of sis calls that will actually

20:27.240 --> 20:31.240
accept and most of these are pretty unexciting, you know, you can kind of see this stuff that's

20:31.240 --> 20:37.000
file related that's network related and so on and so forth. Those are all allowed, we'll just scroll to

20:37.000 --> 20:42.040
the bottom where it actually says the action to be taken is that you allow them and then there's another

20:42.040 --> 20:46.840
set which is a much smaller set which are things run C actually requires because the set comp gets

20:46.840 --> 20:51.960
gets enforced from the point where you start the container. Um, so if you don't allow these then,

20:51.960 --> 20:57.960
you know, it doesn't work. Uh, ask me how I found this out. Really what you want to know,

20:57.960 --> 21:03.960
really what you want to see when you're trying to write a demo. Um, so, with that file,

21:03.960 --> 21:08.520
I have created a Docker compose, you can do some Kubernetes as well, but it's a lot easier to show

21:08.680 --> 21:15.240
a Docker compose file compared to set of Kubernetes manifests. Um, and the only interesting bit here

21:15.240 --> 21:21.800
basically is that we're enforcing a security option which pulls in that JSON and applies it to the container.

21:21.800 --> 21:26.360
So, say, you do some Kubernetes, you can do this with podman, you can just want it, whatever.

21:27.320 --> 21:32.760
All right, so, um, I am not going to actually start this in front of you because, uh, the

21:32.760 --> 21:37.160
crates that I have development server for the front end rebuilds the JavaScript every time and it

21:37.160 --> 21:41.880
takes about 30 seconds and I have about 30 seconds and I still have more things I want to say. So,

21:42.760 --> 21:47.720
we're just going to have to take my word for this actually running in Docker, but I would help

21:47.720 --> 21:52.040
if I change focus to the right thing. If you're at the front, you'll be able to see that this actually

21:52.040 --> 21:57.240
is running on local host 4200. I promise, you know, across my heart that I have not just set up a

21:57.320 --> 22:04.600
proxy to crates.io. Um, but yeah, you know, we have a crates.io instance here. I grabbed a recent database

22:04.600 --> 22:09.880
dump, so these numbers are slightly out of date, but close enough. Um, and, you know, we can go,

22:09.880 --> 22:14.840
look at crates and make API calls and it works, and you can't view the read me because,

22:14.840 --> 22:19.000
don't ask basically. Nothing to do with set comp. This is just a limitation of running the

22:19.000 --> 22:25.720
development server. We can even probably make a search. So, the API server works. That requires the

22:25.800 --> 22:30.680
API server. It is running the limited set of CIS calls that were analyzed, that were statically

22:30.680 --> 22:35.800
analyzed out of the binary when it was built, and it still works. It didn't require anything

22:35.800 --> 22:41.160
that caused it to fail. So, I'm very pleased about that, and I'm looking forward to deploying that

22:41.160 --> 22:48.680
in the next couple of weeks. All right, back to full screen. So, I've touched on most of the next

22:48.680 --> 22:53.160
steps at various points during the talk, but here they are in one convenient list. As I said,

22:53.160 --> 22:59.480
the work that, um, Sergey and myself have been funded to do is more or less than an end. Like,

22:59.480 --> 23:03.720
this is basically the end point. It works to the extent of the static analysis works. We can

23:03.720 --> 23:08.920
analyze a call graph. We can generate a call graph, and we can annotate things, things with capabilities.

23:09.640 --> 23:14.040
Things I would like to do next would be, we've got to finish the specification of the language

23:14.040 --> 23:18.920
independent JSON format because it's way too low, specific right now. Um, we have to annotate the

23:18.920 --> 23:23.240
rest-standard library with all 11,000 functions. By the time we actually do this, it'll probably be

23:23.240 --> 23:29.560
12 or 13,000. We have to make codecats a lot production ready. It is quite possible right now to just

23:29.560 --> 23:35.080
look at it funny, and it will fall over. Um, one significant limitation is that the static analyzer

23:35.080 --> 23:40.680
only worked up to rust 1.86 because the underlying LLVM IR creates only support LLVM 19.

23:41.080 --> 23:49.640
Um, I have started to port them to LLVM 21, and again, LLVM API, not your friend. Um, and the fourth one

23:49.640 --> 23:54.360
is, you know, build more tooling that actually consume these, like that very simple script to just take

23:54.360 --> 23:59.160
the JSON, the other JSON, things that were actually things that would actually be more helpful in

23:59.160 --> 24:03.800
real world infrastructure environments, like, you know, actually enforcing these, actually building this

24:03.800 --> 24:09.480
more into real deployment pipelines. But it's been a fun project. It's pretty exciting, where

24:09.560 --> 24:13.800
obviously, it's a pretty early stage, but I am looking forward to hopefully developing this further

24:13.800 --> 24:19.240
this year, and hopefully getting some help on this, too, from other people, and looking forward

24:19.240 --> 24:23.080
to having something that is going to be useful for people moving forward. Thank you for your attention.

24:29.320 --> 24:33.080
I promised I'd be between 20 and 22 minutes, and I ran two minutes over. I'm very sorry,

24:33.080 --> 24:36.920
so I think I probably only have time for one or two questions. I see two questions if you

24:36.920 --> 24:39.800
can get a mile of your answers. So, all right, I'll try to be fast.

24:47.960 --> 24:54.120
So, one fundamental difference between Go and Rust is that Rust links to the C-standard library,

24:54.120 --> 24:59.640
and there's a risk if you have that the C-s because it's dynamic linking, you can exchange the

24:59.640 --> 25:06.360
library, and also about the step two, annotating, basically on Rust side, you only see the calls to

25:06.360 --> 25:10.840
Chilipsy, maybe, or muscle. How do you deal with that, and how do you automate this, and

25:10.840 --> 25:17.160
is something like already code-based available from Chilipsy, or muscle, and what it's called,

25:17.160 --> 25:23.160
they use, because the problem exists for C-code as well. Okay, so I'll take the last part first,

25:23.160 --> 25:27.400
and I'll work my way back through the question. Well, I'm wine-stack. I don't believe anything

25:27.400 --> 25:31.960
is currently published by those projects. I'm like, this call makes this, this, this, this is

25:32.680 --> 25:35.640
if I'm wrong, I'd love to be wrong, because that would save me a whole bunch of work.

25:37.000 --> 25:41.400
In terms of how you ensure that you're not getting substituted out, you actually touched on one of my

25:41.400 --> 25:45.000
answers for this, which is muscle, because then you're going to be statically linking, and that

25:45.000 --> 25:49.320
makes it significantly harder, hopefully you know it's statically linking against. In the Rust case,

25:49.320 --> 25:54.680
the tool chain version actually shifts with muscle, so we know I got shipped. If somebody is at the

25:54.680 --> 25:59.240
point of doing a build system attack where they're replacing the muscle that's in the Rust tool chain,

25:59.240 --> 26:03.960
then that's probably good luck to them at that point. In terms of dealing with G-Lib C,

26:03.960 --> 26:07.960
if you've got, you know, LD preload and things like that, there's a lot of this is really,

26:07.960 --> 26:12.600
I think this out of scope for this is more about hardening your build and deployment pipelines

26:12.600 --> 26:17.000
more than this sort of thing, but I'm definitely open to ideas on that, so it's a good question.

26:17.000 --> 26:23.800
It's definitely, it's definitely a problem. We are out of the fan, so thank you for the question,

26:23.800 --> 26:27.880
thank you for your presentation. Thank you ever.

