Operating System Security Policy Hardening via ... - Springer Link

4 downloads 148 Views 277KB Size Report
Operating System Security Policy Hardening via Capability Dependency Graphs. Zhihui Han, Liang Cheng, Yang Zhang, and Dengguo Feng. Institute of ...
Operating System Security Policy Hardening via Capability Dependency Graphs Zhihui Han, Liang Cheng, Yang Zhang, and Dengguo Feng Institute of Software, Chinese Academy of Sciences, Beijing, China {hanzhihui,chengliang}@tca.iscas.ac.cn

Abstract. An operating system relies heavily on its access control mechanism to defend against various attacks. The complexities of modern access control mechanisms and the scale of possible configurations are often overwhelming to system administrators and software developers. Therefore, misconfigurations are very common and the security consequences are serious. It is very necessary to detect and eliminate these misconfigurations. We propose an automated and systematic approach to address how to correct the misconfigurations based on capability dependency graph generating and MaxSAT solving. Given the attacker’s initial capabilities, we first automatically generate a capability dependency graph to describe attacker’s potential capabilities and the dependency relationships among these capabilities. Based on the capability dependency graph, we then develop a solution to automate the task of hardening operating system security policy against multi-step attacks resulting from misconfigurations. In this solution, we first represent each capability obtained by an attacker as a propositional logic formula of initial conditions, and then transfer the policy hardening problem to a MaxSAT problem. Finally, we present a notation called normal capability loss to aid an administrator to select an optimal hardening solution leading to minimum system usability loss. We apply our approach to analyze misconfigurations in Ubuntu10.04 shipped with SELinux and study an attack case to evaluate the effectiveness of our approach. Keywords: Operating System, Access Control, Security Policy Misconfigurations, Capability Dependency Graph, MaxSAT.

1

Introduction

Access control subsystem is the major mechanism to provide user isolation and protect operating systems against attacks from malicious users. Security policy is the set of access control configurations for security entities, including processes, file system, network ports, and user accounts. Security policy directly determines the security status of an operating system. A vulnerable security policy will make the access control mechanism to exist in name only and lead to serious security consequences. Therefore, it is important to analyze and detect vulnerabilities in security policy configurations. And more important, detected vulnerabilities should be well fixed to make the security policy more secure. c Springer International Publishing Switzerland 2015  J. Lopez and Y. Wu (Eds.): ISPEC 2015, LNCS 9065, pp. 3–17, 2015. DOI: 10.1007/978-3-319-17533-1_1

4

Z. Han et al.

Policy Configurations

CDG Graph to Logic I Graph Generation Propositions

SAT Solver

Suggested Configurations

Fig. 1. Solution Overview

Existing approaches in operating system security policy analysis, such as VulSAN [2], WACCA [1], ACQE [7], can identify all possible attack paths leading to privilege escalation and output them in a graph structure or some patterns. However, while attack graphs or attack patterns reveal the security threats, they do not directly identify the root causes of attack paths and no systematic solutions exist to correct security policy to eliminate the detected attack paths. Since the number of attack paths is huge, it is impossible for an administrator to manually check and correct possible misconfigurations. In this paper, we focus on solving security problems caused by access control misconfigurations and propose a systematic solution to aid administrators or root users in hardening their security policies, shown in Fig. 1. Since a major functionality of access control is to provide user isolation and privilege escalation is the precondition for most attacks, we target at privilege escalation attack. A privilege escalation attack is a multi-step attack, in which each step gains some additional capabilities and prepares for the next. Given the attacker’s initial capabilities, our approach first generates a capability dependency graph (CDG), which describes all possible transitions of capabilities and intuitively illustrates all possible capabilities the attacker can obtain under the constraint of current (problematic) security policy configurations. Then, for each capability in the capability dependency graph, we compute a propositional logic formula, which describes the relationship between the capability and security policy configurations. Finally, we construct a Boolean formula φ in conjunctive form by adding negations of the logic formulas which represent privileged capabilities. The formula φ means disabling all privileged capabilities. And the security policy hardening problem is converted to finding a model of φ. There may be multiple hardening options, and different option penalizes the system with different usability cost. We design an algorithm to generate an optimal solution leading to minimum capability loss. Our algorithm adds some additional constraints to the formula φ to guide a SAT solver to derive a satisfying solution with minimum usability loss. Our hardening solution is easy to enforce because the hardening options only require disabling some independent initial conditions. Our contributions are as follows: – We propose the concept of capability dependency graph to intuitively describe all possible dependency relationships among user’s potential capabilities. A capability dependency graph can reveal threats by enumerating possible alternate sequences of actions and capabilities. – We present a logic-based process to automatically generate a capability dependency graph with specifying some capabilities and a set of atomic actions to obtain these capabilities.

