Security bugs
New titles?
- Unvalidated Input detection.
- Unvalidated user input detection
In this section we will define “security bugs” in the context of error traces. We want to answer the following 3 questions
- What is a security bug ? How can we formally define a security bug ?
- Given an error trace or a set of error traces, how can we find out if there is a security bug in the program ?
- How can we find out which statement is the cause of the security bug ?
Introduction
A security bug is a class of bugs in which a user input can directly influence whether we reach the error state or not. If we try to define this requirement a bit more formally, a bug is called a security bug if in an error trace, following three properties are satisfied:
- There is some statement \( \mathbf{st} \) in the error trace where the program reads a user input.
- There is some input value such that continuing the execution of the trace from \( \mathbf{st} \), we definitely reach the error.
- There is some input value such that continuing the execution of the trace from \( \mathbf{st} \), we do not reach the error.
Examples
Consider the following example which demonstrates a simple case of buffer overflow caused by a user input:
/* Simple buffer overflow
* Security
*
* Author: Numair Mansur (mansurm@informatik.uni-freiburg.de)
* Date: 2017-10-30
*/
int get_location(void);
int get_value(void);
int f(void);
void main(void) {
int age[10];
int location,value;
location = get_location(); // from user
value = get_value(); // from user
age[location] = value;
}In the above error trace, all three properties of a security bug are satisfied.
Now consider the following non-security case of the above example:
/* Simple buffer overflow
* Non-Security
*
* Author: Numair Mansur (mansurm@informatik.uni-freiburg.de)
* Date: 2017-10-30
*/
int get_location(void);
int get_value(void);
int f(void);
void main(void) {
int age[10];
int location,value, r;
location = get_location(); // from user
value = get_value(); // from user
r = f();
if(r){
age[location] = value;
}
}The program now has a variable r which is taking it’s value from an unknown function f(). This violates property 2 from our definition above and hence we will conclude that there is no security bug in this program.
To see more examples and to find out how we can find security bugs through Ultimate, click here.
Formal Definition
Execution: Let be an error trace of length . An execution of is a sequence of states such that where is the transition formula of .
Blocking Execution: An execution of a trace of size is called a blocking execution if there exists a sequence of states where such that , where is the transition formula of and there exists an assume statement in the trace at position such that .
Security bug: Let be an error trace of length where is an assigning statement at position that assigns a value taken from a user to some variable . The program contains a security bug if there exists an execution of and some values and such that if we start in the state , every execution of the trace is blocking and conversely no execution is blocking for the trace .
Algorithm:
From the formal definition above, and from the definitions of simple bug and definite bug, it is obvious that a program has a security bug if in a feasible error trace, an assigning statement which is taking it’s value from a user is relevant with respect to a simple bug and a definite bug. However, calculating the relevance of a statement for both simple and definite bug can be expensive and inefficient. We therefore propose the following alternative definition for security bug and prove that both definitions are equivalent.
Theorem:
Let be a feasible error trace of length and be a havoc statement of the form at position that assigns a non-deterministic value to the variabble . The statement is relevant with respect to both simple and definite bug iff it is the last error admitting statement in the trace.
Proof:
Let be a havoc statement of the form which assigns a non-deterministic value to the variable at position where . Lets assume is an error admitting statement. By definition, this will imply that there exits a value such that all executions of the trace are blocking. Since we know that is relevant with respect to definite bug, that means that for a value , no blocking execution exists for the trace , which contradicts with our assumption that is error admitting.
Let be a havoc statement of the form which assigns a non-deterministic value to the variable at position . Since is a feasible error trace, we know that there exists an execution and some value , such that if we assign to , the trace starting in the state has a non-blocking execution. Furthermore, since the statement is the last error admitting statement, no havoc exists after the position which is relevant with respect to a simple bug. This means that no execution of the trace will be blocking. Hence is relevant with respect to a definite bug.
Thanks to the modified definition, we can use our algorithm to find relevant statements with respect to simple bugs and efficiently find if there is a security bug in the program.
Algorithm 1. security(): Finding the presence of security bug in a program
———————————————————————————————————
Input: error trace of length
Output: boolean value indicating the presence of a security bug
1 trace error trace
2 relevantPositions relevance(trace)
3 for ( =relevantPositions.length() - 1 to =0 )
4 if trace[ relevantPositions[] ] is a user input then
5 return
6 return
Evaluation
[more to follow…]