CapExec: Towards Transparently-Sandboxed Services (Extended Version)
Mahya Soleimani Jadidi Mariusz Zaborski Brian Kidney Jonathan Anderson






: Towards Transparently-Sandboxed Services (Extended Version)
    
  
    Mahya Soleimani Jadidi1,
    Mariusz Zaborski2,
    Brian Kidney1,
    Jonathan Anderson1
  
  

  
    1
    Department of Electrical and Computer Engineering 

    Memorial University of Newfoundland

    {msoleimanija,brian.kidney,jonathan.anderson}@mun.ca
  
  

  
    2
    Research and Development 

    Fudo Security Inc.

    {oshogbo@FreeBSD.org}
  

    December 30, 2023
====================================================================================================================================================================================================================================================================================================================================================================================





Network services are among the riskiest programs executed by production systems.
Such services execute large quantities of complex code and process data from arbitrary — and untrusted — network sources, often with high levels of system privilege.
It is desirable to confine system services to a least-privileged environment so that the potential damage from a malicious attacker can be limited, but existing mechanisms for sandboxing services require invasive and system-specific code changes and are insufficient to confine broad classes of network services.

Rather than sandboxing one service at a time, we propose that the best place to add sandboxing to network services is in the service manager that starts those services.
As a first step towards this vision, we propose , a process supervisor that can execute a single service within a sandbox based on a service declaration file in which, required resources whose limited access to are supported by Caper services, are specified. 
Using the Capsicum compartmentalization framework and its Casper service framework,  provides robust application sandboxing without requiring any modifications to the application itself.
We believe that this is a first step towards ubiquitous sandboxing of network services without the costs of virtualization.




application security, sandboxing, service manager, Capsicum, compartmentalization




§ INTRODUCTION


Network services and applications have always been attractive targets for remote attackers.
Network services typically incorporate complex protocol parsing code, often written in low-level languages, that is exposed to arbitrary content from the network.
Since these services commonly execute with system privilege, they are at high risk for remote exploitation as a gateway to other system resources.
Because of the risk and the consequence of potential compromises, there is a need to confine network applications and limit the damage that can be inflicted by a successful attack.

One broad class of techniques that seem applicable to the problem of securing network services is sandboxing: restricting software's access to system resources such that the application has the least privilege required to fulfill its function.
However, applying such limitations is challenging.
Many sandboxing frameworks require invasive code modifications, some of which require a great deal of security expertise to apply correctly. 
Incorrectly applied mechanisms may lead to a false sense of security without additional effectual protection.
What is needed, in addition to the development of effective sandboxing techniques, is the development of tools to apply those techniques.

Sandboxing is applicable through different techniques at various levels within operating systems such as system call chroot(2)<cit.>, sandboxing features in service managers such as systemd<cit.>, or application containers such as jail(8)<cit.> or docker<cit.>. We believe that a great deal of benefit can be derived from the application of sandboxing at the level of system services without requiring invasive modifications to the application code of every network service. Hence, we see service managers as the key to securing systems with network-facing services. Securing systems at this level, eliminates the need for securing every service separately.

In this paper, we have combined process supervision and FreeBSD's capability-oriented compartmentalization framework<cit.>, Capsicum <cit.>, to introduce  as a sandboxing process supervisor (<ref>), which runs applications in a restricted capability mode.

Capsicum provides a fine-grained sandboxing mechanism, giving developers a significant degree of control within an application.
However, some developers still avoid the use of this technique, or even other sandboxing approaches, for the following reasons:

    
  * Difficulties of preserving the functionality of the program while enhancing its security
    
  * Requirement for source code modifications and the additional associated testing
    
  * Verifying the correctness and compatibility between limits defined in the sandbox


These issues motivated us to facilitate the use of Capsicum by employing the same ideas in our supervisor program.

 uses Capsicum to restrict the privileges of services, limiting access to resources from compromised applications; it uses the Capsicum-based Casper service framework <cit.> to provide access to required resources through capability channels defined by Casper.
To use  , services' required run-time resources should be described in service declaration files, as an initial step to unify security and functionality descriptions.
When a service's run-time requirements can be described in terms of Casper services,  provides sandboxing without any modifications to source code. 
The mechanism and its various evaluation results are described in  <ref>  and <ref> respectively.
This project is a significant advance beyond the current state of the art (<ref>).


	



§ : A SANDBOXING SERVICE SUPERVISOR