Operating System Security Policy Hardening

5

– We design an algorithm to transfer each node in a capability dependency graph to a propositional logic formula of initial conditions. Based on these logic formulas, we give out a group MaxSAT-based approach to compute a solution leading to minimum capability loss, which hardens the security policy completely while decreasing minimum system usability. – We implement a prototype based on our approach, and use the prototype to study the default configurations of Ubuntu 10.04 shipped with SELinux. The rest of this paper is organized as follows. Section 2 provides the definition of capability dependency graph and presents the automatic process to generate a capability dependency graph. Section 3 states the security policy hardening problem and derives a solution with minimum capability cost based on SAT solving. Section 4 describes the implementation. Section 5 provides an experiment to illustrate the effectiveness and performance of our method. Section 6 is dedicated to related work. Finally, section 7 concludes the paper.

2

Capability Dependency Graph

Capability dependency graphs represent prior knowledge about capabilities and their dependencies. A capability dependency graph describes the pre-conditions required for each action and resulting post-conditions, illustrating all possible means how an attacker compromises the system. The misconfiguration detection and solution are conducted on the graph. 2.1

Definition

Definition 1. A capability dependency graph is a directed graph G = (C0 ∪ Cd , A, E), where C0 is the set of initial vertices which can be divided into initial condition nodes and initial capability nodes, Cd is the set of intermediate vertices indicating the reachable vertices from initial vertices, A is the set of action vertices, E is the set of directed edges, and E ⊆ ((C0 ∪Cd )×A)∪(A×Cd ), (C0 ∪ Cd ) × A is a require relation and A × Cd is an imply relation. There are two types of directed edges that inter-connect different types of vertices. First, an edge can point from an initial or intermediate vertex to an action vertex. Such an edge denotes the require relation, which means the action cannot be executed unless the pre-condition is satisfied. Second, an edge pointing from an action vertex to an intermediate vertex denotes the imply relation, which means executing the action will lead to the post-condition. 2.2

Generating a Capability Dependency Graph

To generate a capability dependency graph, we begin with specifying a set of capabilities that an attacker can get in a system. A capability is essentially a combination of its nature and the objects to which it applies. Since processes and files are mostly exploited to achieve privilege escalation attacks, we consider the following capabilities related to processes and files in our research:

6

Z. Han et al.

1) control process code(proc(Uid, Gid, Domain)). The attacker controls a process with security context proc(Uid, Gid, Domain). This may happen because the attacker has a local account, or because the attacker exploits or launches a program to further control another process. 2) control network input(Port). The attacker can access the system remotely through the port Port. This is an initial capability for a remote attacker. 3) control file data(FileName). The attacker controls the data of a file. In order to achieve attacks, an attacker begins with his initial capabilities, and tries to execute some actions to obtain new capabilities. We specify deriving rules to model attacker’s atomic actions, which describe the transition relationship between different capabilities. Each atomic action takes the format of (pre-conditions, action, new capability), where pre-conditions define the preconditions of action, and they consist of capabilities and security configurations that must be satisfied, and new capability is the capability endowed by the access control mechanism after action is taken. The atomic actions considered in our research are listed in Table 1. Table 1. Atomic Actions Overview Action

Description

WaitExecute(Program)

The attacker waits a program executed in some process to be executed again with the same security context. CompromiseRead(Process, FileName) The attacker writes a file that a process is reading to compromise a process. CompromiseNetwork(Process, Port) The remote attacker sends malicious packets to compromise a network process. HopeExecute(Program) A program is hoped to be executed by privileged users. Execute(Program) The attacker executes a program. WriteFile(FileName) The attacker writes a file.

