Search code examples
c++linuxbuffer-overflowheartbleed-bugnetwork-security

memcpy() not working as expected


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;
}

Solution

  • 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]);
    }