Я пытаюсь сделать простую реализацию ошибки Heartbleed в C / C ++ поверх Linux (используя ElementaryOS на vmplayer). Исходя из моего понимания ошибки «heartbleed», она подразумевает, что клиент отправляет на сервер запрос сердцебиения, указывая больший размер для включенной полезной нагрузки, чем фактический размер полезной нагрузки, что приводит к тому, что сервер включает содержимое из памяти, которое будет размещено после локального буфера полезной нагрузки, в ответе.
То, что я пока имею, является клиент-серверным приложением. Клиент подключается к серверу и отправляет конфиденциальную информацию, в данном случае пароль. Затем клиент отправляет на сервер запрос сердцебиения, указывая неправильный размер для полезной нагрузки. Я объявляю свои локальные переменные следующим образом:
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];
Я получаю содержимое из сердцебиения, как это:
int payloadSize = atoi(strtok(buffer, " "));
temp = strtok(NULL, "\n");
В запросе сердцебиения полезной нагрузкой является «полезная нагрузка», а ее размер равен 45. Затем я вызываю функцию memcpy () следующим образом:
memcpy(payload, temp, payloadSize /* 45 in this case */);
Я ожидаю, что переменная полезной нагрузки будет содержать значение «полезная нагрузка», а затем содержимое из переменной пароля. Однако полезная нагрузка содержит только значение «полезная нагрузка».
cout<<payload<<endl; //prints payload
Кто-нибудь может указать, что я делаю не так?
Весь код:
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;
}
Строки в стиле C заканчиваются нулевым ограничителем (\0
), длина буфера, в котором они хранятся, неизвестна cout
, Когда вы используете memcpy
он копирует нулевой терминатор и все, что находится за пределами строки. Буфер фактически содержит все, что находится за строкой, но это после нулевого терминатора, поэтому он не считается частью строки, что означает, что он не печатается cout
,
Полезная нагрузка буфера может выглядеть так
{ 'p', 'a', 'y', 'l', 'o', 'a', 'd', '\0', 'g', 'a', 'r', 'b', 'a', 'g', 'e' }
^^^^ String ends here
Поэтому, когда вы печатаете его с помощью cout
он печатает только до \0
персонаж. Смысл это будет печатать payload
и ничего больше.
Если вы хотите распечатать все значения в буфере, вам придется распечатать их по одному. Вы могли бы сделать что-то вроде этого:
for(int i = 0; i < 45; i++)
{
cout << payload[i];
}
cout << endl;
который выведет все отдельные символы буфера на консоль.
Поскольку печать специальных символов может привести к странным выводам, вы можете распечатать числовые значения символов. Чтобы напечатать числовые значения байтов, вы можете сделать то, что @MattMcNabb предложил использовать printf. Код будет выглядеть так
for(int i = 0; i < 45; i++)
{
printf("%02X", (unsigned char)payload[i]);
}
Как вы используете strtok
неправильно. Если int payloadSize = atoi(strtok(buffer, " "));
дает тебе payloadSize
тогда вы не будете иметь ничего в temp
когда вы делаете temp = strtok(NULL, "\n");
потому что вы переместили указатель буфера до конца в контексте strtok
,
Проверьте что в temp перед memcpy