An important purpose of access control is to prevent against privilege escalation attack even if a vulnerable program is compromised. Therefore, we introduce “what if” analysis to the specifications of atomic actions, we assume that a program might be compromised and exploited to execute arbitrary code. After specifying the atomic actions, we collect the meta configurations of the target operating system, and encode them into the form of logic predicates. In particular, we also need to formalize the access control mechanism of the target operating system with logic programming language, which is presented as some Horn clauses. Finally, based on the configurations, specifications of atomic actions and the formalized access control mechanism, we build a capability dependency graph through a multi-step process as follows: 1) Search the action specifications to find atomic actions that are enabled by current capabilities under the constraint of configurations. 2) If an atomic action is identified in step 1, calculate the new capability defined in the post-condition of this action, and then construct a record in the form of (pre-conditions, action, new capability). If the record has not been generated, log this record. 3) Add the calculated capability to current capabilities, and repeat step 1 and step 2 until no new log record is generated.

Operating System Security Policy Hardening

7

Finally, we can generate a capability dependency graph by adding edges pointing from each pre-condition to action, and from action to new capability. However, there might be cycles in the graph, which introduces infinite loops into further analysis over the graph. Examining the graph, we find that it is the actions Execute and WriteFile that introduce cycles into the graph because these two actions allow a security context to transfer to the same security context, which is a secure transition and make none security threat. In order to avoid cycles, we add some additional restricts to filter such kind of transitions. One important feature of capability dependency graphs is that the require relation is always conjunctive, whereas the imply relation is always disjunctive. More specifically, an action cannot be performed until all of its pre-conditions have been satisfied, whereas a capability is satisfied if any of executed actions implies the capability. However, an action may require different sets of conditions, whence the require relation for this action is disjunctive between these sets of conditions. We handle this case by constructing a separate vertex for each variant of the action so that the require relation for each variation is still strictly conjunctive. In particular, actions with different pre-conditions and post-conditions are always labeled as distinct vertices. In the graph, vertices representing privileged capabilities are called goal nodes, such as control a process of root user. Attack paths from initial capabilities to goal nodes indicate how an unprivileged attacker obtains privileged capabilities. We hope to find a solution to disable these attack paths.

3

Deriving Solutions to Harden the Security Policy

Intuitively, we aim to find out the causality relationships between goal nodes and the current problematic configurations, and disable the goal nodes by changing associated configurations. The problem we are going to solve becomes which initial conditions should be changed to disable attacker’s goal nodes. It is impossible and error-prone to manually deal with an actual capability dependency graph with hundreds of thousands vertices. We propose an automatic approach based on advanced SAT solving techniques to refine security policy configurations. Our approach can automatically suggest optimal configuration changes to address the security problem within the context of usability requirements. 3.1

Transforming Capability Dependency Graphs to Logic Propositions

We first extract causality relationships between intermediate vertices and initial vertices and express them as propositional logic formulas. In a capability dependency graph, the require relation can be expressed in conjunctive form, whereas the imply relation can be expressed in disjunctive form. Therefore, an action vertex can always be represented as a conjunctive form of all its predecessor condition vertices, and a condition vertex can always be represented as a disjunctive form of all its predecessor action vertices. Initializing initial

8

Z. Han et al.

condition vertices as atomic proposition variables, we search the capability dependency graph to generate propositional logic formulas for every action vertex and intermediate capability vertex with these variables. In particular, we do not use original configurations of access control directly as initial vertices, but use a one-to-one relationship to describe a single access ability that a subject can apply over an object, such as “dac can access(Uid, Gid, FileName, FileType, write)”. This one-to-one relationship can help us to identify the configuration error more precisely. Algorithm 1 and Algorithm 2 describe the procedure to generate propositional logic formulas. Algorithm 1. GenerateFormulas Input: G {capability dependency graph}, initCapaN odes {set of initial capability vertices}, initCondN odes {set of initial condition vertices} Output: f ormulaMap {map of generated propositional logic formulas, vertex, formula}, Uses: queue {queue of graph vertices} 1. for all vertex v ∈ initCapaN odes do 2. G.removeV ertex(v) 3. end for //Initialize the initial conditions as Boolean variables 4. for all vertex v ∈ initCondN odes do 5. f ormulaMap.put(v, v) 6. end for 7. for all vertex v encountered while breadth-first search do 8. Process(v) 9. end for 10. while queue is not empty do 11. Let v = queue.removeF irst() 12. Process(v) 13. end while

