我正在尝试创建一个程序来演示如何发生像heartbleed这样的问题。这就是我到目前为止所做的:
#include <stdio.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#define LPORT 5555
#define ACCEPT_QUEUE_SIZE 10
typedef struct sockaddr_in sockaddr_in;
typedef struct sockaddr sockaddr;
const char *login_msg = "=== Welcome to super secure admin control panel! ===\nPlease enter the password:";
const char *login_len_msg = "\nPlease enter the length of the password you entered: ";
const char *incorrect_password_msg = "\nACCESS DENIED USING PASSWORD: ";
const char *correct_password_msg = "\nACCESS GRANTED.\n";
int sockfd = 0;
sockaddr_in serv_addr;
void connection_handler(int connfd, const char *secret_password, char *client_address) {
pid_t my_pid = fork();
if (my_pid < 0) {
fprintf(stderr, "[!] Failed to fork a new process for connection handler\n");
exit(EXIT_FAILURE);
}
if (my_pid == 0) {
if (1) {
send(connfd, login_msg, strlen(login_msg), 0);
char user_resp[51], secret_key[10];
char user_resp_len_msg[6];
int user_resp_len = 0;
bzero(&user_resp, sizeof(user_resp));
bzero(&user_resp_len_msg, sizeof(user_resp_len_msg));
recv(connfd, &user_resp, 50, 0);
fprintf(stderr, "[*] Client %s sent password %s\n", client_address, user_resp);
send(connfd, login_len_msg, strlen(login_len_msg), 0);
recv(connfd, &user_resp_len_msg, 5, 0);
fprintf(stderr, "[*] Client %s sent password length string %s\n", client_address, user_resp_len_msg);
sscanf(user_resp_len_msg, "%d\n", &user_resp_len);
if (strncmp(secret_password, user_resp, strlen(secret_password)) == 0) {
fprintf(stderr, "[+] %s successfully completed the challenge\n", client_address);
send(connfd, correct_password_msg, strlen(correct_password_msg), 0);
}
else {
send(connfd, incorrect_password_msg, strlen(incorrect_password_msg), 0);
send(connfd, user_resp, user_resp_len, 0);
}
close(connfd);
exit(EXIT_SUCCESS);
}
}
void interrupt_handler(int signum) {
fprintf(stderr, "[*] Caught interrupt signal, dying\n");
close(sockfd);
exit(EXIT_SUCCESS);
}
int main(int argc, const char * argv[]) {
if (argc != 2) {
fprintf(stderr, "[!] Usage: %s [server password]\n", argv[0]);
exit(EXIT_FAILURE);
}
setpgrp();
signal(SIGHUP, SIG_IGN);
signal(SIGINT, interrupt_handler);
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "[!] Failed fo create socket\n");
exit(EXIT_FAILURE);
}
int optval = 1;
if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) != 0) {
fprintf(stderr, "[!] Failed to set SO_REUSEADDR option on socket\n");
exit(EXIT_FAILURE);
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(LPORT);
if (bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) {
fprintf(stderr, "[!] Failed to bind to port %d\n", LPORT);
exit(EXIT_FAILURE);
}
if (listen(sockfd, ACCEPT_QUEUE_SIZE) != 0) {
fprintf(stderr, "[!] Failed to listen on port %d with queue size %d\n", LPORT, ACCEPT_QUEUE_SIZE);
exit(EXIT_FAILURE);
}
sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
char client_addr_str[INET_ADDRSTRLEN];
while (1) {
bzero(&client_addr, sizeof(client_addr));
bzero(&client_addr_str, sizeof(client_addr_str));
int connfd = accept(sockfd, (sockaddr*)&client_addr, &client_addr_len);
inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_addr_str, client_addr_len);
fprintf(stderr, "[*] Got new connection from %s\n", client_addr_str);
connection_handler(connfd, argv[1], client_addr_str);
}
return 0;
}
它以./program [password]
运行。在OSX上,我注意到argv泄露了,所以我决定密码应该去的地方。但是,当我在ubuntu上测试时,情况并非如此。有没有一种简单的方法可以确保内存的相邻部分中存在两个缓冲区?
我认识到预测未定义的行为非常困难,并且可能没有简单的答案。