 Hello, everyone. My name is Miguel Sena. I work for Microsoft. And so this talk is about script execution tool. A work I started working on a few years ago with my first employer, the French Silvestri Agency, which is still going on. Initial scripts are very useful to do a lot of stuff. And they're also useful to attackers. They can be used for a lot of things. And sometimes it might not be worth it to develop a complex exploit, but just to execute, well, upload and execute a script. So along with a series of vulnerabilities, take issues, let's say, for scripts. Initial, well, for instance, well, you cannot trust the environment, the environment variables, and so on. So it might be a risk to execute interested scripts. Initial, the issue is summarized here. There is two ways to execute scripts, either to ask the kernel to execute a path. So it's the first way here, dot slash script dot s h. And the other way is to ask the script interpreter to execute the script. From the user point of view, it's the same. But from the kernel point of view, it is not the same. The first one goes through an executive VUC scroll. And the kernel can, well, execute the scripts, can kind of the same way, it could execute an elf binary. And in a second way, the kernel only executes the interpreter. And then the interpreter opens the file, the script, and interprets the script. So the main difference from the kernel point of view, and especially for all access control systems in the kernel, is that the first way to execute a script is OK because, again, sees everything and knows the intent of the user. And the other way, the second one, the kernel doesn't know what is going on. It just knows that the user want to execute the s h binary. And then this s h binary wants to open a file in a read-only way. So what we like to do here is to let the kernel, and with the kernel to let everything which is behind, so every access control types, including doc, mark, integrity systems, and everything that we can think of to be able to allow or deny such a script execution. OK, the Sved model is, well, first prerequisites. Well, if you want to have a secure system, well, you need to have an access control system in place, enforced, and well configured. But as I explained earlier, well, articles can easily upload scripts as it can do with any binaries and so on. Constraining binary execution is possible. Constraining script execution is not always possible. But here, we want to address this part. So from global point of view, if you want to add a secure system, you may want to enforce execution permissions, execution permissions on the memory, on executable binaries, and so on scripts. So here, we want to address the third point. We want to give the users, the sysadmin, the Linux distros the ability to control script execution, not to deny everything, but to be able to enforce an access control system on that. The same way we can do on everything else. But for this to be, well, practical to work in real life, we need to be able to configure it. Because, well, kernel script interpreters don't ask the canal for such permission. So just open a file, a script, read it, interpret it, and kind of execute it in the own environment, the own process. Here, we want to be able to give the tools to sysadmin to configure in an incremental way the system to be able to control script execution. Because to give you some example, if you're using a new distro, there's a lot of stuff going on, a lot of scripts, and probably multiple script interpreters, like Python, Shell, Dash, Bash, and so on. And well, with one update, you might not have all these script interpreters patched at the same time. So you need to be able to hold these updates and configure your system bit after bit. Another thing to keep in mind is that not all scripts are equals. There's multiple kind of scripts. Scripts is mainly something which is executable from the user point of view, but not from the canal point of view. So this might include, well, for instance, Python scripts, which are really powerful. They can do whatever they want. There's a lot of libraries. There's also Shell scripts, which might be a bit more restricted because it's not designed to do same things. For instance, they cannot directly call CS calls, which might not be a good thing from the attacker point of view. So Python is a good attacker tool, for instance. And we're going to also think about other kind of scripts like JavaScript and WebAssembly, whatever you can find in your web browsers. But these are most of the time already well sandboxed. So different policies and different risks. The first proposal, which was sent some years ago, is called Omega Exec. So the design for this patch series was to enhance the open CS call. First, to give you a bit of background, this was used in a really custom and tailored security distro called ClipOS. And the main policy of this distro was to enforce write the execute policy. So either on the kernel side, on the memory side, on the executable side, on the Linux, well, the main points, partitions, and also the scripts exception. So the idea was to add a new way to open a file. So you can add, for instance, a append, or read only, or write, and so on. So options to pass to the open CS call to open a file in different ways. With this new Omega Exec flag, the idea was to tell the kernel, well, I am a scripting operator, and I want to bend this script to execute it. So the kernel will then go through, well, kind of a common access check on this file. But it will also take care to check that this file was in no exec or executable mount points. So it is interesting because it is really, well, it is not too difficult to configure a system with correct no exec mount options. So it's easy to have, for instance, a slash user with an executable point, but the slash home and slash var as no exec point. And this way, it was kind of the first way to configure this security policy. Internally, using the Omega Exec flag will set the f-mod exec flag, which is an internal flag, which is propagated to other security system in the kernel. And so I implemented a way to configure this from a system point of view, thanks to a CCTL. So two flags for the CCTL, the first one to enforce the no exec, well, investment, or to handle the five-parmesan check. So without any configuration, by default, it will not change anything. So it was a flag that would not do anything if it wasn't configured, which is, in fact, what we want to have. Because if you, well, push this kind of update to generate this row, well, it could break. And that's not a good thing. So by default, it should not do anything. But the idea was to let the sysadmin to configure that and kind of harden their own installation. So at first, for instance, to handle the mounts no execution. And at second time, to make sure that all the scripts have the executable permission, which might not be the case. So these configurations went from using Yama and then creating a new exec LSM. And then finally putting it in the core can. The idea is, well, this should be kind of in mainline, enable, well, possibly enable by default for everyone. It's a long term. So it should not be part of an LSM. Pros and cons. Well, this new flag, the good thing is it's configurable, easily configurable. It can handle, well, notification with the FAN open exec flag, for instance. So you can use FAN notify to get notification of something which is a script, which is being requested to be executed. There's no resignation by design because you open a file. And at the same time, the check is performed. And some things that might be a bit weird at first, that this flag could match either a file, but also directories. And that was kind of an easy or lazy way, at first, to handle specific interpreters to make sure that a whole directory could be executable. So in this case, it was used, well, to patch IST, so Java video machine. The cons. Well, one of the advantage and cons is that open modes, well, this kind of flag, adding a new one, it's not a mode, it's an option, actually, but easy note. So if you use new open option on a whole kernel or a new kernel, we accept that. Accept that in new kernel, it could, in this case, have a different behavior. So that was kind of a good thing and bad thing. The good thing is, well, you don't want to change what already works, but the bad thing is, well, you don't know if it will be enforced or not. So that was considered not a good thing. And there was no way to check already open files. So that might be an issue too. Second proposal was, instead of focus on the open Cisco, focus on the access family of Cisco's. So we added a new AT interpreted flag this time. The same way there is an AT access flag, which is a way to kind of tweak the access check, which is performed with the access Cisco, access at Cisco, or F access at to Cisco. So the AT interpreted flag was kind of transformed to an x-OK mode, which is to check if a file can be executed. So this was kind of hack. So not very good, but still good ATs. Well, we were using an x-ink Cisco, which is, in general, good. But yeah, too much issues. And also maybe a bit too much focus on LSM issues, or LSM problems. The third proposal was to implement a new Cisco. It was kind of made more generic to be able to handle more than only script execution. So the name was chosen after a different backshedding. To name it, trust it for, the idea was to, for use space, to be able to ask the candle, can I trust this file descriptor for this use case? Kind of generic, maybe too much. And the first use case was, can I trust this file descriptor, the content of this file descriptor to be executed? Yeah, so you took five descriptors, good things. And at first, you're able to ask if file descriptor should be executed or not, according to the global security policy. So new standard SQL, quite simple. Flexible, but it's a bit too flexible, and not enough, with not enough specification. And could only handle file descriptors. So that might be an issue for some use cases. We'll see that later. Yeah, something might be not clear enough. It is kind of a different point of view. And it was told that the access kind of families was, in fact, maybe the best solution. So here I come with a new proposal. It is not cemented yet. I started working on it, but there's still some issues. And most importantly, I would like to get some feedback from this audience. So any Kail manors, SM manors, Linux users, system manors, and so on. So the idea is to have two things. First one, exhibit OK, which is a new mode. So when you call the f-axis up to syscall, for instance, or only the access syscall, you either pass f-OK to ask if the file exists, error OK to ask if the file is readable, w-OK if it is writable, or x-OK if it is exitable. Here, the idea is to add a new mode, which is exhibit OK. So quite close to the x-OK mode. But that's not what it's saying. It's kind of a superset of the x-OK mode. The main differences are it's set. So it is the same as x-OK. But it also adds what's set in the penalty f-mode exec, which can be then forwarded to other access control systems, such as LSMs. And it can also be used by the notification system to know that something is about to be executed. And the idea is to improve that by passing three existing flags to LSMs, which already pass when you're executing a binary. So with the exit v-syscall, the different LSM gets these flags and can then take enlightened actions according to safety or not of this potential execution. So as you can see, the idea with exit v-OK is not to only check access control system, to not only check the access control, the permission from the current process to do an execution of a specific file, but to do that also with the context of the current process and to kind of forward more metadata to the current. And yeah, in the case of script execution, well, most of the time you write to use also the R-OK mode, because well, you want to read the content of script to be able to execute it. What about LSMs? Well, it's a different kind of LSM, mainly for access control or for integrity, which is kind of an access control, but not really. So the idea is for them to be able to get the same or almost the same information as when something is about to be executed for a wheel. So they will get the F1 exec. And also, as we just talked about, the LSM and save flags. And they could also be able to differentiate between an exec v native call and such an access call. With the may access flag, which is an internal kind of flag, pass through all the access layers of the kernel. But there's still some commentary changes. Well, once you change your space, you need to go bit by bit. Well, you need to modify script interpreters. It's kind of already done, well, in some ways, for Python, but only for Windows. But the framework is kind of ready. And other interpreters, other script languages would like to be part of this too. Once you have one or two script interpreters updated and ready to transfer this information to ask the kernel for execution, you also need to have, well, you need this to take these script interpreters and also make them compatible with the scripts provided by the distro. And you also need to have the system in to be sure that and date and installation of the system will perform well with the specific scripts that he could put on the system or that developer or user can use for their own use cases. So we cannot do just, we cannot just add a switch on and off. But the idea here is to make it a bit more flexible than the previous proposals and to still rely on a CCTL. But to make the CCTL paramount namespace, the idea is, well, when you do moon points, you do them kind of paramount namespace. So I think it makes sense to be able to configure a namespace to add more restrictions. For instance, you would like to say, you could say, well, for this container, you want to run either no script, well, no entrusted script. So no script coming from a TMP directory, working directory, and so on, but only from read-only sections provided by the container itself, the container image. So that could work. But at the same time, you would work on your script as a developer. So well, on your main namespace or your work namespace, you will not have these kind of restrictions because, well, you would like on your home directory to have script and use them. So JD is, as a first proposal, to be able to reinforce or not either the mount no exact option or end the file permissions. So do we need to have these scripts to have the execution permission? But the difference with this approach, with the previous approaches and this one, is that it is kind of split it in two. For this compatibility to be taken in account, the idea is to add a new access to flags, not mode, but flags. The same way, we can add the at 80 access flags that change the semantic of checking execution, read, or write permissions. So with this flag, it will be possible to rely on the CCTL configuration per namespace instead of, well, enforcing everything at once. So same configuration here. But, yeah, so the idea is for generic script interpreters to use this flag because they will not, well, such developers cannot know where the script interpreters will be used. But if you're developing a Linux tail or a Linux distro for an appliance or something like that, well, this kind of flag would not be required because you'll be able to control everything. Quick list of present guns. With this approach, with the access c-scroll approach, you can either handle five descriptors or path. Paths might be a good or bad thing. Well, one use case that was explained is that you might think about your shell to be able to check the content of the pass variable to know if this file or another file can be executed. So, for instance, if you have a script in your pass environment, well, your shell might check with a name, not a file descriptor, if your script could be part of the completion, for instance. But most of the case, you want to use file descriptors because it is much more safer. And, well, it avoids race conditions, mainly time check and time use race conditions. This approach, I think, is well-defined. I think the most we can do. We can still rely on paramount configuration with the mount no exact option. There's no implicit use of other mode. It is a new dedicated mode, which is kind of a superset of the XOK mode. And this kind of check can be used by LSM to enforce the same restriction as it do for any execution. But also, to log such a terms, for example, for instance, if you're trying to execute something, but you don't want to execute it really, the LSM, like AC Linux does, could log such a term with a specific flag indicating that it is not a real execution, but it is an attempt to execute something. And according to the color, it could know, well, it is a script interpreter, so it might be legitimate or not, and log such a term. So some remaining questions, well, there's questions just after that. But one issue might be, but it is kind of the same for every new interface, is that, well, you can and might not support execv, OK, of course. But it is really easy to check for that. If you set the mode and the Cisco return e and val, well, it is not supported, so you script interpreters can just ignore that. And with the access family Cisco, well, you can use pass too, which might be risky, but it's still possible. So some open questions. First, the name, is it OK? Is it OK? Is it OK to have such a name? I don't like it very much, but I didn't find something better to express such semantic. So any suggestion if you have, feel free to chat. Other questions might be about such ID binaries. So what should happen if either the script, the set ready permission, which is not a good thing, which should not be a good thing and should be noted anyway. And what should happen if the script interpreters is running as set ready, which is also not a good idea, but might happen? And we also need to check and be sure that using such f-mode exec will not trigger unexpected canal behavior. Because it is kind of a new way for use space to set this flag, which wasn't directly available before, only with the regular exec vc score. And yeah, finally, well, we cannot do the exact same checks and enforce the, well, to have the exact same guarantees than when you're executing something for real. Because when you're executing something for real, the current process can be locked. And this will just last for a few milliseconds, I guess. But when the script interpreters do such check, or want to do such check on a file descriptor, you cannot lock anything, really. So that's a limitation. I'll go back to this slide if there's some ideas. Some lesson learned, nothing really new here. But still, well, we should encourage everyone to use file descriptors. But there is some legitimate use cases to not use them. Defining clear goal is really important, especially when you're defining new interface and incoming interface. So it might be good to be flexible enough, but not to be unspecified enough. And yeah, if you want to extend something, that's good. But there's a balance to find between extending or adding a new interface, adding a new Cisco for instance. Well, to wrap up, be able to control script execution is really important. It is not done, really, nowadays, but it should. And one thing to keep in mind is that we don't want to just block script execution. We want to be able to control stuff, like we do with other stuff. And yeah, we'll need some script interpreters changes, but light ones. So that should not be too much of an issue. But one thing to keep in mind is that if you want to have a robust script interpreter, for instance, a restricted bash, well, there's a lot of things to keep in mind, especially to not load any modules, Python modules, for instance, to, most of the time, ignore the path variable, to ignore the LDP load variable, and so on. So you might find some similarities with saturated binaries protections. Next step, well, it's to submit this 19th version for script proposal and get reviews. And well, for future works, there's some ideas to extend these execution restrictions to the kernel, but that will be in the talk for tomorrow. Thank you. You talked a little bit about Python. Are you thinking that the Python interpreter would set this bit on all imported modules, or you're thinking no Python modules? Yeah, so there's already an implementation in the Python interpreter, which was designed for Windows, because they're the same, definitely the same use case for Windows, and, well, Windows has these nice targets for attackers, because it's for end users. There are two kind of same version for PowerShell, for the same ID, same use case behind. For this kind of script interpreters, either you want your user to execute whatever he wants, because he's a developer, a sysadmin that knows what he's doing, I hope so. And in the case of regular users, which are not developers, no sysadmin, no, should execute a script, yes, you should restrict the input that the script interpreter takes, the module that the script editor can load with arguments, dash m, and so on, and you should restrict a lot of things, and yeah, that's implemented in Python, it was wild since I took a look, but yes. Okay, and since you asked for feedback, I think it's ugly to set a new flag via access n. Yeah, well, yeah, just to add something, if you want to enforce that, of course, it should be a build configuration, so you might have two script interpreters, one which is restricted for every user, and then there's one which is only accessible to developers, sysadmin, and so on, but that can be enforced with a regular access control system. Okay, so another question, I think there are some other applications for this other than just executable files, so I think on a system you have configuration files, which can be really critical, and in a way, you could say a configuration file is almost kind of like an executable, it will change the execution behavior of a, also if you have a web service starting up, for example, and I thought, initially, I thought that this would be generalized enough that you could use it for non-executable things, but I'm just wondering if that needs to be taken into consideration if it's gonna be to exec execution focused, and I think there's a possible another application which is reading private keys and utilizing them again, you have these, the behavior can change dramatically based on input, so I just wonder if this is gonna be generic enough or whether that needs to be considered in the naming and the design. Yeah, good question. So that was part of the idea to create a new syscall, trusted for syscall, which was kind of flexible, but maybe a bit too flexible. So yeah, taking into account configuration, configuration is, might be as critical as scripts, but so one thing to keep in mind is that if your script interpreters can take arbitrary configuration, well, it's kind of not your script interpreter anymore, so you should only take confusion from trusted sources, and that can, most of the time, be kind of hard-coded and so works, but in some other cases, it might not be the case. I think it could be possible to extend if access at syscall with other modes, but that will need to be very well-defined, and the issue might be, well, the kernel, it is not only part of the kernel semantic. The kernels really don't know and don't care, but if you're reading a confusion file, if you're reading a sensitive file, if you're reading a picture or whatever, you just care that you're reading something, and the access control system, the current one, or the main ones, only care about reading stuff, not tying, not extending this read permission, splitting this read permission into more granularity, with more semantic, so I think it will not be part, well, it will definitely not be part of these batteries, and it might be worse, creating a new dedicated interface, but that will be changing. Yeah, I think, though, when you have a script interpreter, again, the kernel, from the kernel's point of view, all that's happening is you have a process is opening a file and reading it. It doesn't know that the script interpreter's executing it, so I think that there may be too much semantics already in the kernel around this being for execution, so I'm sort of pushing back towards the trial, it's more like trusted, and let's not get into executable JPEG files. Yeah, so the main pushback from Linus was the semantic needs to be really well defined, and for this to be really well defined needs to be already defined in the kernel, so the idea with a new proposal is to add an interface to expose to user space the kernel semantic, which is already implemented. Yeah, I think the main problem is that we already have the access control for read and write and everything, but the execution, like the access control for execute is hidden in the kernel and has multiple weird side effects that were not, you couldn't query it in any way, whereas everything else, you do a read access check and you'll pass through all the LSMs, like you'll get a real result, but that wasn't true for this. Personally, I'd like to really push back on Linus's suggestion that maybe we should include paths in this because why create a modern interface to something that has, that exposes a time of check, time of use flaw? Like, don't. Yeah, if you're modifying your script interpreter already to do the right thing to check for this, just open the file descriptor first and use it as a file descriptor, and just don't expose something that could get misused. There's no reason to do that. We already have to change user space to use the API, so it should use modern APIs to do these things as opposed to all the race conditions, and that's, I mean, we're still getting hit with these kinds of, and now we swap out the sim link right between the check, so I'd really push back on the path name part of that. For the set UID thing, which is one of the things Linus had said, hey, I don't really care which way this goes, I just want it discussed. He was talking about, well, you could end up doing sort of emulation of execution in user space if you could expose all this stuff, and I don't know if we want like, you know, exec v okay and exec v no suid okay, where you're actually saying, yes, I would like to examine the suid behavior here, or no, I don't care about it, or defining that we'll never go do suid type things, and just declare that interpreters don't have a suid mode anymore, we'll pretend Perl never had suid Perl, and move forward. Yeah, about this set UID stuff, so it's mostly about the no suid mount up. So I think the most safe for word and simple approach would be to just do the same check as a kind of dose when you're executing something, just that and just mix the semantic because it is what we want, but not let the user ask for a specific use case the same way the user doesn't ask for either this passion is in an no exec one point or not, just ask, well, if I do an interview with that, will it work or not? Right, I think the issue is that we might end on a situation where it's not just will it work or not, but will it work and be suid or not? Right now if you try to run, actually run a program that's set UID marked, like the say UID bit is set, but you're in a no suid mount, it just runs without the bit. So if you've got suid Perl trying to do a check, you'll come back with, yeah, you can execute it, it shouldn't be suid though, and then it'll look at the file system and say, oh, I have the bit, so I should do the bit. So I think there needs to be some sort of specific description of if the bits are set and it would be allowed to do this, if we do this, like actually lay it out what the logic should be for that. But personally, I think it should be no suid at all ever. Yeah, so the question might be, if a script interpreter is run as set UID, would that be simpler to just check that this process is running as set UID and then just exit? That could work too. Hi, apologies if you perhaps mentioned this during Trusted 4, suppose I missed it. Have you given any consideration to script interpreters that can take their script via standard in? Because I mean, you could stop somebody with a new flag from running shscript.sh all you want, that person could then just run cat script.sh pipe to sh. Yeah, good question. So that was part and still part of the part series. To, well, yeah, so first I should recheck. So, yeah, so yeah, to summarize, we could call Python with slash dev slash TTY, for instance. Then you could pass stuff inside that. But the question is, is this device executable? Is this file executable? Is this pipe executable? And I think it could be solved quite simply with that. So I would caution you against making this a property of the mount namespace out of concerns about the implications of that in terms of it's being able to escape from that by creating your username space and mount namespaces. Yeah, so in the code I'm writing, the idea is to, when a new namespace is created, it initiates the previous limitations, restrictions, and can only add new ones. But, yeah, that might not be good for everyone, but I think it's much better than a global stem policy that would limit a lot such adoption. So if there's any other idea, I will be interesting to hear. All right, thank you.