Algorithm 2. Process(v ) Input: G, initCondNodes, formulaMap, queue (defined in Algorithm 1) Output: formulaMap, queue 1. if v is a condition vertex and v ∈ / initCondN odes then 2. Let Sa = {a1 , a2 , ..., an } be the action vertices pointing to v in G 3. if Sa ⊆ f ormulaMap.keyset() then 4. Let T = (a1 ∨ a2 ∨ ... ∨ an ) 5. f ormulaMap.put(v, T ) 6. else 7. queue.add(v) 8. end if 9. end if 10. if v is an action vertex then 11. Let Sc = {c1 , c2 , ..., cn } be the condition vertices pointing to v in G 12. if Sc ⊆ f ormulaMap.keyset() then 13. Let T = (c1 ∧ c2 ∧ ... ∧ cn ) 14. f ormulaMap.put(v, T ) 15. else 16. queue.add(v) 17. end if 18. end if

In Algorithm 1, the first three lines remove initial capability vertices and their associated edges from a capability dependency graph because they are what we protect and should not be changed. The next three lines (lines 4-6) initialize every initial condition node as an atomic proposition variable. For the sake of clarity, we name every atomic proposition variable with the name of its associated vertex. The algorithm then traverses the capability dependency graph in a breadth-first manner to generate propositional logic formulas as many as possible (lines 7-9).

Operating System Security Policy Hardening

9

Each vertex reached during breadth-first search will be handled by Algorithm 2 (line 8). Lines 10-13 are used to iteratively generate formulas for the vertices whose formulas are not generated in the graph traversal. Algorithm 2 is designed to generate formula for a vertex. The algorithm first determines the types of the vertex, lines 1-9 are used to deal with condition vertices and lines 10-18 are for action vertices. For each case, the formula for the vertex will be generated only if formulas of all its predecessor vertices have been generated. Otherwise, the vertex is stored in queue for further process. Since there is no cycle in a capability dependency graph, the iterative process described in lines 10-13 of Algorithm 1 is converged and will stop stating that every vertex has associated with a formula. 3.2

The Security Policy Hardening Problem

Algorithm 1 and Algorithm 2 generate propositional logic formulas for all vertices, which also include the formulas for goal nodes. Assuming that the set of goal nodes in a capability dependency graph is Cg , Cg = {cg1 , cg2 , ..., cgn }. The set of propositional logic formulas for goal nodes is Pg , Pg = {pg1 , pg2 , ..., pgn }. We now can express the privilege escalation threats in the system by ψ, ψ = pg1 ∨pg2 ∨...∨pgn . Our enhancing solution intends to eliminate all privilege escalation threats, so our aim can be expressed as φ = ¬ψ = ¬(pg1 ∨ pg2 ∨ ... ∨ pgn ) = ¬pg1 ∧ ¬pg2 ∧ ... ∧ ¬pgn . Therefore, the problem to harden security policy configurations can be converted to finding a model of φ. Seeking a satisfaction model of φ amounts to finding configuration settings that can prevent an attacker from gaining privileged capabilities. Every variable representing a configuration will be assigned T (“enabled”) or F (“disabled”). If a variable is assigned F, it means that the corresponding configuration needs to be removed or disabled. There are multiple models for this formula, and we select an optimal one which not only disables all attack paths but also minimizes system usability loss. 3.3

A Minimum Cost Solution Based on SAT Solving

In order to select an optimal solution with minimum cost in multiple candidate models, we first need an approach to measure the cost of a solution which consists of multiple hardening measures. In an operating system, it is a challenge for an administrator to assess the cost of an individual hardening measure, e.g., a configuration change. First, it is difficult to directly and separately assess the cost of a configuration change. Second, there are hundreds of thousands of configurations in a system and it is impossible for an administrator to manually assign a cost to each of them. Instead of assessing configuration changes individually, we propose a measurement method based on user’s capabilities to automatically assess the cost of a hardening solution. Our method considers hardening measures not in isolation, but in combination. Some configuration changes may reduce system usability. In a given system, the usability can be described with user’s capabilities.

