Software Security: Vulnerability Analysis

Similar documents
CS 161 Computer Security

CIS 5373 Systems Security

Program Verification (6EC version only)

Secure Software Development: Theory and Practice

Static Analysis in Practice

Static Analysis in Practice

Static program checking and verification

Analysis/Bug-finding/Verification for Security

Lecture 9 Assertions and Error Handling CS240

finding vulnerabilities

Memory Safety (cont d) Software Security

typedef void (*type_fp)(void); int a(char *s) { type_fp hf = (type_fp)(&happy_function); char buf[16]; strncpy(buf, s, 18); (*hf)(); return 0; }

Simple Overflow. #include <stdio.h> int main(void){ unsigned int num = 0xffffffff;

Static Program Analysis Part 1 the TIP language

Statically Detecting Likely Buffer Overflow Vulnerabilities

Introduction to Information Security

An Annotated Language

Symbolic Execution and Proof of Properties

Information Flow Analysis and Type Systems for Secure C Language (VITC Project) Jun FURUSE. The University of Tokyo

CCured. One-Slide Summary. Lecture Outline. Type-Safe Retrofitting of C Programs

Checking Program Properties with ESC/Java

JML tool-supported specification for Java Erik Poll Radboud University Nijmegen

Introduction to Axiomatic Semantics

1. (6 points) Control Hijacking Indicate whether the statement is always valid. Indicate true or false, and give a one sentence explanation.

Automatic Generation of Program Specifications

Part II. Hoare Logic and Program Verification. Why specify programs? Specification and Verification. Code Verification. Why verify programs?

Program Verification. Aarti Gupta

A Gentle Introduction to Program Analysis

Lectures 20, 21: Axiomatic Semantics

Advances in Programming Languages

Automatic Software Verification

Isolation/Confinement

Goal. Overflow Checking in Firefox. Sixgill. Sixgill (cont) Verifier Design Questions. Sixgill: Properties 4/8/2010

Towards a Practical, Verified Kernel

The confinement principle

Anne Bracy CS 3410 Computer Science Cornell University

Lecture 4 September Required reading materials for this class

C Source Code Analysis for Memory Safety

Code Contracts. Pavel Parízek. CHARLES UNIVERSITY IN PRAGUE faculty of mathematics and physics

Control Flow Integrity & Software Fault Isolation. David Brumley Carnegie Mellon University

Outline. Introduction. 2 Proof of Correctness. 3 Final Notes. Precondition P 1 : Inputs include

Semantic Analysis. CSE 307 Principles of Programming Languages Stony Brook University

Towards automated detection of buffer overrun vulnerabilities: a first step. NDSS 2000 Feb 3, 2000

Computer Security Course. Midterm Review

Specification and verification of a simple machine

Proof Carrying Code(PCC)

Anne Bracy CS 3410 Computer Science Cornell University

One-Slide Summary. Lecture Outline. Language Security

Runtime Checking for Program Verification Systems

Dawn Song

Hybrid Verification in SPARK 2014: Combining Formal Methods with Testing

Finding User/Kernel Pointer Bugs with Type Inference p.1

Hakim Weatherspoon CS 3410 Computer Science Cornell University

15-122: Principles of Imperative Computation (Section G)

Lecture Notes on Memory Layout

CSC Advanced Object Oriented Programming, Spring Specification

CS2351 Data Structures. Lecture 7: A Brief Review of Pointers in C

"Secure" Coding Practices Nicholas Weaver

Hoare logic. A proof system for separation logic. Introduction. Separation logic

Modular and Verified Automatic Program Repairs

Classes, interfaces, & documentation. Review of basic building blocks

School of Computer & Communication Sciences École Polytechnique Fédérale de Lausanne

CITS5501 Software Testing and Quality Assurance Formal methods

COS 320. Compiling Techniques

Intermediate Programming, Spring 2017*

Software Excellence via. at Microsoft

Language Techniques for Provably Safe Mobile Code

Program Static Analysis. Overview

C Bounds Non-Checking. Code Red. Reasons Not to Use C. Good Reasons to Use C. So, why would anyone use C today? Lecture 10: *&!

Ruzica Piskac Yale University

Advances in Programming Languages

Static Analysis: Overview, Syntactic Analysis and Abstract Interpretation TDDC90: Software Security

Program Security and Vulnerabilities Class 2

Programming Languages Third Edition

Advanced Programming Methods. Introduction in program analysis

CMPSC 497: Static Analysis

An Extensible Programming Language for Verified Systems Software. Adam Chlipala MIT CSAIL WG 2.16 meeting, 2012

Verification & Validation of Open Source

Lecture Notes on Arrays

Undefined Behaviour in C

Syscalls, exceptions, and interrupts, oh my!

Advances in Programming Languages

CPS 310 midterm exam #1, 2/17/2017

Introduction to Axiomatic Semantics (1/2)

Lecture 5 - Axiomatic semantics

n Specifying what each method does q Specify it in a comment before method's header n Precondition q Caller obligation n Postcondition

C and C++ Secure Coding 4-day course. Syllabus

Java Modelling Language (JML) References

Hoare triples. Floyd-Hoare Logic, Separation Logic

CS 161 Computer Security

Hacking in C. Pointers. Radboud University, Nijmegen, The Netherlands. Spring 2019

Adam Chlipala University of California, Berkeley ICFP 2006

FreePascal changes: user documentation

Logic-Flow Analysis of Higher-Order Programs

Homework 3 CS161 Computer Security, Fall 2008 Assigned 10/07/08 Due 10/13/08

Introduction to Axiomatic Semantics (1/2)

Basic Verification Strategy

Differential assertion checking

Real-World Buffer Overflow Protection in User & Kernel Space

Verification Condition Generation

Transcription:

Computer Security Course. Software Security: Vulnerability Analysis

Program Verification

Program Verification How to prove a program free of buffer overflows? Precondition Postcondition Loop invariants

Precondition Precondition for f() is an assertion (a logical proposition) that must hold at input to f() If any precondition is not met, f() may not behave correctly Callee may freely assume obligation has been met The concept similarly holds for any statement or block of statements f(x) Precondition: φ(x) Postcondition: ψ

Precondition Example f(x) φ(x) ψ Precondition: fp points to a valid location in memory fp points to a file the file that fp points to contains at least 4 characters 1:int parse(file *fp) { 2: char cmd[256], *url, buf[5]; 3: fread(cmd, 1, 256, fp); 4: int i, header_ok = 0; 5: if (cmd[0] == G ) 6: if (cmd[1] == E ) 7: if (cmd[2] == T ) 8: if (cmd[3] == ) 9: header_ok = 1; 10: if (!header_ok) return -1; 11: url = cmd + 4; 12: i=0; 13: while (i<5 && url[i]!= \0 && url[i]!= \n ) { 14: buf[i] = tolower(url[i]); 15: i++; 16: } 17: buf[i] = \0 ; 18: printf( Location is %s\n, buf); 19: return 0; }

Postcondition Postcondition for f() An assertion that holds when f() returns f() has obligation of ensuring condition is true when it returns Caller may assume postcondition has been established by f() f(x) Precondition: φ(x) Postcondition: ψ

Postcondition Example f(x) φ(x) ψ Postcondition: buf contains no uppercase letters (return 0) (cmd[0..3] == GET ) 1:int parse(file *fp) { 2: char cmd[256], *url, buf[5]; 3: fread(cmd, 1, 256, fp); 4: int i, header_ok = 0; 5: if (cmd[0] == G ) 6: if (cmd[1] == E ) 7: if (cmd[2] == T ) 8: if (cmd[3] == ) 9: header_ok = 1; 10: if (!header_ok) return -1; 11: url = cmd + 4; 12: i=0; 13: while (i<5 && url[i]!= \0 && url[i]!= n ) { 14: buf[i] = tolower(url[i]); 15: i++; 16: } 17: buf[i] = \0 ; 18: printf( Location is %s\n, buf); 18: return 0; }

Proving Precondition Postcondition Given preconditions and postconditions Specifying what obligations caller has and what caller is entitled to rely upon Verify: No matter how function is called, if precondition is met at function s entrance, then postcondition is guaranteed to hold upon function s return f(x) Precondition: φ(x) Postcondition: ψ

Basic idea: Proving Precondition Postcondition Write down a precondition and postcondition for every line of code Use logical reasoning Requirement: Each statement s postcondition must match (imply) precondition of any following statement At every point between two statements, write down invariant that must be true at that point Invariant is postcondition for preceding statement, and precondition for next one f(x) ψ φ(x)

We ll take our running example, fix the bug, and show that we can successfully prove that the bug no longer exists. f(x) φ(x) ψ 1:int parse(file *fp) { 2: char cmd[256], *url, buf[5]; 3: fread(cmd, 1, 256, fp); 4: int i, header_ok = 0; 5: if (cmd[0] == G ) 6: if (cmd[1] == E ) 7: if (cmd[2] == T ) 8: if (cmd[3] == ) 9: header_ok = 1; 10: if (!header_ok) return -1; 11: url = cmd + 4; 12: i=0; 13: while (i<4 (i<5 && url[i]!= \0 && url[i]!= n ) { 14: buf[i] = tolower(url[i]); 15: i++; 16: } 17: assert(i>=0 && i <5); 18: buf[i] = \0 ; 19: printf( Location is %s\n, buf); 20: return 0; } Bug Fixed! F F assert(i>=0 && i<5); i = 0; is(i<5 is(i<4 && url[i]!= \0 && url[i]!= \n )? T i++; T CRASH! buf[i] = \0 ;

We ll take our running example, fix the bug, and show that we can successfully prove that the bug no longer exists 1:int parse(file *fp) { 2: char cmd[256], *url, buf[5]; 3: fread(cmd, 1, 256, fp); 4: int i, header_ok = 0; 5: if (cmd[0] == G ) 6: if (cmd[1] == E ) 7: if (cmd[2] == T ) 8: if (cmd[3] == ) 9: header_ok = 1; 10: if (!header_ok) return -1; 11: url = cmd + 4; 12: i=0; 13: while (i<4 && url[i]!= \0 && url[i]!= n ) { 14: buf[i] = tolower(url[i]); 15: i++; 16: } 17: buf[i] = \0 ; 18: printf( Location is %s\n, buf); 18: return 0; } f(x) φ(x) So assuming fp points to a file that begins with GET, we want to show that parse never goes down the false assertion path. F assert(i>=0 && i<5); F i = 0; is(i<4 && url[i]!= \0 && url[i]!= \n )? T i++; T ψ But first, we will need the concept of loop invariant. CRASH! buf[i] = \0 ;

Loop Invariant and Induction An assertion that is true at entrance to the loop, on any path through the code Must be true before every loop iteration Both a pre- and post-condition for the loop body φ(i) φ(i+1) i = 0; A is(i<5 && url[i]!= \0 B && url[i]!= \n )? φ(i) T φ(i+1) F buf[i] C = tolower(url[i]); i++;

To verify: Loop Invariant and Induction Base Case: Prove true for first iteration: φ(0) Inductive step: Assume φ(i) at the beginning of the loop. Prove φ(i+1) at the start of the next iteration. φ(i) φ(i+1)

Try with our familiar example, proving that (0 i<5) after the loop terminates: LOOP INVARIANT: /* φ(i) = (0 i<5) */ φ(i) φ(i+1) F F i = 0; is(i<4 && url[i]!= \0 && url[i]!= \n )? assert(i>=0 && i<5); T i++; T CRASH! buf[i] = \0 ; Base Case: /* φ(0) = (0 0<5) */ Inductive Step: /* spp(0 i<5) at the beginning of the loop */ /* for (0 i<4), clearly (0 i+1<5) */ /* (i=5) is not a possible case since that would fail the looping predicate */ /* (0 i+1<5) at the end of the loop */ /* parse never fails the assertion */

Function Post-/Pre-Conditions For every function call, we have to verify that its precondition will be met Then we can conclude its postcondition holds and use this fact in our reasoning Annotating every function with pre- and post-conditions enables modular reasoning Can verify function f() by looking at only its code and the annotations on every function f() calls Can ignore code of all other functions and functions called transitively Makes reasoning about f() an almost purely local activity

Documentation Pre-/post-conditions serve as useful documentation To invoke Bob s code, Alice only has to look at pre- and post-conditions she doesn t need to look at or understand his code Useful way to coordinate activity between multiple programmers: Each module assigned to one programmer, and pre-/postconditions are a contract between caller and callee Alice and Bob can negotiate the interface (and responsibilities) between their code at design time

Avoiding Security Holes To avoid security holes (or program crashes) Some implicit requirements code must meet Must not divide by zero, make out-of-bounds memory accesses, or deference null ptrs, Prove that code meets these requirements using same style of reasoning Ex: when a pointer is dereferenced, there is an implicit precondition that pointer is non-null and in-bounds

Avoiding Security Holes Proving absence of buffer overruns might be much more difficult Depends on how code is structured Instead of structuring your code so that it is hard to provide a proof of no buffer overruns, restructure it to make absence of buffer overruns more evident Lots of research into automated theorem provers to try to mathematically prove validity of alleged pre-/post-conditions Or to help infer such invariants

Program Analyzers analyze large code bases Code Report Type Line Spec Program Analyzer 1 stack oflow 324 2 buffer oflow 8,491 3 buffer oflow 23,212 4 mem leak 86,923 5 unsafe indexing op 5,393,245 false alarm false alarm 12,002 info leak 10,921 potentially reports many warnings may emit false alarms

Soundness, Completeness Property Soundness Definition If the program contains an error, the analysis will report a warning. Sound for reporting correctness Completeness If the analysis reports an error, the program will contain an error. Complete for reporting correctness

Complete Incomplete Sound Reports all errors Reports no false alarms Undecidable Reports all errors May report false alarms Decidable (Ex: Manual Program Verification) (Ex: Abstract Interpretation) Unsound May not report all errors Reports no false alarms Decidable May not report all errors May report false alarms Decidable (Ex: Symbolic Execution) (Ex: Syntactic Analysis)

Isolation and Reference Monitor Slide credit: Dan Boneh

Running untrusted code We often need to run buggy/untrusted code: programs from untrusted Internet sites: toolbars, viewers, codecs for media player old or insecure applications: ghostview, outlook legacy daemons: sendmail, bind Honeypots Goal: ensure misbehaving app cannot harm rest of system Approach: Confinement Can be implemented at many different levels

Confinement (I): Hardware Hardware: run application on isolated hw (air gap) app 1 app 2 Network 2 air gap network 1

Confinement (II): Firewall Firewall: isolate internal network from the Internet

Confinement (III): VM Virtual machines: isolate OS s on a single machine app1 app2 OS 1 OS 2 Virtual Machine Monitor (VMM)

Confinement (IV): Processes Processes: Isolate a process in a single operating system System Call Interposition process 2 process 1 Operating System

Confinement (V): SFI Threads: Software Fault Isolation (SFI) Isolating threads sharing same address space

Implementing confinement: Reference Monitor Key properties: Mediates requests from applications Implements protection policy Enforces isolation and confinement Must always be invoked (complete mediation) Every application request must be mediated Tamperproof/fail safe Reference monitor cannot be killed or if killed, then monitored process cannot accessing anything requiring reference monitor s approval Small enough to be analyzed and validated