We have designed and developed  to be a security-focused service supervisor that executes a service in a sandbox transparently, which means no modification to the service's source code is required.

 creates sandboxes relying on Capsicum<cit.> and Casper services.
Capsicum is a sandboxing framework developed for FreeBSD. The fundamental idea for the framework comes from the concept of capabilities in capability-based systems <cit.>. Capabilities are unforgeable tokens of authority carried by processes to authorize access to the system’s resources. Using Capsicum’s API, once a process enters to the capability mode, all system calls trying to access global namespaces, such as the root filesystem or the PID[Process ID] namespace, will fail.
Capsicum’s sandboxing has changed the regular flow of the system call mechanism in FreeBSD. In addition to Capsicum’s general API, Casper has extended Capsicum’s features by defining capability channels to allow access to some riskier but widely-used services<cit.>.

Using Capsicum and Casper,  creates and loads required Casper services to provide access to white-listed run-time resources. These services and their limits are specified in the application's declaration files as the service configuration.  executes one application at the time in a sandbox made with required Casper's capability channels. 
Hence, limited accesses to the subsetted namespaces are proxied for the application, but any unprivileged behaviour or requests beyond defined limits will fail due to capabilities violations.




 §.§ Service Declaration: Unifying Application Functionality with Security Requirements



 employes libucl<cit.> to parse the service declaration file conforming to JSON format<cit.>. The file should describes the service's binary and its requirements.
Requirements are those system resources that are disallowed in capability mode, but their alternative restricted services are supported by Casper daemon or libcasper API<cit.>. 
An example configuration for the traceroute utility is given in Listing <ref>.

[language=csh, caption=An example of the content of traceroute declaration file, label=traceroute-service-declaration]

        binary: "/usr/sbin/traceroute"
        "system.fileargs" : 
                operations: "OPEN",
                flags: "RDONLY",
                cap_rights: "READ",
                cap_rights: "FCNTL",
                cap_rights: "FSTAT",
                filename: "/etc/protocols",
                filename: "/dev/null"
        
        "system.dns" : 
                family: AF_INET
        
        "system.net" : 
                host: "example.com",
                family: AF_INET
        
        "system.sysctl" : 
                "vm.overcommit": 
                        type: "mib",
                        flag: "CAP_SYSCTL_READ"
                
        



Writing service declarations requires knowledge of system calls used within the applications code. The problem is complicated by system calls used indirectly by libraries.
Recognizing this issue, we developed CapCheck, a tool for highlighting library calls that use system calls not allowed in a Capsicum sandbox. As can be seen in figure <ref>, even a call to fgets(1) can result in unexpected system calls that are disallowed in Capsicum sandboxes.

CapCheck uses readelf and ldd to determine the calls made to external libraries and the libraries that provide them.
It then builds a full call graph for the application and searches it to find paths that result in disallowed system calls.
This information is provided to the end user to develop service declaration files.





 §.§ Sandboxed Execution



As the first step,  parses declaration files and creates the corresponding Casper services. In addition to supporting all Casper services,  needed a capability channel for simple network communications. Therefore, we developed an experimental networking Casper service, system.net, supporting bind(2) and connect(2). After parsing service declaration files,  forks and executes the binary sandboxed in a child process, creating a new environment or context for the process, including arguments, libraries, environment variables, the new runtime linker, shared memory mappings and required libraries to make the process sandboxed.
 replaces disallowed libc system calls with other functions that redirect requests to the existing Casper services.
As a result, the source code remains unmodified.
Figure <ref> shows this mechanism.





§   IN PRACTICE



A useful approach to achieve compartmentalization in large networks is to employ communication and access policies on subnets.
There are always applications such as firewalls and similar tools to enforce policies in a coarse-grained form of actions being held on the edge of the system.
 is a solution to have finer-grained enforced policies on services rather than users behaviors.
If administrators can define detailed policies for important system services, then we will have network services that might be compromised, but they will not cause information leakages over the network, privilege escalation on servers or other consequence server failures.
's dependency on configuration files causes administrators to be able to run sandboxed network services using simple network management tools such as running startup or shut down scripts on specific domains. Limits can be applied to the network more manageable, centralized, and more verifiable.

 turns application sandboxing from a complicated and time-consuming procedure into one that is simpler and faster to implement.
To examine this set of tools we sandbox two of well-known existing utilities, cat(1) and traceroute(8), on FreeBSD 12.0.
In this section, we describe 's internal mechanism.



 §.§ What happens to the process?



