Part 34 Buffer Overflow
Dynamic Taint Analysis Tracking Data During Actual Program Execution
So far, we have mostly examined the data path based on the assembly and program structure
That is, we did not execute the program and tried to understand from the code where the data might come from and where it might end up
This is called
Static Analysis
Now we want to go one step further
to actually execute the program and see exactly what paths the data takes in this specific execution
Here we come to
Dynamic Taint Analysis
or DTA
Difference between Static and Dynamic
In Static Analysis, the program is not executed
We examine from the code and assembly
Source
↓
Instruction
↓
Function
↓
Sink
But in Dynamic Analysis, the program is actually executed
Program Running
↓
Input
↓
Execution
↓
Track Data
↓
Sink
That is, instead of just saying that this path might exist
We see what actually happened in this specific execution
A simple example
Suppose the program is
C
#include <stdio.h>
#include <string.h>
void process(char *input)
{
char buffer[32];
strcpy(buffer, input);
printf("%s\n", buffer);
}
The program runs and receives this input
hello
Now our question is
What path has this hello taken since entering the program?
This is where Taint Analysis comes in
Source
First we need to specify where the data entered the program from
For example:
stdin
Or for example
read()
recv()
fread()
Then we can say
stdin
↓
TAINTED DATA
That is, the data entered from Source is currently marked as Tainted
Taint Propagation
Now suppose the program does this
C
char *input = get_input();
char *data = input;
char *ptr = data;
From the perspective of Taint Tracking, the data dependency path can be a concept like this
input
↓
Tainted
↓
data
↓
Tainted
↓
ptr
↓
Tainted
That is, the value in ptr comes from data that was entered from Source
Of course, in real tools, the story is a little more complicated
Because Taint is not just a simple label on a variable
The tool must also track data transfers and changes in Registers and Memory
Taint
on a part of the data
An important point is that not always the entire data is Tainted
Suppose the input is
ABCDEFGH
An operation may depend only on the first four bytes
ABCD
In this case, we can have a concept like this
A → Tainted
B → Tainted
C → Tainted
D → Tainted
E → Clean
F → Clean
G → Clean
H → Clean
That is, Taint can be tracked at the level of a part of a value
This is especially true in the analysis of Parsers and Protocols Important
Because we can understand which part of the Input actually affected a particular operation
Taint
in Registers
Now let's move on to what we deal with most in Reverse Engineering
Assembly
Suppose that when the program is executed, this instruction is executed
mov rax,[rbp-40h]
and the Memory at the address
[rbp-40h]
contains Tainted data
So after executing the instruction
[rbp-40h]
↓
RAX
Now the next instruction
mov rcx,rax
will also follow the Taint dependency
Memory
↓
RAX
↓
RCX
Then suppose we have
mov [rdi],rcx
Now the data path is
Input
↓
Memory
↓
RAX
↓
RCX
↓
Memory Write
Here we are not just guessing from the Assembly
We are following the data path in a real execution
An important point about Taint and Pointer
Here we need to be careful Okay
Taint
a Pointer does not necessarily mean that the data that the Pointer points to is tainted
For example:
Pointer
↓
0x12345678
The value of the Pointer itself may be affected by Input
But the content of the address
0x12345678
is not necessarily tainted in the same way
This difference is very important in Memory Analysis
Because
Pointer Taint
and
Pointee Data Taint
are two different concepts
What is the advantage of Dynamic Analysis
?
Suppose a function has 50 different paths
In Static Analysis we may need to examine a large number of these paths
But Dynamic Analysis only sees what is actually executed on the current execution
For example:
Function
├── Path A
├── Path B
├── Path C
└── Path D
If our Input causes Path C to be executed
Input
↓
Path C
↓
Sink