1

I'm new to cyber security, and I am trying to understand why the following code is susceptible to a heap overflow attack...

struct data {
 char name[128];
};
struct fp {
 int (*fp)();
};
void printName() {
 printf("Printing function...\n");
}
int main(int argc, char **argv) {
 struct data *d;
 struct fp *f;
 d = malloc(sizeof(struct data));
 f = malloc(sizeof(struct fp));
 f->fp = printName;
 read(stdin,d->name,256);

 f->fp();
}

Is it because of the read(stdin, d->name, 256) as it is reading more than the allocated buffer size of 128 for char name in struct data?

Any help would be great

New contributor
user12378023961 is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
  • Yes, it allows more space to be allocated. But I think it is buffer overflow not heap overflow though – Ruturaj Apr 15 at 22:53
  • I was told that it was something to do with the pointers itself. But man, I am super confused and I am new to C and computing in general :( :( – user12378023961 Apr 15 at 22:59
  • read(stdin,d->name,256); looks wrong. Sure about using stdin here with read(int fd, void *buf, size_t count)? (Enable all compiler warnings) – chux Apr 16 at 1:05
1

A heap overflow attack is similar to a buffer overflow attack, except instead of overwriting values in the stack, the attacker tramples data in the heap.

Notice in your code that there are two dynamically allocated values:

d = malloc(sizeof(struct data));
f = malloc(sizeof(struct fp));

So d now holds the address of a 128-byte chunk of memory in the heap, while f holds the address of an 8-byte (assuming a 64-bit machine) chunk of memory. Theoretically, these two addresses could be nowhere near each other, but since they're both relatively small, it's likely that the OS allocated one larger chunk of contiguous memory and gave you pointers that are next to each other.

So once you run f->fp = printName;, your heap looks something like this:

Note: Each row is 8 bytes wide

     |                        |
     +------------------------+
f -> | <Address of printName> |
     +------------------------+
     |           ▲            |
     |      11 more rows      |
     |       not shown        |
     |                        |
d -> |  <Uninitialized data>  |
     +------------------------+
     |                        |

Your initial assessment of where the vulnerability comes from is correct. d points to 128 bytes of memory, but you let the user write 256 bytes to that area. C has no mechanism for bounds checking, so the compiler is perfectly happy to let you write past the edge of the d memory. If f is right next to d, you'll fall over the edge of d and into f. Now, an attacker has the ability to modify the contents of f just by writing to d.

To exploit this vulnerability, an attacker feeds the address of some code that they've written to d by repeating it for all 256 bytes of input. If the attacker has stored some malicious code at address 0xbadc0de, they feed in 0xbadc0de to stdin 32 times (256 bytes) so that the heap gets overwritten.

     |  0xbadc0de  |
     +-------------+
f -> |  0xbadc0de  |
     +-------------+
     |     ...     |
     |  0xbadc0de  |
     |  0xbadc0de  |
d -> |  0xbadc0de  |
     +-------------+
     |             |

Then, your code reaches the line

f->fp();

which is a function call using the address stored in f. The machine goes to memory location f and retrieves the value stored there, which is now the address of the attacker's malicious code. Since we're calling it as a function, the machine now jumps to that address and begins executing the code stored there, and now you've got a lovely arbitrary code execution attack vector on your hands.

  • Thanks for the detailed explanation sir! Just a quick one, what does f -> fp() mean? What is fp() even ~ like is it a function call of some sort? – user12378023961 Apr 16 at 0:56
  • The arrow operator (->) is 'dereference and access'. f is a pointer to a struct fp, so f->fp is equivalent to (*f).fp, i.e. get the struct fp located at memory location f and access its member named fp. The fp member is a pointer to a function, so putting the () after it is calling the function it points to. – Elan Hamburger Apr 16 at 1:03
  • More generally, in C function names are treated as pointers to those functions. So someFunc() is simply loading someFunc as a pointer to a function and then executing it, just like f->fp() is loading f->fp and executing it. Just like you can store a pointer to data, you can store a pointer to a function and it's effectively the same as calling the function by name. – Elan Hamburger Apr 16 at 1:06
  • I see, its starting to make sense now. So essentially, there was like a buffer overflow in the read(stdin, d->name, 256) and as such, some of the memory contents were leaked into the adjacent struct. Then, when the pointer f is dereferencing the struct fp - then they could instead point it to a different function in the heap ?? I appreciate your help thus far - if you could be so kind to provide a diagram it would be excellent sir. God bless Sir!! – user12378023961 Apr 16 at 1:07
  • Precisely! I actually thought about putting in a diagram in the answer, but decided against it originally. I'll go back and edit the answer to include a few now if it'll help you. – Elan Hamburger Apr 16 at 1:11

Your Answer

user12378023961 is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.