← All talks

Sandboxing in Linux with Zero Lines of Code

BSides Liverpool24:0415 viewsPublished 2022-01Watch on YouTube ↗
Speakers
Tags
About this talk
Ignat Korchagin explores seccomp, a Linux kernel technology for restricting system calls without modifying application code. The talk covers seccomp basics, low-level policy definition, the libseccomp library, and practical sandboxing approaches via systemd and custom tooling—enabling operators to enforce security policies on third-party and proprietary binaries.
Show transcript [en]

hi uh thanks for coming back do you hear me well louder hi my name is ignat and today we're going to talk about sandboxing in linux with no code that's a little bit about myself i work on linux things at welfare i'm very passionate about security and crypto and i enjoy low level programming kernel code bootloaders and other scary stuff but today we're going to talk about uh sandboxing segbot sandboxing with a technology called systeccomp in linux but to understand seccomp we first so what are system calls nowadays we don't write applications of real hardware we usually use some kind of operating system and our which is like a middleman man like application and the hardware and

applications interact with the kernel using some kind of api so these apis are called system applications read then write data to this send data over network using always using some system square right so a system system call is a well-defined interface between the application and the kernel and it provides some low-level abstractions and services to applications file networking time etc and it also the kernel enforces different access control policies to different resources from applications as well as just some resource management so what is second now second is the linux technology and it's just another system called linux but it is special in a way because it's provide a way for an application to notify the kernel of its intention or

other system call which it intends to use for this normal function and then like it's like a contract between an application and the kernel and then the kernel enforces this contract provided by the application yeah so basically second is not a sandbox itself as linux developers like to say cycle is a tool to create symbols so yeah the second you can just the application can infer its intended usage to the kernel and kernel then take actions when this contract is published for example this application may say that it will only use this scale like it will store like files locally but it will never basically send any data over the network and then the linux can enforce that

policy for that application so basically why it's useful uh let's see an example imagine you're a developer and you're writing a simple like clock application so what a clock application mean from the product um so probably it only needs to know time right so basically in your second policy you can tell the kernel hey in the clock application i will only use the system called get time of day and when your application functions normally at some point you need no time it requests time from the kernel it's part of your contract it's part of your promise to the kernel the kernel will allow but imagine you wrote your application in the low-level language which is very unsafe and

somebody got like exploited your application involved arbitrary code what the attacker can try to do they try to steal data from your uh from your computer and for example then they try to send this data over the network to themselves and they will divert your plot application to use the send system called that but that is not part of your original contract right so the kernel immediately will kill the application and then prevent the data so yeah this is like a simple an example of just some simple symbols policy with however there are some challenges for how do we use second in the application so anyone doesn't know a unix rename comma where you type your name and you get

like your type the operating system so in this example we will write a simple you name like application which will use uh you name system code there is also a system call called yeah well this is the full application source code the important parts here is just we ask the kernel to provide some information and we prove that information to the user so let's see how it works you can just profile this application execute it and it will tell you that your operating system is left so let's try to sandbox this application and we'll just modify it at modified by adding another function called sandbox and we will implement this function in this way now

i know it looks scary but the gist here is we define our second policy in this assembly-like language and then basically the gist of the like for the purposes of this exercise we will define at that point so we know our application needs a

right so in this case we basically all this program it allows every system call for the application except the dna and what we tell them the policy instead of just filling the application we'll talk if the application tries to use your name system call return on error code and we will use the funky error called e-net down like network is down like why would we need network force yeah and then we just provide this policy so let's try to execute it now and yeah if we execute it we see that the application exit was error and the error code is networked so we are now hitting this error pass here and we definitely know we're

hitting it because of our second policy because under normal uh scenario like you nation memory from that down system [Music] but as we just see like the problem with satcom tools like this low-level assembly language it's very hard to write hard to manage hard to update and like we see it was the code was huge for a simple application imagine building world applications and we also need to be aware of different low level details we need to know there is system called numbers architecture because so that's why like the community came up with a simplified library called leafset call we can rewrite our sandboxing function with the library now we see that the code is much smaller

and simpler and there are like three important pieces here we define our default policy we allow all system calls then we explicitly add a rule saying like if you try to use your name system call this case in this case we'll change the policy to be more strict and tell the kernel just to kill the application yeah and yeah and then we'll learn the policy so let's see how it works and when we compile this application you need to remember that we also need to link to the library because we now use the library and create this application we'll see this like message from from the shell that the application was killed [Music] but again there is a problem right so

in all these scenarios i have to explicitly modify our application to add sandboxes right so how do you think like how many developers start a project with this mindset so like most likely i will of course i will write bugs and vulnerabilities so the first thing i should do in my application is like provide like proper sun bosses right

and security is always like somewhere in there and yeah and this is the problem the second that second applies the rules policy only to the current process so it is expected that application developers will add support for sandboxing to their application but on the other hand we have system in assay who operate code in production and they want applications to descend but they don't have any control over this right and also like in big environments you probably use some third party proprietor applications which you don't even have access to the software but you would like to unbox it somehow so there is a gap right so one side we have application developers you can send

their code but don't have the incentive and on the other hand we have like operators system and slv security professionals who want to assemble but don't have that this is where no code second comes to mind so how can you add second support to an application without touching it and this is like system b our favorite process manager i'll try to do that so if you run your you application through this system called filter directive in your unit files and uh basically you can provide an allow list or a deny list of system calls this service or application should be using so you basically can convey the second policy to the terminal for the application management system

