Taint Analysis On Memory and Pointers
So far we have done Taint Analysis on simple variables
But in a real program the story gets a bit more complicated because data is not just moved between a few variables
It may be entered into Memory
It may be placed into a Buffer
It may be moved through a Pointer
And then several different functions may use the same Memory
This is where Taint Analysis gets a bit more difficult
Suppose we have this
C
char buffer[32];
buffer[0] = input;
Here the data that came from input is entered into Memory
So we can say
input
↓
buffer[0]
Now suppose we have a Pointer to this Buffer
C
char *p = buffer;
And then
char x = p[0];
Here the data is read from Memory again
So our path becomes
input
↓
buffer[0]
↓
p[0]
↓
x
So Taint doesn't just move between Register and Variable
Its path may go through several different stages
Register
↓
Memory
↓
Pointer
↓
Memory
↓
Register
This is very important in Reverse Engineering
Because when you look at the Assembly you may not see anything called input or buffer at all
For example, you may see something like this
mov [rbp-40h], eax
lea rcx, [rbp-40h]
movzx eax, byte ptr [rcx]
Here you have to find the relationship between these instructions yourself
That is
EAX
↓
[rbp-40h]
↓
RCX
↓
[RCX]
↓
EAX
That is, the data is entered into Memory once
Then a Pointer is created to that part
And the data is read again through the same Pointer
Pointer
Itself Is it Tainted or the data it points to
Here we have to be very careful
Suppose
C
char *p = buffer;
char x = *p;
Buffer may contain Tainted data
But Pointer itself, i.e. p, is not necessarily Tainted
For example:
Address = Clean
Data at Address = Tainted
These two are different
So when you analyze Memory, always ask yourself two separate questions
Where did this Address come from
Where did the content of this Address come from
For example, we may have
Pointer
0x500000
This Address itself may be completely normal and reliable
But the content of
Memory[0x500000]
may have come from user input
So Address can be Clean
But Data inside that Address is Tainted
A simpler example:
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] + 10;
return x;
}
If we Tainted the input
the data path will be
input[0]
↓
buffer[0]
↓
x
↓
return
So even if the data is in the Stack for a while
Taint
itself will not go away
as long as the new data is still attached to that value
Now let's see the same in Assembly
We may see something like this
movzx eax, byte ptr [rcx]
mov byte ptr [rbp-20h], al
movzx eax, byte ptr [rbp-20h]
add eax, 10
Here we have to create the path ourselves from the instructions
[RCX]
↓
AL
↓
[rbp-20h]
↓
EAX
↓
ADD 10
If [RCX] contains Tainted data
then this path should also be followed in our analysis
Why Pointer Analysis is important
Because in large programs, data may be passed directly from one Variable to the next Variable No
The path may be like this
Input
↓
Buffer
↓
Pointer
↓
Function A
↓
Memory
↓
Pointer
↓
Function B
↓
Comparison
If you only look at the registers, you may miss the data connection in the middle of the path
That is why it is very important in Reverse Engineering
Data Flow and Memory Analysis
are very interrelated
An important point
Not every Memory Access means transferring Taint
For example:
C++
int x = 100;
int y = x + 20;
Here no data came from Input
So
x = Clean
y = Clean
But if we have
C++
int x = input;
int y = x + 20;
Here we have
x = Tainted
y = Tainted
So we always need to trace the origin of the data
Just because a value is entered into Memory is not enough
We need to see where that value came from and where it was used
Exercise:
Examine this code
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] ^ 0x55;
int clean = 500;
clean += 20;
int result = x + 10;
return result;
}
Assume input[0] is the starting point of Taint
Find the path
input[0]
↓
?
↓
?
↓
?
↓
return
Then specify why clean should not enter this path
@reverseengine