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())