-2

I'm trying to make a simple implementation of the Heartbleed Bug in C/C++ over Linux (Using ElementaryOS on vmplayer). From my understanding of the heartbleed bug, it involves the client sending a heartbeat request to the server, specifying a greater size for the included payload than the actual size of the payload, which results in the server including contents from memory that will be laid out after the local payload buffer, in the response.

What I have so far is a client/server application. The client connects to the server and sends a piece of sensitive information, in this case, a password. Next, the client sends a heartbeat request to the server, specifying the wrong size for the payload. I declare my local variables like this:

        char password[30];// = new char[30];    //Some sensitve data, that will be accessed using the Heartbleed Bug
        char temp[15];// = new char[15];
        char payload[45];// = new char[45];

I retrieve the content from the heartbeat request like this:

        int payloadSize = atoi(strtok(buffer, " "));
        temp = strtok(NULL, "\n");

In the heartbeat request, the payload is "payload", and the size is 45. I then make a call to memcpy() like this:

        memcpy(payload, temp, payloadSize /* 45 in this case */);

I'm expecting the payload variable to be hold the value "payload", followed by contents from the password variable. However payload holds the value "payload" only.

        cout<<payload<<endl; //prints payload

Can anyone point out what I'm doing wrong?

All code:

int main()
{
    ofstream log("ServerLog.txt");

    int listeningSocket = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in serverAddress, clientAddress;
    socklen_t clientLen;

    serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1");
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = 54321;

    if(bind(listeningSocket, (sockaddr *) &serverAddress, sizeof(serverAddress)) < 0)
    {
        cout<<strerror(errno)<<endl;
    }

    else
    {
        cout<<"Socket bound to port 12345"<<endl;

        if(listen(listeningSocket, 5) < 0)
        {
            cout<<strerror(errno)<<endl;
        }

        else
        {
            log<<"Listening for connections now..."<<endl;
            int commSocket = accept(listeningSocket, (sockaddr *) &clientAddress, &clientLen);

            log<<"Now connected to a client..."<<endl;

            int N = 0;
            char buffer[100];// = new char[100];
            char password[30];// = new char[30];    //Some sensitve data, that will be accessed using the Heartbleed Bug
            char *temp = new char[15];
            char payload[45];// = new char[45];

            N = recv(commSocket, password, 100, 0);     //Receive sensitive data, in this case, a password

            if(N == 0)          //Check for remote socket close
            {
                cout<<"The remote connection has been closed."<<endl;
            }

            else if(N < 0)      //In case there is an error
            {
                cout<<strerror(errno)<<endl;
            }

            else        //All good, move on
            {
                //cout<<N<<" "<<password<<endl;
                log<<"Password received from client: "<<password<<" of length: "<<N<<endl;

                N = recv(commSocket, buffer, 100, 0);      //recv heartbeat request

                if(N == 0)          //Check for remote socket close
                {
                    cout<<"The remote connection has been closed."<<endl;
                }

                else if(N < 0)      //In case there is an error
                {
                    cout<<strerror(errno)<<endl;
                }

                else
                {
                    log<<"Heartbeat request received from client: "<<buffer<<endl;

                    int payloadSize = atoi(strtok(buffer, " "));
                    temp = strtok(NULL, "\n");

                    memcpy(payload, temp, 45);

                    if(N < 0)
                    {
                        cout<<strerror(errno)<<endl;
                    }
                }
            }
        }
    }

    return 0;
}
Aamir Khan
  • 2,945
  • 2
  • 25
  • 32
  • How are you examining the `payload` after `memcpy()`? Viewing/printing as a string (which won't help you, since the `\0` at the end of `"payload"` will end the print)? Or dumping all 45 bytes, which may work depending on the arrangement of your stack? – Paul Roub Jun 01 '15 at 21:13
  • Are you intending that the memcpy will pick up `password` too? – M.M Jun 01 '15 at 21:14
  • There is no guarantee that `password` is directly after `temp` in memory. In case of the real Heartbleed, something like this was the case, but maybe your program has a different memory layout. – deviantfan Jun 01 '15 at 21:14
  • `memmove()` works better than `memcpy()` with buffers that may overlap. – chux - Reinstate Monica Jun 01 '15 at 21:14
  • 3
    There is nothing wrong with memcpy() -- It is doing exactly what you're telling it to do. – PaulMcKenzie Jun 01 '15 at 21:15
  • 1
    Paul Roub: Using a simple cout. I actually did think of this, the null character causing the termination, so I wrote a for loop to print out all 45 characters one by one. It didn't seem to help. (Got the same result) Matt McNabb: Yes deviantfan: From my understanding of how local variables are laid out in memory, variables are pushed in the order they appear onto the stack, which should guarantee that password is after temp. – Aamir Khan Jun 01 '15 at 21:15
  • what is `char temp[15];// = new char[15];` supposed to mean? – M.M Jun 01 '15 at 21:17
  • You'll need to post your actual code instead of bits and pieces of it . For example you haven't shown how you got the data into `temp` and `password`, or what data is in there when you are testing. And `char temp[15];` followed by `temp = strtok(NULL, "\n");` must give a compilation error, so this isn't even bits and pieces of your real code. [See here](http://stackoverflow.com/help/mcve) for how to prepare code for posting in a question. – M.M Jun 01 '15 at 21:19
  • Local variables may be stored in any order or even optimized out – M.M Jun 01 '15 at 21:20
  • What you just posted doesn't compile – M.M Jun 01 '15 at 21:23
  • @AamirKhan `I'm expecting the payload variable to be hold the value "payload", followed by contents from the password variable` Maybe that's the problem. Who says that's the way those variables will be layed out in memory, or if they even exist? If you stored those variables in a `struct` then maybe you will be closer to what you're looking for. – PaulMcKenzie Jun 01 '15 at 21:25
  • Why do you expect that copying 45 bytes out of `buffer` will read what is in `password`? Buffer is 100 bytes so you are not overflowing it. – M.M Jun 01 '15 at 21:27
  • Not buffer, password. According to the lectures I've taken (https://class.coursera.org/softwaresec-002/lecture/29) local variables are pushed in the order they appear on the stack. So I'm expecting temp to be initially copied into payload, and then some content from password too, since it's an overflow. – Aamir Khan Jun 01 '15 at 21:33
  • Your lectures are wrong. Also `temp` is a *pointer* (in your updated code). It points into `buffer`. Using `temp` as argument to `memcpy` means to read out of `buffer` from the point where `temp` is pointing (which will be just after the first space in the original buffer). The `new char[15];` is never read or written and leaks memory (which wasn't even in the stack anyway). – M.M Jun 01 '15 at 21:38
  • @AamirKhan why not hard-code into those arrays the data you claim will demonstrate the issue instead of all that socket code? Assume that the connection is made, and some data that illustrates the issue has been sent. ok, so skip all of that superfluous socket code and just fill the arrays with the suspect data, and then continue on from there. That would make it much easier for us, and probably for you also. – PaulMcKenzie Jun 01 '15 at 22:03

2 Answers2

2

C style strings are terminated with a null terminator (\0), the length of the buffer they are stored isn't known to cout. When you use memcpy it copies the null terminator and all the stuff beyond the string. The buffer does actually hold everything behind the string, but it is after the null terminator so it is not considered part of the string, which means it is not printed by cout.

The buffer payload might look like this

{ 'p', 'a', 'y', 'l', 'o', 'a', 'd', '\0', 'g', 'a', 'r', 'b', 'a', 'g', 'e' }
                                     ^^^^ String ends here

So when you print it using cout it only prints until the \0 character. Meaning it will print payload and nothing else.

If you want to print all the values in the buffer you will have to print them one by one. You could do something like this:

for(int i = 0; i < 45; i++)
{
    cout << payload[i];
}
cout << endl;

which will print all the individual characters of the buffer to the console.

As printing special characters could produce strange output you might want to print out the numeric values of the characters. To print the numeric values of the bytes you can do what @MattMcNabb suggested using printf. The code would then look like this

for(int i = 0; i < 45; i++)
{
    printf("%02X", (unsigned char)payload[i]);
}
phantom
  • 3,292
  • 13
  • 21
  • 1
    printing control characters or null byte this way might produce strange output, it might be more useful to `printf("%02X", (unsigned char)payload[i]);` or the cout equivalent – M.M Jun 01 '15 at 21:29
0

The way you are using strtok is wrong. If int payloadSize = atoi(strtok(buffer, " ")); gives you payloadSize then you are not going to have anything in temp when you do temp = strtok(NULL, "\n"); because you have moved the buffer pointer to the end in the context of strtok.

Check whats in temp before memcpy

resultsway
  • 12,299
  • 7
  • 36
  • 43