10

Z. Han et al.

We divide intermediate capability vertices into two types, normal capability nodes and privileged capability nodes. The intermediate capability vertices indicating goal nodes are called privileged capability nodes, and the rest of intermediate capability vertices are called normal capability nodes. A hardening solution must prevent privilege escalation attacks by disabling all privileged capability nodes, while it may have to disable some normal capability nodes, which we call normal capability loss. Definition 2. Given a capability dependency graph G, the propositional logic formula to express the policy hardening problem is φ, S is a model of φ, the number of normal capability nodes in the graph is k and the set of associated formulas is {p1 , p2 , ..., pk }, capability loss for ith capability node caused by S is denoted by costi (S), and the normal capability loss resulting from S can be calculated with the following evaluation formula, C=

k  i=1

 costi (S), and costi (S) =

1 0

pi (S) = f alse pi (S) = true

We expect such a model that minimizes the normal capability loss. It is impossible to iterate all models of φ to select the optimal model with minimum normal capability loss because resolving all models is NP-hard. For a real system, we could not get all models within acceptable time. Fortunately, our problem can benefit much from Group MaxSAT [8]. Group MaxSAT. A group MaxSAT formula is ψ = ψH ∪ GS where ψH is a set of hard clauses and GS = {(G1 , w1 ), ..., (Gm , wm )} is a set of soft groups. Each group (Gi , wi ) ∈ GS is defined by a set of clauses Gi = {Ci1 , ..., Cik } and a weight wi . Any assignment that unsatisfies a subset of the clauses in a soft group (Gi , wi ) will be penalized with a unique cost of wi . The objective of the group MaxSAT problem is to find an assignment that satisfies all hard clauses and minimizes the sum of weights of unsatisfied soft groups. Our security policy hardening problem can be modeled as a group MaxSAT problem. Assuming that the security requirement is expressed as φ which is mentioned in section 3.2, and the set of propositional logic formulas for all normal capability nodes is Ps , Ps = {p1 , p2 , ..., pk }. By applying De Morgan’s law [11], we can convert the proposition φ and the propositional logic formulas in Ps to their conjunctive normal forms (CNF). Given φ representing the CNF of φ, Ps = {p 1 , p 2 , ..., p k } is the CNF set of Ps , policy hardening problem ψ can be represented as group MaxSAT, ψ = φ ∪ GS , where GS = {(p 1 , 1), (p 2 , 1), ..., (p k , 1)}. It can also be expressed intuitively with following formula, ψ = (φ , ∞) ∧ (p 1 , 1) ∧ (p 2 , 1) ∧ ... ∧ (p k , 1), where the weight ∞ means φ must be satisfied. A model of ψ is such an assignment that satisfies φ and minimizes the number of unsatisfied CNFs in GS . Group MaxSAT has been thoroughly studied by the SAT solving community [20], [8]. Although the problem is NP-hard, modern SAT solvers have been very successful in practice, being able to handle Boolean formulas with millions of variables and clauses in seconds.

Operating System Security Policy Hardening

4

11

Implementation

Our prototype consists of three components: Capability Dependency Graph Generator, Graph Translator and SAT Solver. The Capability Dependency Graph Generator first scans the host system and collects current system information and meta configurations of access control. All the collected information is encoded into Prolog fact. All specifications of atomic actions are encoded to Prolog deriving rules. Table 2 summarizes the deriving rules for Ubuntu with SELinux. In our implementation, we consider two types of privilege escalation attacks. The first type is that a local attacker with an unprivileged local account wants to control a privileged process, and his initial capability can be presented as control process code(Proc), where Proc is the security context of an unprivileged local user. The second type is that a remote attacker who can access the targeted host through a network port wants to control a privileged process, and his capability is indicated with control network input(Port). After initializing attacker’s initial capabilities, we use the following Prolog rule to start the Capability Dependency Graph generation: search :- control process code( Proc), fail. The Capability Dependency Graph Generator is implemented with 100 lines of bash scripts, 500 lines of Prolog code and 450 lines of Java code. Table 2. Deriving Rules of Atomic Actions for SELinux Capability

Pre-conditions

Action

control process code( proc(Uid, Gid, Domain)) control process code( proc(Uid, Gid, Domain))

