I’m trying to teach myself about buffer overflows and exploitation in C++. I’m an intermediate C++ guy, at best, so bear with me. I’ve followed a few tutorials, but here’s some example code to illustrate my question:
#include <string>
#include <iostream>
using namespace std;
int main()
{
begin:
int authentication = 0;
char cUsername[10], cPassword[10];
char cUser[10], cPass[10];
cout << "Username: ";
cin >> cUser;
cout << "Pass: ";
cin >> cPass;
strcpy(cUsername, cUser);
strcpy(cPassword, cPass);
if(strcmp(cUsername, "admin") == 0 && strcmp(cPassword, "adminpass") == 0)
{
authentication = 1;
}
if(authentication)
{
cout << "Access granted\n";
cout << (char)authentication;
}
else
{
cout << "Wrong username and password\n";
}
system("pause");
goto begin;
}
I know there’s all kinds of bad juju in here with cin << String, etc… Anyhow, when I enter too many letters (a ton of A‘s for instance) into cUser and cPass, I just get an Access Violation from Visual Studio. If, however, I type 20ish A‘s, then a space, then another A into cUser, it skips asking me for cPass (assuming because it’s been filled after the space character caused the previous call to cin to return) and just grants me access.
At what point, and why, is data overflowing into “authentication” and why does it only happen when I have the space and not when I have a million A‘s… I never get the “Access Violation” when I use a space in the input for cUser.
I modified your program a little bit to make it more illustrative:
I compiled it with x64 compiler command-line MS compiler, no optimizations. So now we have an exe that we want to “hack”. We load the program with WinDbg (really good debugger) and take a look at the disassembly (notice, I’ve supplied full debug info, for clarity):
Now, since we know how x64 stack works we can start “hacking”.
RSPis stack pointer, function stack is addresses aboveRSPvalue (stack grows into smaller addresses). So, we see thatRSP+28his wherecUsername,RSP+38hisauthentication, andRSP+40hiscPassword, where 28h, 38h and 40h are hexadecimal offsets. Here is little image to illustrate:What do we see from here? We see that compiler aligned data on 8 byte boundary: for example, we asked to allocate 10 bytes for
cUsername, but we got 16 bytes – x64 bit stack is aligned on 8-byte boundary, naturally. That means in order to write intoauthenticationwe need to write intocUsernameMORE that 16 bytes (symbols). Notice also, that compiler putcPasswordhigher thatauthentication– we cannot overwriteauthenticationusingcPassword, onlycUsername.So now we run our program and input
Username: 0123456789abcdef1.0123456789abcdef= 16 bytes, the next1is going to be put into lower byte ofauthentication– good enough for us: