Program Verification & Vulnerabilities: Buffer Overflows, Format Strings, Integer Overflow, Slides of Software Engineering

An overview of program verification and various types of vulnerabilities, including memory safety vulnerabilities such as buffer overflows, format strings, and integer overflows, as well as runtime detection methods like stack guard and fuzzing. It also covers static analysis techniques for detecting bugs and the use of pre- and post-conditions for documenting and proving code correctness.

Typology: Slides

2012/2013

Uploaded on 04/25/2013

ayushmati
ayushmati 🇮🇳

4.4

(130)

159 documents

1 / 34

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Program Verification & Other Types of
Vulnerabilities
Docsity.com
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22

Partial preview of the text

Download Program Verification & Vulnerabilities: Buffer Overflows, Format Strings, Integer Overflow and more Slides Software Engineering in PDF only on Docsity!

Program Verification & Other Types of

Vulnerabilities

Review

  • Memory safety vulnerability
    • Buffer overflow
    • Format string
    • Integer overflow
  • Runtime detection
    • Stack Guard, randomization, etc
  • Fuzzing for bug finding
    • Blackbox fuzzing
    • Whitebox fuzzing
    • Graybox fuzzing

Static Analysis

  • Instead of running the code to detect attacks or find bugs, we statically analyze code
  • Simple pattern match:
    • Whether program uses unsafe APIs: gets, sprintf, etc.
  • Simple checks:
    • E.g., variable use before def or initialization, double free, null terminator, etc.
  • More sophisticated analysis
    • E.g., potential array-out-of-bounds check
  • Many tools available
    • Open source:
      • http://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis
    • Commercial tools: Coverity, Fortify, etc.

Program Verification

• Can we prove a program free of buffer

overflows?

• How to prove a program free of buffer

overflows?

  • Precondition
  • Postcondition
  • Loop invariants

Simple Precondition Example

int deref(int *p) {

return *p;

  • Unsafe to dereference a null pointer
    • Impose precondition that caller of deref() must meet: p ≠ NULL holds at entrance to deref()
  • If all callers ensure this precondition, it will be safe to

call deref()

  • Can combine assertions using logical connectives

(and, or, implication)

Another Example

int sum(int *a[], size_t n) {

int total = 0, i; for (i=0; i<n; i++) total += *(a[i]); return total;

• Precondition:

  • a[] holds at least n elements
  • Forall j.(0 ≤ j < n) → a[j]≠NULL

Proving Precondition→Postcondition

  • Given preconditions and postconditions
    • Which specifies what obligations caller has and what caller is entitled to rely upon
  • Verify that, no matter how function is called, if

precondition is met at function’s entrance, then

postcondition is guaranteed to hold upon function’s

return

  • Must prove that this is true for all inputs
  • Otherwise, you’ve found a bug in either specification (preconditions/postconditions) or implementation

Proving Precondition→Postcondition

  • Basic idea:
    • 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

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-/post- conditions 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 dereference null pointers, …
  • We can try to 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 inbounds

Buffer Overruns

  • 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

• Other types of vulnerabilities

Non-Language-Specific Vulnerabilities

int openfile(char *path) { struct stat s; if (stat(path, &s) < 0) return -1; if (!S_ISRREG(s.st_mode)) { error("only regular files allowed!"); return -1; } return open(path, O_RDONLY); }

  • Code to open only regular files
    • Not symlink, directory, nor special device
  • On Unix, uses stat() call to extract file’s meta-data
  • Then, uses open() call to open the file

What’s the Vulnerability?

  • Code assumes “s” is unchanged between stat() and open() calls – Never assume anything…
  • An attacker could change file referred to by path in between stat() and open() - From regular file to another kind - Bypasses the check in the code! - If check was a security check, attacker can subvert system security
  • Time-Of-Check To Time-Of-Use (TOCTTOU) vulnerability
    • Meaning of path changed from time it is checked (stat()) and time it is used (open())