there so let's try that let's basically recompile our original non-sandbox version of our toy tool and we run we can run it with systemd right we see that it runs fine we see the expected output and we see the exit successful now we can actually set a sandbox of the system b so we'll add this directly and like prohibit this application to use the unique system call and we'll see that it will be killed by the system right with the signal we can also like emulate the policy uh of returning custom error codes so there is another directive

arrow however there is a small print with this the system via pro if you dig into the documentation some system calls are always allowed and this is because the way how systemd works it always needs to allow the system properly and one of the scary parts here is the exact resistance which is always allowed even if you put it into the denial so why is it bad right let's go back to our clock application when an attacker basically exploits our application against arbitrary code execution what is the first

[Music] so if your application doesn't mean that systems calling its policy it's very good to prohibit it that's why we designed our own sandboxing tool so it's a toolkit to inject custom second policy rules into almost any process [Music] it kind of like works on top of the approach of system d but takes it one step further you can blow up any systems also and the toolkit actually consists of two components sharing library for dynamically linked applications and you can block any system calls and it works in any binary in any programming language even proprietary environment and it's open and you may say hey shared library you said it's low code like why don't we have to have a shared

library so it's a shared library but it's special in a way that you don't have to program against you just need to use it in your process so again we will come back to our original uh you name like simple tool and with with our toolkit we can actually just uh provide custom cycle policy provided in a denial list with

[Applause] and we will immediately see that the applications [Applause] [Music] yeah so two things here we just reload the library for the two people source code notification and we provide our desired component environment and nobody likes a weak reward because it doesn't always work in all cases for example what if you just want to hatch the application once and for all

again we don't need to modify the source code we are actually compiling this direct and then all we need to do is just to provide the second policy into the environment variable and yeah what about stack

toolkit is a launcher for starting to rebuild application instead of just launching the applications directly we just launched it through our launcher here and again with the same environment variable we can provide this customer policy to any application so yeah cloud for example configuration by environment variables the preferred ways to use an allow list everything is denied except

and also there is another feature called default action which you can switch from basically just very strict action for the kernel to kill your application if you finalize the second policy to just load the action which is very useful for proprietary requirements we don't know which system called users so you can run the binary with the logging and actually see what it what it needs for normalization yeah and the sandbox tool contains a few components there is a lip sandbox library for dynamically applications and you need to provide this library to process address space or patch the alpha finally directly and there is a sandbox supply to a launcher which can be used also statically in design economic

applications and you just need to run run your objectives through the launch but at this point you may ask also why do we need like if the launcher works for dynamically linked applications statically application why do we need the rewarding library why we can't always use like the launcher for any kind of product and to understand that basically the difference let's from like one thousand people review any code startup of the operating system so when you launch an executable usually there is some kind of a one-time unique say like every language every framework has some kind of a runtime in each stage including

this is where your main function starts correctly right so one time next stage is visible to the developer it's like added by the compiler application yeah and in terms of sandboxing if you use the launcher the second course will be enforced before the run timing stage and if you use the uh preloaded libraries the central policy will be enforced after that happens what is runtime basically a lot of system calls which are never used afterwards in the application so there is like memories attack and maps and protects etc and your main code might not need them in the future so it would be nice to actually block them when your service operates and production and basically applying the second policy

a little bit later allows you to have a tighter second policy data so yeah in this case we only need to allow system calls uh from the main logic and in this case we'll also need to allow system calls for the long time so again let's see an example of our application so now let's try instead of a denialist approach we switch to allow what is the minimum number of syscalls we need to allow for our applications to run successfully and in this case like if we use the dynamic library approach we only need to allow four system calls for the application to function if we switch to the launcher mode the same application requires us to allow

13 system calls to function so it's four versus circuit if you have a dynamically linked interface with a dynamic library you can have a much tighter policy for your application indeed yeah i think that's it basically i feel some useful links if you didn't understand

[Music] um yeah thank you very much [Music] questions

questions yes please um don't say the ability to require exist so what i was thinking was that if you write our own software sure we'll know what's his clustering most of the time it will be especially third-party stuff but we don't know what it does being able to just trace all the assist calls by using weld parting and saying this

yeah let me find this slide yeah this is what this default action is about yeah so if you want to run in this like discovery mode so what we need to do is you can you create a allow list with no system calls so you deny everything but you also switch this default action to log and then you can monitor the system log or if you use audi it will be in the audit logs that an application violated the second policy and which system called it then you can pass this load and actually identify which system call is being used by this proprietary

[Music]

[Music] yeah so containers also use staccomp as well so like many container systems like docker kubernetes they have some kind of a default save system called policy it's quite wide but you can actually for most of them you can specify your own custom cycle balls and you can like shrink it down still the fact that basically but in this case it's the same as with runtime in each stage right so what i'm trying to say is when now people don't build very slimmed down containers with only one binary they're still kind of fat you have some basic file systems and stuff so you have this fancy runtime lead stage right so i think the best approach is to have

actually both because second policies in the kernel they are stacked so you can sandbox yourself but then you can like shrink it down later if you need to so for example when you your container application starts you have a wider second policy defining the container runtime for your application to actually allow it to start go through all the batch commands and print you need to actually start the service and then once your main code is started you can use this tool to further like narrow that like the attack surface by narrowing down the allow system

so