process running(Pid, Uid, Gid, Program, User, Role, Domain) control file data(Program) process reading(Pid, FileName) control file data(FileName) process running(Pid, Uid, Gid, Program, User, Role, Domain) control network input(Port) receiving data(Program, Port) process running(Pid, Uid, Gid, Program, User, Role, Domain) is executable(Program) control file data(Program) user info(TestUser, OldUid, OldGid) process running( , OldUid, OldGid, , , , OldDomain) is executable(Program) dac can execute(OldUid, OldGid, Program) dac execv(OldUid, OldGid, Uid, Gid, Program) se can execute(OldDomain, Program, Domain) privilege enhancing(OldUid, OldGid, OldDomain, Uid, Gid, Domain) control process code(proc(Uid, Gid, Domain)) dac can access(Uid, Gid, FileName, FileType, write) se can access(Domain, FileName, write)

WaitExecute( Program)

control process code( proc(Uid, Gid, Domain)) control process code(*)

control process code( proc(Uid, Gid, Domain))

control file data( FileName)

CompromiseRead( Program, FileName)

CompromiseNetwork (Program, Port) HopeExecute( Program)

Execute(Program)

WriteFile(FileName)

The wildcard of * means that the value can be manipulated by the attacker.

The Graph Translator demands three inputs: a capability dependency graph, the initial capabilities and the goal nodes. All vertices whose in-degrees are 0 represent security configurations, which are declared as Boolean variables. The Graph

12

Z. Han et al.

Translator traverses the capability dependency graph in a breadth-first manner to calculate propositional logic formulas for each vertex. The goal nodes are specified in an input file where an administrator declares the capabilities to be disabled. In our implementation, we focus on the privileged capabilities. The Graph Translator consists of about 1000 lines of Java code. The SAT Solver is implemented on the top of Sat4j [12]. It first constructs the final group MaxSAT expression discussed in Section 3.3, and computes all the maximal satisfiable subsets (MSS). By computing the normal capability loss for each MSS, it selects the MSS with minimum capability loss. Then, it reconstructs a SAT expression with the formulas included in the selected MSS, which is satisfiable. Finally, it solves the new SAT expression and gets an optimal model. The SAT Solver is implemented with about 500 lines of Java code.

5

Experiment and Evaluation

To evaluate the effectiveness and performance, we applied our approach to a real operating system to enhance the security policy, including both DAC and MAC meta configurations. The operating system we selected is Ubuntu10.04 with the standard targeted SElinux policy enabled, where the version of the policy is selinux-policy-ubuntu 0.2.20091117-0ubuntu1. During the experiment, we install and run some common software in the system, namely openssh-server, apacheserver, mysql-server, bind, filezilla, firefox and adobe reader, and use the default security policy configurations when the system is installed. In our experiment, the part of system information collection is conducted on a virtual machine running Ubuntu 10.04 Desktop with SELinux enabled, and the other parts of our experiment are conducted on a Windows machine with Intel(R) Core(TM) i7-4770 3.40 GHz 3.40 GHz CPU, with 8GB memory, and running Windows 7. 5.1

Experiment Results

In the experiment, the Graph Generator takes about 10 minutes to scan and collect relevant information, with most of the time spent on file system scanning, and it takes approximately 30 minutes to generate a capability dependency graph by Prolog’s built-in interpretation engine. The capability dependency graph consists of 815 graph nodes and 922 graph edges, including 207 action vertices, 117 capability vertices, 18 initial capability vertices and 8 goal nodes. The goal nodes can be represented as the control of a process such that (1)the security context is “*”, or (2) the security context contains the SID of root user. We search the capability dependency graph to enumerate all attack paths, each of which is an alternate sequence of capabilities and actions starting from an initial node and ending with a goal node. We find 355 attack paths, where 34 are local attacks and 355 are remote attacks. To block these attack paths, our prototype traverses the capability dependency graph to construct a group MaxSAT expression. Then it solves the expression with our SAT solver and gets a satisfaction model. Information of the

Operating System Security Policy Hardening

13

group MaxSAT expression and the time consumed for the whole process from graph generating to SAT solving are list in Table 3. Table 3. Group MaxSAT # variables # clauses # T # F cost time(sec) 343 644 192 151 16