I won't explain in too much detail, but the following code creates a buffer overflow that controls the return instruction pointer on the stack, changing the flow of the code into calling func2().
/*
compile: gcc test.c -fno-stack-protector
run: ./a.out 4 (or ./a.out 2... greater than ./a.out 1)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#define USE_CANARY
void func2()
{
printf("I did it!n");
}
void func(const char *input)
{
#ifdef USE_CANARY
long int canary = 0xdeadbeefc001d00d;
#endif
char buf[256];
// strcpy(buf, input);
// cause there's a null in the address...
memcpy(buf, input, 256+8*8);
// buffer overfl0wwwwwwwwwww
printf("buf: %sn", buf);
fflush(stdout);
#ifdef USE_CANARY
if (canary != 0xdeadbeefc001d00d) {
printf("Buffer overflow detected! Aborting!n");
abort();
}
#endif
}
int main(int argc, char **argv)
{
char input[1024];
int loop;
// fill up input with 'A' x 256 (leaving 1024-256 bytes left)
memset(input, 'A', 256);
printf("size of func2's pointer (in bytes): %ldn", sizeof(&func2));
printf("func2's address: %lxn", (unsigned long) func2);
if (argc < 2) {
loop = 4;
}
else {
loop = atoi(argv[1]);
}
// fill past the 'A's with a pointer to func2(), up to "loop".
for (int i = 0; i < loop; i++) {
unsigned long *p = (unsigned long *) (input + 256 + i * sizeof(&func2));
*p = (unsigned long) func2;
}
printf("input: %sn", input);
// verify the func2's addresses are actually inside the input buffer.
for (int i = 0; i < loop; i++) {
char *p = input + 256 + i * sizeof(&func2);
unsigned long *q = (unsigned long *) p;
printf("address: %lxn", *q);
}
// call func with input buffer.
func(input);
}
Output:
gcc test.c -fno-stack-protector
$ ./a.out
size of func2's pointer (in bytes): 8
func2's address: 561e883a51e9
input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�Q:�V
address: 561e883a51e9
address: 561e883a51e9
address: 561e883a51e9
address: 561e883a51e9
buf: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�Q:�V
I did it!
I did it!
I did it!
Segmentation fault (core dumped)
Notice if you compiled with -DUSE_CANARY, it uses our artificial canary, and will abort before the overflow exploits the return instruction pointer.
gcc test.c -fno-stack-protector -DUSE_CANARY
$ ./a.out
size of func2's pointer (in bytes): 8
func2's address: 562a98ea0209
input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA �*V
address: 562a98ea0209
address: 562a98ea0209
address: 562a98ea0209
address: 562a98ea0209
buf: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA �*V
Buffer overflow detected! Aborting!
Aborted (core dumped)
Source code follows:
/*
compile: gcc test.c -fno-stack-protector
run: ./a.out 4 (or ./a.out 2... greater than ./a.out 1)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#define USE_CANARY
unsigned long get_canary()
{
asm("mov %fs:0x28, %rax");
}
void func2()
{
printf("I did it!n");
}
void func(const char *input)
{
#ifdef USE_CANARY
long int canary = 0xdeadbeefc001d00d;
#endif
char buf[256];
// strcpy(buf, input);
// cause there's a null in the address...
memcpy(buf, input, 256+8*8);
// buffer overfl0wwwwwwwwwww
printf("buf: %sn", buf);
fflush(stdout);
#ifdef USE_CANARY
if (canary != 0xdeadbeefc001d00d) {
printf("Buffer overflow detected! Aborting!n");
abort();
}
#endif
}
int main(int argc, char **argv)
{
char input[1024];
int loop;
// fill up input with 'A' x 256 (leaving 1024-256 bytes left)
memset(input, 'A', 256);
printf("size of func2's pointer (in bytes): %ldn", sizeof(&func2));
printf("func2's address: %lxn", (unsigned long) func2);
if (argc < 2) {
loop = 2; // loop = 2 is the optimal solution for this case.
}
else {
loop = atoi(argv[1]);
}
unsigned long gcc_canary = get_canary(); // get gcc canary secret value.
// After the 'AAAA' put a gcc_canary for us. The space gcc will provide will be 16 bytes, so we just fill
// it up with 2x canary secret values. (assuming loop = 2)
for (int i = 0; i < loop; i++) {
unsigned long *p = (unsigned long *) (input + 256 + i * sizeof(&func2));
*p = (unsigned long) gcc_canary;
}
// Now put a pointer to func2(), at least twice (assuming loop = 2).
for (int i = loop; i < loop * 2; i++) {
unsigned long *p = (unsigned long *) (input + 256 + i * sizeof(&func2));
*p = (unsigned long) func2;
}
printf("input: %sn", input);
// verify the func2's addresses are actually inside the input buffer.
for (int i = 0; i < loop * 2; i++) {
char *p = input + 256 + i * sizeof(&func2);
unsigned long *q = (unsigned long *) p;
printf("address or canary: %lxn", *q);
}
// call func with input buffer.
func(input);
}
Output:
gcc test.c
$ ./a.out
size of func2's pointer (in bytes): 8
func2's address: 55ad7774c21d
input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
address or canary: ac9d07067ff30500
address or canary: ac9d07067ff30500
address or canary: 55ad7774c21d
address or canary: 55ad7774c21d
buf: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
I did it!
Segmentation fault (core dumped)