Therefore, your first assignment is to compromise the security of Dave's desktop machine while he is away sitting on the beach, looking at volcanic rocks, and giving his so-called "talk." To do this, you can pursue one (or both) of the following attack strategies:
Of course, this has not stopped people from cracking the password database. One common approach is to use dictionary-based attacks in which every word (and various combinations of words) in a dictionary are run through the encryption algorithm and compared to the entries in the password database. Such a scheme works fairly well if people have chosen common words as their password. Alternatively, you could perform an exhaustive search of all 2^56 (7.2e16) keys--a time consuming task to be sure, but entirely possible given enough computing power and patience.
Only the first 8 characters of a Unix password are significant. To create an encrypted password, the lower 7-bits of each character are combined to create a 56-bit key. In addition, a special two character sequence known as a "salt value" is used to initialize the state of the encryption algorithm (the purpose of which is to discourage the use of hardware DES implementations as a password-cracking tool). To encrypt a password, the char *crypt(char *key, char *salt) function is used by supplying the plain-text password key and a salt value. crypt() returns a pointer to a string containing a printable version of the encrypted key. For example:
To compile the above program, you will probably need to do something like this:#include <crypt.h> #include <stdio.h> /* Print out the encrypted version of a password */ int main(int argc, char *argv[]) { char *key, *salt, *encrypted; key = argv[1]; /* Get key value */ salt = argv[2]; /* Get salt */ encrypted = crypt(key,salt); printf("encrypted : %s\n", encrypted); }
Finally, let's try it out:unix % cc encrypt.c -lcrypt -o encrypt
The only thing to notice about the output is that the salt value always appears as the first two characters of the encrypted password. In your cracking program, you will need to strip off the first two characters of the encrypted password and use them as the salt value given to crypt().unix % encrypt kevin xy encrypted : xynJzgmAhsBOo unix %
Checking to see if a password is valid is pretty similar. Here is a program that checks to see if a plaintext password matches the encrypted version.
This works about the same way:#include <crypt.h> #include <stdio.h> #include <string.h> /* Check if a password is correct */ int main(int argc, char *argv[]) { char *password, *encrypted, salt[3]; password = argv[1]; /* Get the plaintext password */ encrypted = argv[2]; /* Get the encrypted version */ salt[0] = encrypted[0]; /* Pull off the salt value */ salt[1] = encrypted[1]; salt[2] = 0; if (strcmp(crypt(password,salt),encrypted) == 0) { printf("%s is the password!\n", password); } else { printf("%s is not the password. Get lost.\n", password); } }
unix % cc pwcheck.c -lcrypt -o pwcheck unix % pwcheck kevin xynJzgmAhsBOo kevin is the password! unix % pwcheck dave xynJzgmAhsBOo dave is not the password. Get lost. unix %
You should compile the above programs and mess around with them just to get a feel for how they work. You might try them out with your own password just to see if you can reproduce the encrypted version of your password for instance (although some newer systems are using MD5 to encode passwords).
Here's a rough plan on how to tackle this problem:
% crack encrypted_password size start endWhere encrypted_password is the encrypted password, size is the number of letters in the unencrypted password, start is the starting password guess, and end is the ending guess. For example:
Note: This is not the only way to do this and having the above interface is not a strict requirement. Also, remember that the "salt" value is the first two characters of the encrypted password.% crack abO2KZUsQYIAA 2 "00" "zz" The password is "g4" %
If you succeed, I must be able to fully recreate the attack solely from your writeup in order to receive full credit!
/* ----------------------------------------------------------------------------- * server.c * * An echo server. Reads a line of input from a client and echos it back to them. * ----------------------------------------------------------------------------- */ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <netdb.h> #include <fcntl.h> #include <string.h> int readline(int sd, char *buffer, int maxlen) { char *c = buffer; int nread = 0; while (nread < maxlen) { if (read(sd,c,1) < 0) return 0; nread++; if (*(c++) == '\n') break; } *c = 0; return nread; } void echo(int sockfd) { int n; char line[256]; while(1) { /* Read a line of data */ n = readline(sockfd,line,256); if (n == 0) break; /* Write it back to the client */ write(sockfd,line,n); } } /* Open up the socket and listen for connections */ int main(int argc, char **argv) { int sd; /* Socket descriptor */ int port; /* TCP port number */ struct sockaddr_in servaddr; /* Server address */ if (argc != 2) { printf("Usage : server port\n"); exit(1); } port = atoi(argv[1]); /* Open up a socket */ sd = socket(AF_INET, SOCK_STREAM, 0); if (sd < 0) { printf("Unable to open socket!\n"); exit(1); } /* Bind the socket to the specified port number */ memset(&servaddr, 0, sizeof(servaddr)); /* Clear the server address */ servaddr.sin_family = AF_INET; servaddr.sin_port = htons(port); /* Set the port number */ servaddr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { printf("unable to bind to port %d\n", port); exit(1); } /* Allow no more than 5 pending connections */ listen(sd,5); /* Start listening for connections */ printf("Server listening on port %d\n", port); for (;;) { struct sockaddr_in clientaddr; /* Client address */ int len = sizeof(clientaddr); int clientfd; /* Client descriptor */ char message[256]; /* Message buffer */ /* Accept a new connection */ clientfd = accept(sd, (struct sockaddr *) &clientaddr, &len); if (clientfd < 0) { printf("Accept error!\n"); exit(1); } /* Print a message saying where the connection came from */ printf("Received a connection from %s, port %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); /* echo */ echo(clientfd); /* Close the connection */ close(clientfd); printf("Client closed connection\n"); } /* Never reached */ }
/* ----------------------------------------------------------------------------- * client.c * * Echo client. Reads a line of input from the user, sends it to the * server. The server sends it back to us. * ----------------------------------------------------------------------------- */ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <netdb.h> #include <fcntl.h> #include <string.h> int readline(int sd, char *buffer, int maxlen) { char *c = buffer; int nread = 0; while (nread < maxlen) { if (read(sd,c,1) < 0) return 0; nread++; if (*(c++) == '\n') break; } *c = 0; return nread; } int main(int argc, char **argv) { int sd; struct sockaddr_in servaddr; struct hostent *host; char line[256]; if (argc != 3) { printf("usage : client hostname port\n"); exit(1); } /* Create a socket */ sd = socket(AF_INET, SOCK_STREAM, 0); /* Set the address */ memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(atoi(argv[2])); if ((host = gethostbyname(argv[1])) == (NULL)) { printf("Unknown host : %s\n", argv[1]); exit(1); } memcpy(&servaddr.sin_addr,host->h_addr,host->h_length); /* Try to connect to the server */ if (connect(sd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { printf("Unable to connect with the server.\n"); exit(1); } printf("Connected to %s\n", argv[1]); while (1) { int n = readline(0,line,256); /* Read a line of input from user */ if (n == 0) break; write(sd,line,n); /* Send the line to the server */ n = readline(sd,line,256); /* Read it back from the server */ if (n == 0) break; printf("The server said: %s\n", line); } /* Close the socket */ close(sd); exit(0); }