Symbolic Execution for Mortals
In 2003 the Defense Advanced Research Projects Agency, DARPA,
announced the Cyber Grand Challenge, a two-year competition seeking to
create automatic systems for vulnerability detection, exploitation, and
patching in near real-time which brought quite a big and complex task to
the table. With this task symbolic execution techniques, which have been
around since the mid '70s, came to the attention of a bigger and
broader audience. Difficulty is intriguing for hackers and security
researchers, it makes things interesting but when we are just learning
about something complicated it can be overwhelming and we need to take a
step back and tackle it from a different angle.
This article is meant to give you a different view or angle from which
to tackle this topic, to help you understand the basic concepts of
symbolic execution and how it works in order to enable you to do some
more in depth research on the topic. Common problems and how to solve
them will not be covered here but once you are finished with this
article you should have all the concepts to be able to understand the
why to the problems behind symbolic execution.
Symbolic Execution
Symbolic execution is a popular program analysis technique introduced in
the mid ’70s in the context of software testing to check whether a
certain property can be violated by a program. Today it conserves the
same principles and there is no doubt that it has played a big role in
the detection of security vulnerabilities in modern software (Baldoni et
al, 2016).
Symbolic execution can simultaneously explore multiple paths that a
program could take under different inputs that don’t necessarily have to
be defined. The analysis is done at either a source or binary code
level. You might wonder how a program can be executed with different
inputs without actually knowing the exact value of that input. Well,
think of it as a mathematical expression, if you don’t know the exact
value to plug into the expression, you just put x or y which is just
a variable or better yet a symbol to represent some unknown value. Same
thing here, if we give a program symbolic, rather than concrete input
values it can run with those values and give me an expression at the
end.
How it works
The execution of all possible paths is done by a symbolic execution
engine. To keep track of all executions, the engine always maintains a
state (stmt, σ, π).
-
stmt: Statement. The next statement to evaluate. -
σ: Symbolic Store. Maps program variables to concrete values or
symbolsαi. -
Ï€: Path Constraints. A set of all conditions the symbols must
meet in order to reach the stmt in the execution branch. Always true
at the beginning.
To better understand how symbolic execution works we are going to look
at an example that illustrates the steps taken by the symbolic execution
engine.
Consider the following code:
simexec.c.
void myFunct(int a, int b){
int x = 1, y = 0;
if (a != 5){
y = 3 * x;
if (b == 1){
x = 5 * (a + b);
}
}
assert(x - y != 0);
}
Let’s try to find the values that make the assert fail.

Figure 1. Symbolic execution tree of function myFunct()
The symbolic execution tree in the image represents the states the
symbolic execution engine goes through during the analysis of the
program, in this case during the analysis of myFunct().
-
The function receives two parameters,
aandb. The symbolic
execution engine assigns symbols for each parameter:a → αa,b → αb. Since the program hasn’t really began executing, we don’t yet
have any paths and therefore no constraints, thusπis always true
at the beginning. The next statement to execute isint x = 1, y = 0;leaving our engine in stateA: σ = {a → αa , b→ αb} π = true. -
The next statement is executed and two values are assigned. This
addsx →1,y → 0to ourσ. No conditions have been established
soπis stilltrue. The next statement to execute isif (a != 5)leaving our engine in stateB: σ = {a → αa , b → αb, x = 1, y = 0} π = true. -
Here a decision is made based on the value of the first parameter,
a. Since symbolic execution doesn’t assign concrete values to
input the initial symbols remain and a will still be equal toαa.
Symbolic execution analyses multiple paths, so the execution tree
must continue whether the condition is met or not. The symbolic
execution engine can take two states. If the condition is met,
αa!= 5, that implies a constraint which is added toπleaving
our engine in stateC: σ = {a → αa , b → αb, x = 1, y = 0} π = αa != 5where the next statement to execute would bey = 3 * x. If
the condition is not met,αa = 5, that also implies a constraint
and it must also be added to ourπleaving our engine in stateD: σ = {a → αa , b → αb, x= 1, y = 0} π = αa = 5and the next
statement to execute would be the assert which when executed would
pass all checks meaning the end of that branch. -
The value of
yis assigned to be three times that ofx. The only
thing this changes is the value ofyin ourσand the next
statement to execute is a condition leaving our engine in stateE: σ = {a → αa , b →αb, x = 1, y = 3} π = αa != 5. -
A decision is made based on the value of the second parameter,
b.
Both possibilities are evaluated. If the condition is met,αb = 1,
that is another constraint and it is added to ourπ. The next
statement to execute would bex= 5 * a ` b;+ leaving our engine in state `F: σ = {a → αa , b → αb, x = 1, y= 3} π = αa != 5 ∧ αb = 1. If
the condition is not met,αb != 1, that constraint must also be
added to ourπleaving our engine in stateG: σ = {a → αa , b → αb, x = 1, y = 3} π = αa!= 5 ∧ αb != 1where the next statement to
execute would be the assert which when executed would pass all
checks meaning the end of that branch. -
The value of
xis assigned to be five times the sum ofaand
b. The only thing this changes is the value ofxin ourσ
leaving our engine in stateH: σ = {a → αa , b → αb, x = 5(αa ` αb), y = 3} π = αa != 5 ∧ αb = 1+ where the next statement to execute would be the assert. When the assert, `x -y != 0is evaluated the values are replaced
for what our engine has.5(αa `αb) - 3 = 0 ∧ αa != 5 ∧ αb = 1+ this is the expression that tells me what values will make my assert fail. To satisfy the previous expression `αahas to be equal to0.6
andαbhas to be1.
There it is, we found a property that can be violated and the conditions
that must be met in order to violate it.
References
*** This is a Security Bloggers Network syndicated blog from Fluid Attacks RSS Feed authored by Juan Aguirre. Read the original post at: https://fluidattacks.com/blog/symbolic-execution-mortals/