Using , the administrator or developer of the system can describe required resources in terms of Casper's services similar to the example that has been shown in the listing <ref>.
Suppose that listing <ref> is a piece of code for our process.

[language=C++, caption=Software that depends on functionality disallowed by Capsicum, label=local-system-sample]
void  test_gethostbyname() 
  const char *ipstr = "127.0.0.1";
  struct in_addr ip;
  struct hostent *hp;
  if(!inet_aton(ipstr,    ip))
    fprintf(stdout, "Unable to parse IP address 

  hp = gethostbyaddr((const void*)   ip, sizeof(ip), AF_INET);
  CPPUNIT_ASSERT(hp != NULL);


In the absence of , the process calls gethostbyaddr(3) function and the corresponding libc function will be called as shown in figure <ref>.



When a process enters the capability mode, all system calls which try to access the global namespaces will fail.
So the corresponding system functionality will not be met.
The Casper daemons help solve this problem by creating restricted capability channels to access system resources with limited privileges.
To use Casper services in , the service configuration file should contain the following content for our example:

[language=csh, caption=An example of a service declaration for system.dns  ]

	"system.dns": 
		type: ADDR,
		family: AF_INET
	



When  parses the file, it defines a capability channel for system.dns service with specified limits and sets required libraries to be preloaded later.
Preloaded libraries and their functions retrieve capability channels that are open in the parent process, which is the main thread of .
In our example,  a capability channel will be established for the service system.dns.
Then calling gethostbyaddr(3) will be redirected to the channel with Casper's equivalent function cap_gethostbyaddr(3).
The procedure is shown in figure <ref>.

 

As it is mentioned before, to test our application functionality on elementary utilities, we tried to sandbox cat(1) and traceroute(8).
cat(1) is a very small utility which needs only one Casper service, system.fileargs, to open files specified as command line arguments.
Listing <ref> shows cat's service declaration file that we used to make it sandboxed.

[language=csh, caption=An example of a service declaration to sandbox cat(1) , label=cat.svc ]

	binary: "/bin/cat"
	"system.fileargs": 
		operations: "OPEN",
		flags: "O_RDONLY",
		cap_rights: "READ",
		filename:  "test.txt"
	





§ EVALUATION AND COMPARISONS





  §.§.§ Runtime Performance

In this section, we describe evaluation results and our observations about runtime performances, memory usage, correctness, and comparison of running existing applications, cat(1) and traceroute(8), in 's sandbox and other sandboxing technologies. 
We have chosen these two utilities as they are both simple small utilities sandboxed before using Capsicum.
Examining these application allows us to have a direct comparison to traditional applications of Capsicum requiring source code modification. 

To investigate running cat(1) with , which only needs one Casper service (system.fileargs), we examined cat(1) and its sandboxed version with two test scenarios. 
In the first scenario, we examined invocations of cat with files of various sizes from 1 MB to 1 GB, shown in figure <ref>.
Since a large portion of  overhead is spent pre-opening and holding handles to file descriptors, in the second test scenario, we ran our tests with a varied number of empty files as the input set, from 10 to 10000, shown in figure <ref>. All measurements were performed ten times per test case.





As can be seen in figure <ref>, executing cat in our sandbox adds overhead to the execution time. 
We find this delay more tolerable as the number of inputs grows. 
The majority of the cost of using  is spent in setting up the sandbox.
This includes the reading of configuration files, the creating of Casper services and fork(2) calls to load and open them.  

Here is how we explain various points affected execution time with the observed latency.  parses a service declaration file and then defines essential Casper services with their specified limits.
Opening Casper services is always associated with calling fork(2) for each service.
After all service definitions, CapExec forks and executes the binary, in the child process, in capability mode. 
Hence, the cost of establishing a sandbox environment and Casper channels always exist, but just once during runtime, which is negligible for large inputs, in terms of size. It shows 1% on average in our cases for huge files, and 1% to 2% for small cases.
However, this step costs more for a large number of files with equal sizes in our second test scenario. In worst cases of those tests, it takes over twice of the time. We can explain this issue with this fact that there is a linear relationship between the number of mentioned services and their features in service declaration file and the time required for configuration.

There are additional reasons for the delays seen in our outputs.
The presence of preloaded libraries redirected system calls and the communication between the sandboxed process and Casper daemon, all affect process runtime. The increasing number of Casper services opened by the supervisor program causes more latency during runtime.

Although we believe these delays are acceptable to achieve secure services, there are various places in  which can be optimized.  For example, with better sets of service declaration options, such as Casper fileargs arguments, we could speed the first phase of sandbox creation.  has been developed with the most straightforward possible design as a proof of concept but additional optimization on the current system is one of our current focuses.




's approach includes the additional cost of spawning Casper services and 's preloaded libraries. In addition to the structures keeping services configurations and limits, new processes have to be spawned by the supervisor program. We can see the impact of this design in figure <ref>, which shows a broader impact on memory overhead.




  §.§.§ Completeness and Correctness


We have investigated the correctness of our sandboxed application through ktrace(1) output, which provides us detailed information about invoked system calls and their returned values. For example, in the listing <ref> which is presenting the ktrace(8) output of the unmodified version of cat(1) program,  we can see the disallowed system call, open(2), which is forbidden in the capability mode. 

[language=csh, caption=The trace of the syscall for the cat(1) application., label=ktracecat]
cat    CALL  openat(AT_FDCWD,0x80024a010,0<O_RDONLY>)
cat    NAMI  "test/0"
cat    RET   openat 3


In Capsicum, when an application tries to access a global namespace, a capability error is returned, and the intended system call fails. Listing <ref> is demonstrating the capability error returned from the system call open(2) in capability mode.

[language=csh, caption=The trace of the syscall for the sandboxed application., label=ktracecapcat]
cat    CALL  cap_enter
cat    RET   cap_enter 0
cat    CALL  openat(AT_FDCWD,0x7fffffffe990,0<O_RDONLY>)
cat    CAP   restricted VFS lookup
cat    RET   openat -1 errno 94 Not permitted in capability mode


CapExec delegates tasks to Casper services for disallowed replaced system calls. Requests are sent to Casper services through a capability channel which passes commands through UNIX domain sockets to Casper services. 
We can see this procedure in the ktrace(8) output shown in the listing <ref>.
The system call sendto(2), instead of the forbidden system call, is invoked. We can see that the final open(1) system call was executed by a different process.
Since in our examined applications, no system call failed and no related capability error was observed and they worked normally, we can infer that the corresponding configuration file sufficed for the applications.

[language=csh, caption=The trace of the syscall for cat(1) under the CapExec., label=ktracecapexeccat]
cat	CALL  sendto(0x4,0x80163b0a0,0x4e,0,0,0)
cat	GIO   fd 4 wrote 78 bytes
       0x0000 6c00 0200 0000 0000 0000 003b 0000 0000 0000 0004 0400 0500  |l..........;............|
       0x0018 0000 0000 0000 0000 0000 0000 0000 636d 6400 6f70 656e 0004  |..............cmd.open..|
       0x0030 0500 0700 0000 0000 0000 0000 0000 0000 0000 6e61 6d65 0074  |..................name.t|
       0x0048 6573 742f 3000                                               |est/0.|

cat	RET   sendto 78/0x4e
[...]
capexec   CALL  openat(AT_FDCWD,0x800283078,0<O_RDONLY>)
capexec   NAMI  "test/0"




  §.§.§ Comparisons with Virtualization-based Solutions


We have also compared  with equivalent virtualization-based solutions. There are various aspects to compare these systems such as required time to set up the sandboxed or virtualized environment, the complexity of configurations, required storage for each approach, memory utilization for each tool and finally the latency of running an application using each tool. To investigate  against most widely-used containers, we examined running traceroute(8) under five situations. First, we ran traceroute(8) on the native system out of any container or . For the rest of the cases, we started the container, ran traceroute(8) on it and then stopped it. We practiced this procedure on a virtual machine, a FreeBSD jail, a docker, and finally with . Figure <ref> shows the latency and memory used of running traceroute(8) using each technology, giving an average of five test runs. It also shows the time spent to start each container up and to stop them after the test.
Since  runs directly on the native operating system, we can see that the memory overhead is negligible. 



There are also additional storage requirements for the investigated isolation techniques. As an example, the size of the image for a virtual machine, a docker image, and a jail image in our tests, were 2.6 GB, 204 MB, and 800 MB respectively, which shows that how much isolating a small application might cost. This issue makes a light user-level sandboxing application like  distinguished from other sandboxing solutions.

Regardless of numerical comparisons, we can compare the mentioned solutions in terms of some other non-statistical criteria. Here are some of the most significant aspects. 
Setting a virtual machine, a jail(8) or a docker(8) container require considerable time. 
The time to learn, configure, and run the first instance of these containers, depends on the underlying operating system and the user's proficiency.
For the various secured environment, one should define, create, and install various images to be virtualized later. 
In most cases, there are also post configurations to make the environment efficient to run the intended application. For example, to run network services under any of these applications, one might do many networking and firewall configurations to make the service and its response reachable through network. 
This issue holds for  as well but in a tremendously lightened way. We believe the time for setting up a sandbox and running a user-level application, is much less than creating and configuring each of those containers. 
To run an application under , one should first find all disallowed system call invoked by it, and this stage could be complicated for unprofessional users. However, we tried to ease this step with CapCheck. 

As the last significant point, all of the described solutions above need the root privilege to run. While we are trying to create and run a sandbox under an unprivileged application. On the other hand, they are still probable to get compromised.  As an example, a virtual machine is itself breakable, so in that case, the network service running in it will be out of access. To avoid breaches in the future, a solution is to combine services running in virtualized containers with other services running out of them; however, this will add more complexity and effort to the problem.




§ RELATED WORK


To design 's scheme, we have studied several sandboxing mechanisms and service managers, focusing on their security options. 
As the most important options, user permissions and privilege are essential   for most of contemporary init systems. 
Configuring uid and gid in systemd<cit.>, s6<cit.>  or other service managers, are examples of these options.
However, they are still susceptible to complex attacks such as those utilizing privilege escalation.
 

launchd<cit.>, the service manager on macOS, was the first system that expanded inetd's<cit.> socket activation, and was adopted because of its performance.
launchd provides options for security that are mostly focused on permission features such as username, groupname, initgroup and umask.
The only option concerning sandboxing schemes is the ability to set the root directory. The short list of security options in launchd originated from the internal security scheme of macOS, which is consistent and well-designed.

systemd<cit.> is another widely-used service manager in which various security options are supported. In contrast with launchd,
systemd supports very fine-grained security controls such as uid/gid control and isolation options such as inaccessible or read-only paths, root, and tmp directories.
Benefiting from seccomp<cit.>, system call filtering is also provided.
However, with all of these features, it is left to the developer to verify the configuration's compatibility with the use of the application but it is easy to define inappropriate policies.

There are also other service managers with similar security mechanisms such as relaunchd, also known as jobd<cit.>, nosh<cit.>, and s6<cit.>, which are quite different in design. 
For example, relaunchd runs services in jails <cit.>, and its options are configurable for the user, while most of nosh's security features are internal. Also, nosh uses the concept of capabilities in design. nosh’s design and mechanism is based on daemontools which is a package of tools for UNIX service management<cit.>. 
There are also other service management tools inspired by deamontool such as runit<cit.> and s6 that are much more than an init system. s6 is a package of tools including various security features such as access control management on client connections, supporting uid-less privileges, ability to define sudo family <cit.>, etc. Most of deamontool-based service managers benefit from internal security in their design.

In addition to service managers, we also have studied widely-used existing sandboxing tools such as  chroot(2) as a sandboxing system call, FreeBSD's jail and docker that were examined as containers in section <ref>, seccomp(2)<cit.> as a framework that applies system call filtering, CloudABI<cit.> which provides process-level sandboxing benefiting from capabilities, etc. An interesting point about all of these applications is the different level at which each  of them are providing sandboxing.

 
















































§ FUTURE WORK



 is a service supervisor that executes one application at a time.
That application executes in isolation, a sandbox provided by Capsicum with limited access to global resources mediated by Casper.
However,  is not a complete service manager.
Our intent is to use  as a foundation for a service manager
that handles interdependencies and sandboxes groups of network services.
To aid in this goal, we are also developing more network-oriented Casper services.
Additionally, we are investigating ways to reduce the amount of security specific knowledge required to use . We aim to make defining security policy as simple as specifying white-listed resources required by services, so that  decides which Casper services, with what configuration, should start. 



§ CONCLUSION


In this paper, we introduced , a prototype sandboxing supervisor that facilitates service sandboxing both on local and network services.
Using Capsicum and Casper, along with a simple configuration file, we transparently provide this isolation at run time without any modification on the application's source code.
The system requires to be configured based on essential Casper services, which is challenging for the user, but to facilitate this procedure, we provide CapCheck, a tool to discover system calls that require wrapping.
This demonstrates that sandboxing itself can be a service, a key foundation for building security-aware service managers in the future.

bib/IEEEbibtran



