Long story short: if there is an implicit declaration of a function because a header file wasn't included, GCC will warn about the implicit use but allow it. Without the header, GCC doesn't know the expected arguments or return value of the function (logically, it has no prototypes to reference). If the linker can find the function being used, it is linked to during that step.
Consider this contrived and barely functional code sample:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void (*fp)(void);
void handle_error(void)
{
printf("you're doing it wrong\n");
}
void handle200(void)
{
printf("you're still not doing it right\n");
}
void setvalue(int x)
{
if (x == 200)
{
fp = handle200;
}
else
{
fp = handle_error;
}
}
int main(int argc, char *argv[])
{
time_t mytime;
int x;
size_t bufsize = 65 * 1024;
char *buf;
buf = malloc(bufsize);
memset(buf, 0, sizeof(buf));
fgets(buf, bufsize-1, stdin);
x = atoi(buf);
setvalue(x);
mytime = time();
printf("my time is %u\n", mytime);
fp();
free(buf);
return 0;
}
There is no inclusion of time.h as required for the time function, but the code compiles and executes. If GCC is issuing warnings, during compilation it will say timebug.c:44: warning: implicit declaration of function ‘time’.
I was an idiot for not having warnings enabled while compiling the code I was working on, it would have saved me tons of time (u see wat i did there?)
The prototype for time() is:
time_t time(time_t *t);
And the action taken by time() is as follows:
Get the time from the system.
If the time_t pointer supplied as the only argument is not NULL,
dereference it and store the time there.
Return the time.
In the code sample, since the last value pushed to the stack is the value passed to setvalue(), and as it still resides on the stack when time() is called, it is taken as the argument to time(). If the atoi() call on the buffer returns anything that is not 0 (aka NULL), then time() will treat it as a valid pointer, dereference and write the time to it. This wreaked havoc on my code as in my case, the last value to the stack was a structure pointer full of linked lists and things, and the bug didn't trigger until free() happened on those lists.
Clever uses of this could be done to stage a fun CTF service. If an attacker had this vulnerable app to work with and a means of manipulating time (like a MITM of the Network Time Protocol), they could write an arbitrary value to [esp]. Additionally, every 4.2 minutes the lower 8 bits of time rotates. Every 18.2 hours, the lower 16 bits rotates. EBP? Other useful stuff on the stack? Lots of options.
1 comments:
neat; i hadnt considered that and was thinking it was some time_t<->int conversion due to the lack of a prototype, but the stack argument makes sense.
Now I'm wondering how many 'if parameter is non-NULL' type functions exist in contexts where the stack isnt cleaned up between the two function calls.
Post a Comment