SECURITY

Password Cracking

8/31/2010 3:28:08 PM
password_cracking.html

Passwords aren't generally stored in plaintext form. A file containing all the passwords in plaintext form would be far too attractive a target, so instead, a one-way hash function is used. The best-known of these functions is based on DES and is called crypt(), which is described in the manual page shown below.

NAME
crypt - password and data encryption

SYNOPSIS
#define _XOPEN_SOURCE
#include char *crypt(const char *key, const char *salt); DESCRIPTION crypt() is the password encryption function. It is based on the Data Encryption Standard algorithm with variations intended (among other things) to discourage use of hardware implementations of a key search. key is a user's typed password. salt is a two-character string chosen from the set [a-zA-Z0-9./]. This string is used to perturb the algorithm in one of 4096 different ways.

This is a one-way hash function that expects a plaintext password and a salt value for input, and then outputs a hash with the salt value prepended to it. This hash is mathematically irreversible, meaning that it is impossible to determine the original password using only the hash. Writing a quick program to experiment with this function will help clarify any confusion.

1. Password Cracking

1.1. crypt_test.c
#define _XOPEN_SOURCE
#include #include int main(int argc, char *argv[]) { if(argc < 2) { printf("Usage: %s &amp;lt;salt value&amp;gt;\n", argv[0]);<br> exit(1); <br> }<br> printf("password \"%s\" with salt \"%s\" ", argv[1], argv[2]);<br> printf("hashes to ==&amp;gt; %s\n", crypt(argv[1], argv[2])); <br>}&amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="be linked"&amp;gt;&amp;lt;/a&amp;gt;When this program is compiled, the crypt library needs to be linked. This is shown in the following output, along with some test runs.&amp;lt;a name="idx-CHP-7-1902"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;<br>&amp;lt;pre&amp;gt;reader@hacking:~/booksrc $ gcc -o crypt_test crypt_test.c <br>/tmp/cccrSvYU.o: In function `main':<br>crypt_test.c:(.text+0x73): undefined reference to `crypt'<br>collect2: ld returned 1 exit status<br>reader@hacking:~/booksrc $ gcc -o crypt_test crypt_test.c -l crypt<br>reader@hacking:~/booksrc $ ./crypt_test testing je<br>password "testing" with salt "je" hashes to ==&amp;gt; jeLu9ckBgvgX.<br>reader@hacking:~/booksrc $ ./crypt_test test je<br>password "test" with salt "je" hashes to ==&amp;gt; jeHEAX1m66RV.<br>reader@hacking:~/booksrc $ ./crypt_test test xy<br>password "test" with salt "xy" hashes to ==&amp;gt; xyVSuHLjceD92 <br>reader@hacking:~/booksrc $&amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="two runs"&amp;gt;&amp;lt;/a&amp;gt;Notice that in the last two runs, the same password is encrypted, but using different salt values. The salt value is used to perturb the algorithm further, so there can be multiple hash values &amp;lt;a name="idx-CHP-7-1903"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="are used"&amp;gt;&amp;lt;/a&amp;gt;for the same plaintext value if different salt values are used. The hash value (including the prepended salt) is stored in the password file under the premise that if an attacker were to steal the password file, the hashes would be useless.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="authenticate using"&amp;gt;&amp;lt;/a&amp;gt;When a legitimate user needs to authenticate using the password hash, that user's hash is looked up in the password file. The user is prompted to enter her password, the original salt value is extracted from the password file, and whatever the user types is sent through the same one-way hash function with the salt value. If the correct password was entered, the one-way hashing function will produce the same hash output as is stored in the password file. This allows authentication to function as expected, without ever having to store the plaintext password.&amp;lt;/p&amp;gt;<br><br><br>&amp;lt;a name="dictionary_attacks"&amp;gt;&amp;lt;/a&amp;gt;<br>&amp;lt;h4 id="title-ID0EIKOM" class="docSection2Title"&amp;gt;0x761. Dictionary Attacks&amp;lt;/h4&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="password file"&amp;gt;&amp;lt;/a&amp;gt;It turns out, however, that the encrypted passwords in the password file aren't so useless after all. Sure, it's mathematically impossible to reverse the hash, but it is possible to just quickly hash every word in a dictionary, using the salt value for a specific hash, and then compare the result with that hash. If the hashes match, then that word from the dictionary must be the plaintext password.&amp;lt;a name="idx-CHP-7-1904"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="simple dictionary"&amp;gt;&amp;lt;/a&amp;gt;A simple dictionary attack program can be whipped up fairly easily. It just needs to read words from a file, hash each one using the proper salt value, and display the word if there is a match. The following source code does this using filestream functions, which are included with stdio.h. These functions are easier to work with, since they wrap up the messiness of &amp;lt;tt&amp;gt;open()&amp;lt;/tt&amp;gt; calls and file descriptors, using FILE structure pointers, instead. In the source below, the &amp;lt;tt&amp;gt;fopen()&amp;lt;/tt&amp;gt; call's &amp;lt;tt&amp;gt;r&amp;lt;/tt&amp;gt;&amp;lt;a name="NULL on"&amp;gt;&amp;lt;/a&amp;gt; argument tells it to open the file for reading. It returns NULL on failure, or a pointer to the open filestream. The &amp;lt;tt&amp;gt;fgets()&amp;lt;/tt&amp;gt;&amp;lt;a name="call gets"&amp;gt;&amp;lt;/a&amp;gt; call gets a string from the filestream, up to a maximum length or when it reaches the end of a line. In this case, it's used to read each line from the word-list file. This function also returns NULL on failure, which is used to detect then end of the file.&amp;lt;a name="idx-CHP-7-1905"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="idx-CHP-7-1906"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;<br>&amp;lt;a name="crypt_crackc"&amp;gt;&amp;lt;/a&amp;gt;<br>&amp;lt;h5 id="title-ID0ENLOM" class="docSection3Title"&amp;gt;7.6.2.1. crypt_crack.c&amp;lt;/h5&amp;gt;<br>&amp;lt;div class="codeSegmentsExpansionLinks"&amp;gt;<br> Code View:&amp;lt;/div&amp;gt;&amp;lt;pre class=""&amp;gt;<br>#define _XOPEN_SOURCE<br>#include &amp;lt;unistd.h&amp;gt;<br>#include &amp;lt;stdio.h&amp;gt;<br><br>/* Barf a message and exit. */<br>void barf(char *message, char *extra) {<br> printf(message, extra);<br> exit(1);<br>}<br><br>/* A &amp;lt;a name="idx-CHP-7-1907"&amp;gt;&amp;lt;/a&amp;gt;dictionary attack example program */<br>int main(int argc, char *argv[]) {<br> FILE *wordlist;<br> char *hash, word[30], salt[3];<br> if(argc &amp;lt; 2)<br> barf("Usage: %s &amp;lt;wordlist file&amp;gt; &amp;lt;password hash&amp;gt;\n", argv[0]);<br><br> strncpy(salt, argv[2], 2); // First 2 bytes of hash are the salt.<br> salt[2] = '\0'; // terminate string<br><br> printf("Salt value is \'%s\'\n", salt);<br><br> if( (wordlist = fopen(argv[1], "r")) == NULL) // Open the wordlist.<br> barf("Fatal: couldn't open the file \'%s\'.\n", argv[1]);<br><br> while(fgets(word, 30, wordlist) != NULL) { // Read each word<br> word[strlen(word)-1] = '\0'; // Remove the '\n' byte at the end.<br> hash = crypt(word, salt); // Hash the word using the salt.<br> printf("trying word: %-30s ==&amp;gt; %15s\n", word, hash);<br> if(strcmp(hash, argv[2]) == 0) { // If the hash matches<br> printf("The hash \"%s\" is from the ", argv[2]);<br> printf("plaintext password \"%s\".\n", word);<br> fclose(wordlist);<br> exit(0);<br> }<br> }<br> printf("Couldn't find the plaintext password in the supplied wordlist.\n");<br> fclose(wordlist); <br>}<br><br> &amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="output shows"&amp;gt;&amp;lt;/a&amp;gt;The following output shows this program being used to crack the password hash &amp;lt;span class="docEmphasis"&amp;gt;jeHEAX1m66RV.&amp;lt;/span&amp;gt;&amp;lt;a name="the words"&amp;gt;&amp;lt;/a&amp;gt;, using the words found in /usr/share/dict/words.&amp;lt;/p&amp;gt;<br>&amp;lt;div class="codeSegmentsExpansionLinks"&amp;gt;<br> Code View:&amp;lt;/div&amp;gt;&amp;lt;pre class=""&amp;gt;reader@hacking:~/booksrc $ gcc -o crypt_crack &amp;lt;a name="idx-CHP-7-1908"&amp;gt;&amp;lt;/a&amp;gt;crypt_crack.c -lcrypt<br>reader@hacking:~/booksrc $ ./crypt_crack /usr/share/dict/words jeHEAX1m66RV.<br>Salt value is 'je'<br>trying word: ==&amp;gt; jesS3DmkteZYk<br>trying word: A ==&amp;gt; jeV7uK/S.y/KU<br>trying word: A's ==&amp;gt; jeEcn7sF7jwWU<br>trying word: AOL ==&amp;gt; jeSFGex8ANJDE<br>trying word: AOL's ==&amp;gt; jesSDhacNYUbc<br>trying word: Aachen ==&amp;gt; jeyQc3uB14q1E<br>trying word: Aachen's ==&amp;gt; je7AQSxfhvsyM<br>trying word: Aaliyah ==&amp;gt; je/vAqRJyOZvU<br><br>.:[ output trimmed ]:.<br><br>trying word: terse ==&amp;gt; jelgEmNGLflJ2<br>trying word: tersely ==&amp;gt; jeYfo1aImUWqg<br>trying word: terseness ==&amp;gt; jedH11z6kkEaA<br>trying word: terseness's ==&amp;gt; jedH11z6kkEaA<br>trying word: terser ==&amp;gt; jeXptBe6psF3g<br>trying word: tersest ==&amp;gt; jenhzylhDIqBA<br>trying word: tertiary ==&amp;gt; jex6uKY9AJDto<br>trying word: test ==&amp;gt; jeHEAX1m66RV.<br>The hash "jeHEAX1m66RV." is from the plaintext password "test". <br>reader@hacking:~/booksrc $<br><br> &amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;Since the word &amp;lt;span class="docEmphasis"&amp;gt;test&amp;lt;/span&amp;gt;&amp;lt;a name="in the"&amp;gt;&amp;lt;/a&amp;gt; was the original password and this word is found in the words file, the password hash will eventually be cracked. This is why it's considered poor security practice to use passwords that are &amp;lt;a name="idx-CHP-7-1909"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="dictionary words"&amp;gt;&amp;lt;/a&amp;gt;dictionary words or based on dictionary words.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="original password"&amp;gt;&amp;lt;/a&amp;gt;The downside to this attack is that if the original password isn't a word found in the dictionary file, the password won't be found. For example, if a non-dictionary word such as &amp;lt;tt&amp;gt;h4R%&amp;lt;/tt&amp;gt;&amp;lt;a name="attack won"&amp;gt;&amp;lt;/a&amp;gt; is used as a password, the dictionary attack won't be able to find it:&amp;lt;/p&amp;gt;<br>&amp;lt;div class="codeSegmentsExpansionLinks"&amp;gt;<br> Code View:&amp;lt;/div&amp;gt;&amp;lt;pre class=""&amp;gt;reader@hacking:~/booksrc $ ./crypt_test h4R% je<br>password "h4R%" with salt "je" hashes to ==&amp;gt; jeMqqfIfPNNTE<br>reader@hacking:~/booksrc $ ./crypt_crack /usr/share/dict/words jeMqqfIfPNNTE<br>Salt value is 'je'<br>trying word: ==&amp;gt; jesS3DmkteZYk<br>trying word: A ==&amp;gt; jeV7uK/S.y/KU<br>trying word: A's ==&amp;gt; jeEcn7sF7jwWU<br>trying word: AOL ==&amp;gt; jeSFGex8ANJDE<br>trying word: AOL's ==&amp;gt; jesSDhacNYUbc<br>trying word: Aachen ==&amp;gt; jeyQc3uB14q1E<br>trying word: Aachen's ==&amp;gt; je7AQSxfhvsyM<br>trying word: Aaliyah ==&amp;gt; je/vAqRJyOZvU<br><br>.:[ output trimmed ]:.<br><br>trying word: zooms ==&amp;gt; je8A6DQ87wHHI<br>trying word: zoos ==&amp;gt; jePmCz9ZNPwKU<br>trying word: zucchini ==&amp;gt; jeqZ9LSWt.esI<br>trying word: zucchini's ==&amp;gt; jeqZ9LSWt.esI<br>trying word: zucchinis ==&amp;gt; jeqZ9LSWt.esI<br>trying word: zwieback ==&amp;gt; jezzR3b5zwlys<br>trying word: zwieback's ==&amp;gt; jezzR3b5zwlys<br>trying word: zygote ==&amp;gt; jei5HG7JrfLy6<br>trying word: zygote's ==&amp;gt; jej86M9AG0yj2<br>trying word: zygotes ==&amp;gt; jeWHQebUlxTmo <br>Couldn't find the plaintext password in the supplied wordlist.<br><br> &amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;Custom &amp;lt;a name="idx-CHP-7-1910"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="modifications of"&amp;gt;&amp;lt;/a&amp;gt;dictionary files are often made using different languages, standard modifications of words (such as transforming letters to numbers), or simply appending numbers to the end of each word. While a bigger dictionary will yield more passwords, it will also take more time to process.&amp;lt;/p&amp;gt;<br><br><br>&amp;lt;a name="exhaustive_brute-force_attacks"&amp;gt;&amp;lt;/a&amp;gt;<br>&amp;lt;h4 id="title-ID0EHNOM" class="docSection2Title"&amp;gt;0x762. Exhaustive Brute-Force Attacks&amp;lt;/h4&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="dictionary attack"&amp;gt;&amp;lt;/a&amp;gt;A dictionary attack that tries every single possible combination is an &amp;lt;span class="docEmphasis"&amp;gt;exhaustive brute-force&amp;lt;/span&amp;gt;&amp;lt;a name="crack every"&amp;gt;&amp;lt;/a&amp;gt; attack. While this type of attack will technically be able to crack every conceivable password, it will probably take longer than your grandchildren's grandchildren would be willing to wait.&amp;lt;a name="idx-CHP-7-1911"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="idx-CHP-7-1912"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="With "&amp;gt;&amp;lt;/a&amp;gt;With 95 possible input characters for &amp;lt;tt&amp;gt;crypt()-&amp;lt;/tt&amp;gt;&amp;lt;a name="are "&amp;gt;&amp;lt;/a&amp;gt;style passwords, there are 95&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt;&amp;lt;a name="passwords for"&amp;gt;&amp;lt;/a&amp;gt; possible passwords for an exhaustive search of all eight-character passwords, which works out to be over seven quadrillion possible passwords. This number gets so big so quickly because, as another character is added to the password &amp;lt;a name="idx-CHP-7-1913"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="it would"&amp;gt;&amp;lt;/a&amp;gt;length, the number of possible passwords grows exponentially. Assuming 10,000 cracks per second, it would take about 22,875 years to try every password. Distributing this effort across many machines and processors is one possible approach; however, it is important to remember that this will only achieve a linear speedup. If one thousand machines were combined, each capable of 10,000 cracks per second, the effort would still take over 22 years. The linear speedup achieved by adding another machine is marginal compared to the growth in keyspace when another character is added to the password length.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="growth is"&amp;gt;&amp;lt;/a&amp;gt;Luckily, the inverse of the exponential growth is also true; as characters are removed from the password length, the number of possible passwords decreases exponentially. This means that a four-character password only has 95&amp;lt;sup&amp;gt;4&amp;lt;/sup&amp;gt;&amp;lt;a name="can be"&amp;gt;&amp;lt;/a&amp;gt; possible passwords. This keyspace has only about 84 million possible passwords, which can be exhaustively cracked (assuming 10,000 cracks per second) in a little over two hours. This means that, even though a password like &amp;lt;tt&amp;gt;h4R%&amp;lt;/tt&amp;gt;&amp;lt;a name="in a"&amp;gt;&amp;lt;/a&amp;gt; isn't in any dictionary, it can be cracked in a reasonable amount of time.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="length is"&amp;gt;&amp;lt;/a&amp;gt;This means that, in addition to avoiding dictionary words, password length is also important. Since the complexity scales up exponentially, doubling the length to produce an eight-character password should bring the level of effort required to crack the password into the unreasonable time frame.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="idx-CHP-7-1914"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="Designer has"&amp;gt;&amp;lt;/a&amp;gt;Solar Designer has developed a password-&amp;lt;a name="idx-CHP-7-1915"&amp;gt;&amp;lt;/a&amp;gt;cracking program called &amp;lt;a name="idx-CHP-7-1916"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="then an"&amp;gt;&amp;lt;/a&amp;gt;John the Ripper that uses first a dictionary attack and then an exhaustive brute-force attack. This program is probably the most popular one of its kind; it is available at &amp;lt;a class="docLink" target="_blank" href="http://www.openwall.com/john"&amp;gt;http://www.openwall.com/john&amp;lt;/a&amp;gt;&amp;lt;a name="It has"&amp;gt;&amp;lt;/a&amp;gt;. It has been included on the LiveCD.&amp;lt;a name="idd1e27266"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;<br>&amp;lt;div class="codeSegmentsExpansionLinks"&amp;gt;<br> Code View:&amp;lt;/div&amp;gt;&amp;lt;pre class=""&amp;gt;reader@hacking:~/booksrc $ john<br><br>&amp;lt;a name="idx-CHP-7-1917"&amp;gt;&amp;lt;/a&amp;gt;John the Ripper Version 1.6 Copyright (c) 1996-98 by Solar Designer<br><br>Usage: john [OPTIONS] [PASSWORD-FILES]<br>-single "single crack" mode<br>-wordfile:FILE -stdin wordlist mode, read words from FILE or stdin<br>-rules enable rules for wordlist mode<br>-incremental[:MODE] incremental mode [using section MODE]<br>-external:MODE external mode or word filter<br>-stdout[:LENGTH] no &amp;lt;a name="idx-CHP-7-1918"&amp;gt;&amp;lt;/a&amp;gt;cracking, just write words to stdout<br>-restore[:FILE] restore an interrupted session [from FILE]<br>-session:FILE set session file name to FILE<br>-status[:FILE] print status of a session [from FILE]<br>-makechars:FILE make a charset, FILE will be overwritten<br>-show show cracked passwords<br>-test perform a benchmark<br>-users:[-]LOGIN|UID[,..] load this (these) user(s) only<br>-groups:[-]GID[,..] load users of this (these) group(s) only<br>-shells:[-]SHELL[,..] load users with this (these) shell(s) only<br>-salts:[-]COUNT load salts with at least COUNT passwords only<br>-format:NAME force ciphertext format NAME (DES/BSDI/MD5/BF/AFS/LM)<br>-savemem:LEVEL enable memory saving, at LEVEL 1..3<br>reader@hacking:~/booksrc $ sudo tail -3 /etc/shadow<br>matrix:$1$zCcRXVsm$GdpHxqC9epMrdQcayUx0//:13763:0:99999:7:::<br>jose:$1$pRS4.I8m$Zy5of8AtD800SeMgm.2Yg.:13786:0:99999:7:::<br>reader:U6aMy0wojraho:13764:0:99999:7:::<br>reader@hacking:~/booksrc $ sudo john /etc/shadow<br>Loaded 2 passwords with 2 different salts (FreeBSD MD5 [32/32])<br>guesses: 0 time: 0:00:00:01 0% (2) c/s: 5522 trying: koko<br>guesses: 0 time: 0:00:00:03 6% (2) c/s: 5489 trying: exports<br>guesses: 0 time: 0:00:00:05 10% (2) c/s: 5561 trying: catcat<br>guesses: 0 time: 0:00:00:09 20% (2) c/s: 5514 trying: dilbert!<br>guesses: 0 time: 0:00:00:10 22% (2) c/s: 5513 trying: redrum3<br>testing7 (jose)<br>guesses: 1 time: 0:00:00:14 44% (2) c/s: 5539 trying: KnightKnight<br>guesses: 1 time: 0:00:00:17 59% (2) c/s: 5572 trying: Gofish! <br>Session aborted<br><br> &amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="the account"&amp;gt;&amp;lt;/a&amp;gt;In this output, the account jose is shown to have the password of &amp;lt;tt&amp;gt;testing7&amp;lt;/tt&amp;gt;.&amp;lt;/p&amp;gt;<br><br>&amp;lt;a name="hash_lookup_table"&amp;gt;&amp;lt;/a&amp;gt;<br>&amp;lt;h4 id="title-ID0EFQOM" class="docSection2Title"&amp;gt;0x763. Hash Lookup Table&amp;lt;/h4&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="interesting idea"&amp;gt;&amp;lt;/a&amp;gt;Another interesting idea for password cracking is using a giant &amp;lt;a name="idx-CHP-7-1919"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="passwords were"&amp;gt;&amp;lt;/a&amp;gt;hash lookup table. If all the hashes for all possible passwords were precomputed and stored in a searchable data structure somewhere, any password could be cracked in the time it takes to search. Assuming a binary search, this time would be about O(log&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &amp;lt;span class="docEmphasis"&amp;gt;N&amp;lt;/span&amp;gt;), where &amp;lt;span class="docEmphasis"&amp;gt;N&amp;lt;/span&amp;gt; is the number of entries. Since &amp;lt;span class="docEmphasis"&amp;gt;N&amp;lt;/span&amp;gt; is 95&amp;lt;sup&amp;gt;8&amp;lt;/sup&amp;gt;&amp;lt;a name="works out"&amp;gt;&amp;lt;/a&amp;gt; in the case of eight-character passwords, this works out to about O(8 log&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; 95), which is quite fast.&amp;lt;a name="idx-CHP-7-1920"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="terabytes of"&amp;gt;&amp;lt;/a&amp;gt;However, a hash lookup table like this would require about 100,000 terabytes of storage. In addition, the design of the password-hashing algorithm takes this type of attack into consideration and mitigates it with the salt value. Since multiple plaintext passwords will hash to different password hashes with different salts, a separate lookup table would have to be created for each salt. With the DES-based &amp;lt;tt&amp;gt;crypt()&amp;lt;/tt&amp;gt; function, there are 4,096 possible &amp;lt;a name="idx-CHP-7-1921"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="a smaller"&amp;gt;&amp;lt;/a&amp;gt;salt values, which means that even for a smaller keyspace, such as all possible four-character passwords, a hash lookup table becomes impractical. With a fixed salt, the storage space needed for a single lookup table for all possible four-character passwords is about one gigabyte, but because of the salt values, there are 4,096 possible hashes for a single plaintext password, necessitating 4,096 different tables. This raises the needed &amp;lt;a name="idx-CHP-7-1922"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="such an"&amp;gt;&amp;lt;/a&amp;gt;storage space up to about 4.6 terabytes, which greatly dissuades such an attack.&amp;lt;/p&amp;gt;<br><br>&amp;lt;a name="password_probability_matrix"&amp;gt;&amp;lt;/a&amp;gt;<br>&amp;lt;h4 id="title-ID0EXROM" class="docSection2Title"&amp;gt;0x764. Password Probability Matrix&amp;lt;/h4&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="is a"&amp;gt;&amp;lt;/a&amp;gt;There is a trade-off between &amp;lt;a name="idx-CHP-7-1923"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="seen in"&amp;gt;&amp;lt;/a&amp;gt;computational power and storage space that exists everywhere. This can be seen in the most elementary forms of computer science and everyday life. MP3 files use compression to store a high-quality sound file in a relatively small amount of space, but the demand for computational resources increases. Pocket calculators use this trade-off in the other direction by maintaining a lookup table for functions such as sine and cosine to save the calculator from doing heavy computations.&amp;lt;a name="idx-CHP-7-1924"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="This trade"&amp;gt;&amp;lt;/a&amp;gt;This trade-off can also be applied to cryptography in what has become known as a &amp;lt;a name="idx-CHP-7-1925"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="for this"&amp;gt;&amp;lt;/a&amp;gt;time/space trade-off attack. While Hellman's methods for this type of attack are probably more efficient, the following source code should be easier to understand. The general principle is always the same, though: Try to find the sweet spot between computational power and storage space, so that an exhaustive brute-force attack can be completed in a reasonable amount of time, using a reasonable amount of space. Unfortunately, the dilemma of salts will still present itself, since this method still requires some form of storage. However, there are only 4,096 possible salts with &amp;lt;tt&amp;gt;crypt()&amp;lt;/tt&amp;gt;&amp;lt;a name="so the"&amp;gt;&amp;lt;/a&amp;gt;-style password hashes, so the effect of this problem can be diminished by reducing the needed storage space far enough to remain reasonable despite the 4,096 multiplier.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="having an"&amp;gt;&amp;lt;/a&amp;gt;This method uses a form of lossy compression. Instead of having an exact &amp;lt;a name="idx-CHP-7-1926"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="plaintext values"&amp;gt;&amp;lt;/a&amp;gt;hash lookup table, several thousand possible plaintext values will be returned when a password hash is entered. These values can be checked quickly to converge on the original plaintext password, and the lossy compression allows for a major space reduction. In the demonstration code that follows, the keyspace for all possible four-character passwords (with a fixed salt) is used. The storage space needed is reduced by 88 percent, compared to a full &amp;lt;a name="idx-CHP-7-1927"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="must be"&amp;gt;&amp;lt;/a&amp;gt;hash lookup table (with a fixed salt), and the keyspace that must be brute-forced through is reduced by about 1,018 times. Under the assumption of 10,000 cracks per second, this method can crack any four-character password (with a fixed salt) in under eight seconds, which is a considerable speedup when compared to the two hours needed for an exhaustive bruteforce attack of the same keyspace.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="that correlates"&amp;gt;&amp;lt;/a&amp;gt;This method builds a three-dimensional binary matrix that correlates parts of the hash values with parts of the plaintext values. On the x-axis, the plaintext is split into two pairs: the first two characters and the second two characters. The possible values are enumerated into a binary vector that is 95&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;lt;a name="split into"&amp;gt;&amp;lt;/a&amp;gt;, or 9,025, bits long (about 1,129 bytes). On the y-axis, the ciphertext is split into four three-character chunks. These are enumerated the same way down the columns, but only four bits of the third character are actually used. This means there are 64&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;lt;a name="The z"&amp;gt;&amp;lt;/a&amp;gt;.4, or 16,384, columns. The z-axis exists simply to maintain eight different two-dimensional matrices, so four exist for each of the plaintext pairs.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="paired values"&amp;gt;&amp;lt;/a&amp;gt;The basic idea is to split the plaintext into two paired values that are enumerated along a vector. Every possible plaintext is hashed into ciphertext, and the ciphertext is used to find the appropriate column of the matrix. Then the plaintext enumeration bit across the row of the matrix is turned on. When the ciphertext values are reduced into smaller chunks, collisions are inevitable.&amp;lt;/p&amp;gt;<br>&amp;lt;a name="password_probability_matrixd1e27400"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;p&amp;gt;&amp;lt;table cellspacing="0" border="1" width="100%"&amp;gt;&amp;lt;colgroup span="2"&amp;gt;&amp;lt;col /&amp;gt;&amp;lt;col /&amp;gt;&amp;lt;/colgroup&amp;gt;&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th scope="col" class="docTableCell thead"&amp;gt;Plaintext&amp;lt;/th&amp;gt;&amp;lt;th scope="col" class="docTableCell thead"&amp;gt;Hash&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class="docTableCell"&amp;gt;test&amp;lt;/td&amp;gt;&amp;lt;td class="docTableCell"&amp;gt;je&amp;lt;span class="docEmphStrong"&amp;gt;HEA&amp;lt;/span&amp;gt;X1m66RV.&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class="docTableCell"&amp;gt;!J)h&amp;lt;/td&amp;gt;&amp;lt;td class="docTableCell"&amp;gt;je&amp;lt;span class="docEmphStrong"&amp;gt;HEA&amp;lt;/span&amp;gt;38vqlkkQ&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class="docTableCell"&amp;gt;".F+&amp;lt;/td&amp;gt;&amp;lt;td class="docTableCell"&amp;gt;je&amp;lt;span class="docEmphStrong"&amp;gt;HEA&amp;lt;/span&amp;gt;1Tbde5FE&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td class="docTableCell"&amp;gt;"8,J&amp;lt;/td&amp;gt;&amp;lt;td class="docTableCell"&amp;gt;je&amp;lt;span class="docEmphStrong"&amp;gt;HEA&amp;lt;/span&amp;gt;nX8kQK3I&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;br /&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="In this"&amp;gt;&amp;lt;/a&amp;gt;In this case, the column for &amp;lt;tt&amp;gt;HEA&amp;lt;/tt&amp;gt;&amp;lt;a name="the bits"&amp;gt;&amp;lt;/a&amp;gt; would have the bits corresponding to the plaintext pairs &amp;lt;tt&amp;gt;te, !J,&amp;lt;/tt&amp;gt; "., and &amp;lt;tt&amp;gt;"8&amp;lt;/tt&amp;gt;&amp;lt;a name="as these"&amp;gt;&amp;lt;/a&amp;gt; turned on, as these plaintext/hash pairs are added to the matrix.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="such as"&amp;gt;&amp;lt;/a&amp;gt;After the matrix is completely filled out, when a hash such as &amp;lt;tt&amp;gt;jeHEA38vqlkkQ&amp;lt;/tt&amp;gt; is entered, the column for &amp;lt;tt&amp;gt;HEA&amp;lt;/tt&amp;gt;&amp;lt;a name="matrix will"&amp;gt;&amp;lt;/a&amp;gt; will be looked up, and the two-dimensional matrix will return the values &amp;lt;tt&amp;gt;te, !J&amp;lt;/tt&amp;gt;, "., and &amp;lt;tt&amp;gt;"8&amp;lt;/tt&amp;gt;&amp;lt;a name="four matrices"&amp;gt;&amp;lt;/a&amp;gt; for the first two characters of the plaintext. There are four matrices like this for the first two characters, using ciphertext substring from characters 2 through 4, 4 through 6, 6 though 8, and 8 though 10, each with a different vector of possible first two-character plaintext values. Each vector is pulled, and they are combined with a bitwise AND. This will leave only those bits turned on that correspond to the plaintext pairs listed as possibilities for each substring of ciphertext. There are also four matrices like this for the last two characters of plaintext.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="determined by"&amp;gt;&amp;lt;/a&amp;gt;The sizes of the matrices were determined by the &amp;lt;a name="idx-CHP-7-1928"&amp;gt;&amp;lt;/a&amp;gt;pigeonhole principle. This is a simple principle that states: If &amp;lt;span class="docEmphasis"&amp;gt;k&amp;lt;/span&amp;gt; + 1 objects are put into &amp;lt;span class="docEmphasis"&amp;gt;k&amp;lt;/span&amp;gt;&amp;lt;a name="two objects"&amp;gt;&amp;lt;/a&amp;gt; boxes, at least one of the boxes will contain two objects. So, to get the best results, the goal is for each vector to be a little bit less than half full of 1s. Since 95&amp;lt;sup&amp;gt;4&amp;lt;/sup&amp;gt;&amp;lt;a name="entries will"&amp;gt;&amp;lt;/a&amp;gt;, or 81,450,625, entries will be put in the matrices, there need to be about twice as many holes to achieve 50 percent saturation. Since each vector has 9,025 entries, there should be about (95&amp;lt;sup&amp;gt;4&amp;lt;/sup&amp;gt;&amp;lt;a name="three characters"&amp;gt;&amp;lt;/a&amp;gt; &#183; 2) / 9025 columns. This works out to be about 18,000 columns. Since ciphertext substrings of three characters are being used for the columns, the first two characters and four bits from the third character are used to provide 64&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;lt;a name="or about"&amp;gt;&amp;lt;/a&amp;gt; &#183; 4, or about 16 thousand columns (there are only 64 possible values for each character of ciphertext hash). This should be close enough, because when a bit is added twice, the overlap is ignored. In practice, each vector turns out to be about 42 percent saturated with 1s.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="are four"&amp;gt;&amp;lt;/a&amp;gt;Since there are four vectors that are pulled for a single ciphertext, the &amp;lt;a name="idx-CHP-7-1929"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="of any"&amp;gt;&amp;lt;/a&amp;gt;probability of any one enumeration position having a 1 value in each vector is about 0.42&amp;lt;sup&amp;gt;4&amp;lt;/sup&amp;gt;&amp;lt;a name="first two"&amp;gt;&amp;lt;/a&amp;gt;, or about 3.11 percent. This means that, on average, the 9,025 possibilities for the first two characters of plaintext are reduced by about 97 percent to 280 possibilities. This is also done for the last two characters, providing about 280&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&amp;lt;a name="assumption of"&amp;gt;&amp;lt;/a&amp;gt;, or 78,400, possible plaintext values. Under the assumption of 10,000 cracks per second, this reduced keyspace would take under 8 seconds to check.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="to create"&amp;gt;&amp;lt;/a&amp;gt;Of course, there are downsides. First, it takes at least as long to create the matrix as the original brute-force attack would have taken; however, this is a one-time cost. Also, the salts still tend to prohibit any type of storage attack, even with the reduced storage-space requirements.&amp;lt;/p&amp;gt;<br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="can be"&amp;gt;&amp;lt;/a&amp;gt;The following two source code listings can be used to create a &amp;lt;a name="idx-CHP-7-1930"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="first listing"&amp;gt;&amp;lt;/a&amp;gt;password probability matrix and crack passwords with it. The first listing will generate a matrix that can be used to crack all possible four-character passwords salted with &amp;lt;tt&amp;gt;je&amp;lt;/tt&amp;gt;&amp;lt;a name="second listing"&amp;gt;&amp;lt;/a&amp;gt;. The second listing will use the generated matrix to actually do the password &amp;lt;a name="idx-CHP-7-1931"&amp;gt;&amp;lt;/a&amp;gt;cracking.&amp;lt;/p&amp;gt;<br>&amp;lt;a name="ppm_genc"&amp;gt;&amp;lt;/a&amp;gt;<br>&amp;lt;h5 id="title-ID0EMYOM" class="docSection3Title"&amp;gt;7.6.5.1. ppm_gen.c&amp;lt;/h5&amp;gt;<br>&amp;lt;div class="codeSegmentsExpansionLinks"&amp;gt;<br> Code View:&amp;lt;/div&amp;gt;&amp;lt;pre class=""&amp;gt;/*********************************************************\<br>* Password Probability Matrix * File: ppm_gen.c *<br>***********************************************************<br>* *<br>* Author: Jon Erickson &amp;lt;matrix@phiral.com&amp;gt; *<br>* Organization: Phiral Research Laboratories *<br>* *<br>* This is the generate program for the PPM proof of *<br>* concept. It generates a file called 4char.ppm, which *<br>* contains information regarding all possible 4- *<br>* character passwords salted with 'je'. This file can *<br>* be used to quickly crack passwords found within this *<br>* keyspace with the corresponding ppm_crack.c program. *<br>* *<br>\*********************************************************/<br><br>#define _XOPEN_SOURCE<br>#include &amp;lt;unistd.h&amp;gt;<br>#include &amp;lt;stdio.h&amp;gt;<br>#include &amp;lt;stdlib.h&amp;gt;<br><br>#define HEIGHT 16384<br>#define WIDTH 1129<br>#define DEPTH 8<br>#define SIZE HEIGHT * WIDTH * DEPTH<br><br>/* Map a single hash byte to an enumerated value. */<br>int enum_hashbyte(char a) {<br> int i, j;<br> i = (int)a;<br> if((i &amp;gt;= 46) &amp;amp;&amp;amp; (i &amp;lt;= 57))<br> j = i - 46;<br> else if ((i &amp;gt;= 65) &amp;amp;&amp;amp; (i &amp;lt;= 90))<br> j = i - 53;<br> else if ((i &amp;gt;= 97) &amp;amp;&amp;amp; (i &amp;lt;= 122))<br> j = i - 59;<br> return j;<br>}<br><br>/* Map 3 hash bytes to an enumerated value. */<br>int enum_hashtriplet(char a, char b, char c) {<br> return (((enum_hashbyte(c)%4)*4096)+(enum_hashbyte(a)*64)+enum_hashbyte(b));<br>}<br>/* Barf a message and exit. */<br>void barf(char *message, char *extra) {<br> printf(message, extra);<br> exit(1);<br>}<br><br>/* Generate a 4-char.ppm file with all possible 4-char &amp;lt;a name="idx-CHP-7-1932"&amp;gt;&amp;lt;/a&amp;gt;passwords (salted w/ je). */<br>int main() {<br> char plain[5];<br> char *code, *data;<br> int i, j, k, l;<br> unsigned int charval, val;<br> FILE *handle;<br> if (!(handle = fopen("4char.ppm", "w")))<br> barf("Error: Couldn't open file '4char.ppm' for writing.\n", NULL);<br><br> data = (char *) malloc(SIZE);<br> if (!(data))<br> barf("Error: Couldn't allocate memory.\n", NULL);<br><br> for(i=32; i&amp;lt;127; i++) {<br> for(j=32; j&amp;lt;127; j++) {<br> printf("Adding %c%c** to 4char.ppm..\n", i, j);<br> for(k=32; k&amp;lt;127; k++) {<br> for(l=32; l&amp;lt;127; l++) {<br><br> plain[0] = (char)i; // Build every<br> plain[1] = (char)j; // possible 4-byte<br> plain[2] = (char)k; // password.<br> plain[3] = (char)l;<br> plain[4] = '\0';<br> code = crypt((const char *)plain, (const char *)"je"); // Hash it.<br><br> /* Lossfully store statistical info about the pairings. */<br> val = enum_hashtriplet(code[2], code[3], code[4]); // Store info about<br> bytes 2-4.<br><br> charval = (i-32)*95 + (j-32); // First 2 plaintext bytes<br> data[(val*WIDTH)+(charval/8)] |= (1&amp;lt;&amp;lt;(charval%8));<br> val += (HEIGHT * 4);<br> charval = (k-32)*95 + (l-32); // Last 2 plaintext bytes<br> data[(val*WIDTH)+(charval/8)] |= (1&amp;lt;&amp;lt;(charval%8));<br><br> val = HEIGHT + enum_hashtriplet(code[4], code[5], code[6]); // bytes 4-6<br> charval = (i-32)*95 + (j-32); // First 2 plaintext bytes<br> data[(val*WIDTH)+(charval/8)] |= (1&amp;lt;&amp;lt;(charval%8));<br> val += (HEIGHT * 4);<br> charval = (k-32)*95 + (l-32); // Last 2 plaintext bytes<br> data[(val*WIDTH)+(charval/8)] |= (1&amp;lt;&amp;lt;(charval%8));<br><br> val = (2 * HEIGHT) + enum_hashtriplet(code[6], code[7], code[8]); //<br> bytes 6-8<br> charval = (i-32)*95 + (j-32); // First 2 plaintext bytes<br> data[(val*WIDTH)+(charval/8)] |= (1&amp;lt;&amp;lt;(charval%8));<br> val += (HEIGHT * 4);<br> charval = (k-32)*95 + (l-32); // Last 2 plaintext bytes<br> data[(val*WIDTH)+(charval/8)] |= (1&amp;lt;&amp;lt;(charval%8));<br><br> val = (3 * HEIGHT) + enum_hashtriplet(code[8], code[9], code[10]);<br> // bytes 8-10<br> charval = (i-32)*95 + (j-32); // First 2 plaintext chars<br> data[(val*WIDTH)+(charval/8)] |= (1&amp;lt;&amp;lt;(charval%8));<br> val += (HEIGHT * 4);<br> charval = (k-32)*95 + (l-32); // Last 2 plaintext bytes<br> data[(val*WIDTH)+(charval/8)] |= (1&amp;lt;&amp;lt;(charval%8));<br> }<br> }<br> }<br> }<br> printf("finished.. saving..\n");<br> fwrite(data, SIZE, 1, handle);<br> free(data);<br> fclose(handle); <br>}<br><br> &amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;The first piece of code, &amp;lt;a name="idx-CHP-7-1933"&amp;gt;&amp;lt;/a&amp;gt;ppm_gen.c, can be used to generate a fourcharacter &amp;lt;a name="idx-CHP-7-1934"&amp;gt;&amp;lt;/a&amp;gt;password probability matrix, as shown in the output below. The &amp;lt;tt&amp;gt;-O3&amp;lt;/tt&amp;gt;&amp;lt;a name="for speed"&amp;gt;&amp;lt;/a&amp;gt; option passed to GCC tells it to optimize the code for speed when it compiles.&amp;lt;a name="idx-CHP-7-1935"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;<br>&amp;lt;pre&amp;gt;reader@hacking:~/booksrc $ gcc -O3 -o ppm_gen ppm_gen.c -lcrypt<br>reader@hacking:~/booksrc $ ./ppm_gen<br>Adding ** to 4char.ppm..<br>Adding !** to 4char.ppm..<br>Adding "** to 4char.ppm..<br><br>.:[ output trimmed ]:.<br><br>Adding ~|** to 4char.ppm..<br>Adding ~}** to 4char.ppm..<br>Adding ~~** to 4char.ppm..<br>finished.. saving..<br>@hacking:~ $ ls -lh 4char.ppm<br>-rw-r--r-- 1 142M 2007-09-30 13:56 4char.ppm<br>reader@hacking:~/booksrc $&amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="The "&amp;gt;&amp;lt;/a&amp;gt;The 142MB 4char.ppm file contains loose associations between the plaintext and hash data for every possible four-character password. This data can then be used by this next program to quickly crack four-character passwords that would foil a dictionary attack.&amp;lt;/p&amp;gt;<br><br>&amp;lt;a name="ppm_crackc"&amp;gt;&amp;lt;/a&amp;gt;<br>&amp;lt;h5 id="title-ID0EVZOM" class="docSection3Title"&amp;gt;7.6.5.2. ppm_crack.c&amp;lt;/h5&amp;gt;<br>&amp;lt;div class="codeSegmentsExpansionLinks"&amp;gt;<br> Code View:&amp;lt;/div&amp;gt;&amp;lt;pre class=""&amp;gt;/*********************************************************\<br>* Password Probability Matrix * File: ppm_crack.c *<br>***********************************************************<br>* *<br>* Author: Jon Erickson &amp;lt;matrix@phiral.com&amp;gt; *<br>* Organization: Phiral Research Laboratories *<br>* *<br>* This is the crack &amp;lt;a name="idx-CHP-7-1936"&amp;gt;&amp;lt;/a&amp;gt;program for the PPM proof of concept.*<br>* It uses an existing file called 4char.ppm, which *<br>* contains information regarding all possible 4- *<br>* character &amp;lt;a name="idx-CHP-7-1937"&amp;gt;&amp;lt;/a&amp;gt;passwords salted with 'je'. This file can *<br>* be generated with the corresponding ppm_gen.c program. *<br>* *<br>\*********************************************************/<br><br>#define _XOPEN_SOURCE<br>#include &amp;lt;unistd.h&amp;gt;<br>#include &amp;lt;stdio.h&amp;gt;<br>#include &amp;lt;stdlib.h&amp;gt;<br><br>#define HEIGHT 16384<br>#define WIDTH 1129<br>#define DEPTH 8<br>#define SIZE HEIGHT * WIDTH * DEPTH<br>#define DCM HEIGHT * WIDTH<br><br>/* Map a single hash byte to an enumerated value. */<br>int enum_hashbyte(char a) {<br> int i, j;<br> i = (int)a;<br> if((i &amp;gt;= 46) &amp;amp;&amp;amp; (i &amp;lt;= 57))<br> j = i - 46;<br> else if ((i &amp;gt;= 65) &amp;amp;&amp;amp; (i &amp;lt;= 90))<br> j = i - 53;<br> else if ((i &amp;gt;= 97) &amp;amp;&amp;amp; (i &amp;lt;= 122))<br> j = i - 59;<br> return j;<br>}<br><br>/* Map 3 hash bytes to an enumerated value. */<br>int enum_hashtriplet(char a, char b, char c) {<br> return (((enum_hashbyte(c)%4)*4096)+(enum_hashbyte(a)*64)+enum_hashbyte(b));<br>}<br><br>/* Merge two vectors. */<br>void merge(char *vector1, char *vector2) {<br> int i;<br> for(i=0; i &amp;lt; WIDTH; i++)<br> vector1[i] &amp;amp;= vector2[i];<br>}<br><br>/* Returns the bit in the vector at the passed index position */<br>int get_vector_bit(char *vector, int index) {<br> return ((vector[(index/8)]&amp;amp;(1&amp;lt;&amp;lt;(index%8)))&amp;gt;&amp;gt;(index%8));<br>}<br><br>/* Counts the number of plaintext pairs in the passed vector */<br>int count_vector_bits(char *vector) {<br> int i, count=0;<br> for(i=0; i &amp;lt; 9025; i++)<br> count += get_vector_bit(vector, i);<br> return count;<br>}<br><br>/* Print the plaintext pairs that each ON bit in the vector enumerates. */<br>void print_vector(char *vector) {<br> int i, a, b, val;<br> for(i=0; i &amp;lt; 9025; i++) {<br> if(get_vector_bit(vector, i) == 1) { // If bit is on,<br> a = i / 95; // calculate the<br> b = i - (a * 95); // plaintext pair<br> printf("%c%c ",a+32, b+32); // and print it.<br> }<br> }<br> printf("\n");<br>}<br><br>/* Barf a message and exit. */<br>void barf(char *message, char *extra) {<br> printf(message, extra);<br> exit(1);<br>}<br><br>/* Crack a 4-character &amp;lt;a name="idx-CHP-7-1938"&amp;gt;&amp;lt;/a&amp;gt;password using generated 4char.ppm file. */<br>int main(int argc, char *argv[]) {<br> char *pass, plain[5];<br> unsigned char bin_vector1[WIDTH], bin_vector2[WIDTH], temp_vector[WIDTH];<br> char prob_vector1[2][9025];<br> char prob_vector2[2][9025];<br> int a, b, i, j, len, pv1_len=0, pv2_len=0;<br> FILE *fd;<br><br> if(argc &amp;lt; 1)<br> barf("Usage: %s &amp;lt;password hash&amp;gt; (will use the file 4char.ppm)\n", argv[0]);<br><br> if(!(fd = fopen("4char.ppm", "r")))<br> barf("Fatal: Couldn't open PPM file for reading.\n", NULL);<br><br> pass = argv[1]; // First argument is password hash<br><br> printf("Filtering possible plaintext bytes for the first two characters:\n");<br><br> fseek(fd,(DCM*0)+enum_hashtriplet(pass[2], pass[3], pass[4])*WIDTH, SEEK_SET);<br> fread(bin_vector1, WIDTH, 1, fd); // Read the vector associating bytes 2-4 of hash.<br><br> len = count_vector_bits(bin_vector1);<br> printf("only 1 vector of 4:\t%d plaintext pairs, with %0.2f%% saturation\n", len,<br> len*100.0/<br>9025.0);<br><br> fseek(fd,(DCM*1)+enum_hashtriplet(pass[4], pass[5], pass[6])*WIDTH, SEEK_SET);<br> fread(temp_vector, WIDTH, 1, fd); // Read the vector associating bytes 4-6 of hash.<br> merge(bin_vector1, temp_vector); // Merge it with the first vector.<br><br> len = count_vector_bits(bin_vector1);<br> printf("vectors 1 AND 2 merged:\t%d plaintext pairs, with %0.2f%% saturation\n", len, <br>len*100.0/9025.0);<br><br> fseek(fd,(DCM*2)+enum_hashtriplet(pass[6], pass[7], pass[8])*WIDTH, SEEK_SET);<br> fread(temp_vector, WIDTH, 1, fd); // Read the vector associating bytes 6-8 of hash.<br> merge(bin_vector1, temp_vector); // Merge it with the first two vectors.<br><br> len = count_vector_bits(bin_vector1);<br> printf("first 3 vectors merged:\t%d plaintext pairs, with %0.2f%% saturation\n", len, <br>len*100.0/9025.0);<br><br> fseek(fd,(DCM*3)+enum_hashtriplet(pass[8], pass[9],pass[10])*WIDTH, SEEK_SET);<br> fread(temp_vector, WIDTH, 1, fd); // Read the vector associatind bytes 8-10 of hash.<br> merge(bin_vector1, temp_vector); // Merge it with the othes vectors.<br><br> len = count_vector_bits(bin_vector1);<br> printf("all 4 vectors merged:\t%d plaintext pairs, with %0.2f%% saturation\n", len, <br>len*100.0/9025.0);<br><br> printf("Possible plaintext pairs for the first two bytes:\n");<br> print_vector(bin_vector1);<br><br> printf("\nFiltering possible plaintext bytes for the last two characters:\n");<br><br> fseek(fd,(DCM*4)+enum_hashtriplet(pass[2], pass[3], pass[4])*WIDTH, SEEK_SET);<br> fread(bin_vector2, WIDTH, 1, fd); // Read the vector associating bytes 2-4 of hash.<br><br> len = count_vector_bits(bin_vector2);<br> printf("only 1 vector of 4:\t%d plaintext pairs, with %0.2f%% saturation\n", len,<br> len*100.0/<br>9025.0);<br><br> fseek(fd,(DCM*5)+enum_hashtriplet(pass[4], pass[5], pass[6])*WIDTH, SEEK_SET);<br> fread(temp_vector, WIDTH, 1, fd); // Read the vector associating bytes 4-6 of hash.<br> merge(bin_vector2, temp_vector); // Merge it with the first vector.<br><br> len = count_vector_bits(bin_vector2);<br> printf("vectors 1 AND 2 merged:\t%d plaintext pairs, with %0.2f%% saturation\n", len, <br>len*100.0/9025.0);<br><br> fseek(fd,(DCM*6)+enum_hashtriplet(pass[6], pass[7], pass[8])*WIDTH, SEEK_SET);<br> fread(temp_vector, WIDTH, 1, fd); // Read the vector associating bytes 6-8 of hash.<br> merge(bin_vector2, temp_vector); // Merge it with the first two vectors.<br><br> len = count_vector_bits(bin_vector2);<br> printf("first 3 vectors merged:\t%d plaintext pairs, with %0.2f%% saturation\n", len, <br>len*100.0/9025.0);<br><br> fseek(fd,(DCM*7)+enum_hashtriplet(pass[8], pass[9],pass[10])*WIDTH, SEEK_SET);<br> fread(temp_vector, WIDTH, 1, fd); // Read the vector associatind bytes 8-10 of hash.<br> merge(bin_vector2, temp_vector); // Merge it with the othes vectors.<br><br> len = count_vector_bits(bin_vector2);<br> printf("all 4 vectors merged:\t%d plaintext pairs, with %0.2f%% saturation\n", len, <br>len*100.0/9025.0);<br><br> printf("Possible plaintext pairs for the last two bytes:\n");<br> print_vector(bin_vector2);<br> printf("Building &amp;lt;a name="idx-CHP-7-1939"&amp;gt;&amp;lt;/a&amp;gt;probability vectors...\n");<br> for(i=0; i &amp;lt; 9025; i++) { // Find possible first two plaintext bytes.<br> if(get_vector_bit(bin_vector1, i)==1) {;<br> prob_vector1[0][pv1_len] = i / 95;<br> prob_vector1[1][pv1_len] = i - (prob_vector1[0][pv1_len] * 95);<br> pv1_len++;<br> }<br> }<br> for(i=0; i &amp;lt; 9025; i++) { // Find possible last two plaintext bytes.<br> if(get_vector_bit(bin_vector2, i)) {<br> prob_vector2[0][pv2_len] = i / 95;<br> prob_vector2[1][pv2_len] = i - (prob_vector2[0][pv2_len] * 95);<br> pv2_len++;<br> }<br> }<br><br> printf("&amp;lt;a name="idx-CHP-7-1940"&amp;gt;&amp;lt;/a&amp;gt;Cracking remaining %d possibilites..\n", pv1_len*pv2_len);<br> for(i=0; i &amp;lt; pv1_len; i++) {<br> for(j=0; j &amp;lt; pv2_len; j++) {<br> plain[0] = prob_vector1[0][i] + 32;<br> plain[1] = prob_vector1[1][i] + 32;<br> plain[2] = prob_vector2[0][j] + 32;<br> plain[3] = prob_vector2[1][j] + 32;<br> plain[4] = 0;<br> if(strcmp(crypt(plain, "je"), pass) == 0) {<br> printf("&amp;lt;a name="idx-CHP-7-1941"&amp;gt;&amp;lt;/a&amp;gt;Password : %s\n", plain);<br> i = 31337;<br> j = 31337;<br> }<br> }<br> }<br> if(i &amp;lt; 31337)<br> printf("Password wasn't salted with 'je' or is not 4 chars long.\n");<br><br> fclose(fd); <br>}<br><br> &amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="piece of"&amp;gt;&amp;lt;/a&amp;gt;The second piece of code, &amp;lt;a name="idx-CHP-7-1942"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="be used"&amp;gt;&amp;lt;/a&amp;gt;ppm_crack.c, can be used to crack the troublesome password of &amp;lt;tt&amp;gt;h4R%&amp;lt;/tt&amp;gt; in a matter of seconds:&amp;lt;a name="idx-CHP-7-1943"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;<br>&amp;lt;div class="codeSegmentsExpansionLinks"&amp;gt;<br> Code View:&amp;lt;/div&amp;gt;&amp;lt;pre class=""&amp;gt;reader@hacking:~/booksrc $ ./crypt_test h4R% je<br>password "h4R%" with salt "je" hashes to ==&amp;gt; jeMqqfIfPNNTE<br>reader@hacking:~/booksrc $ gcc -O3 -o ppm_crack ppm_crack.c -lcrypt<br>reader@hacking:~/booksrc $ ./ppm_crack jeMqqfIfPNNTE<br>Filtering possible plaintext bytes for the first two characters:<br>only 1 vector of 4: 3801 plaintext pairs, with 42.12% saturation<br>vectors 1 AND 2 merged: 1666 plaintext pairs, with 18.46% saturation<br>first 3 vectors merged: 695 plaintext pairs, with 7.70% saturation<br>all 4 vectors merged: 287 plaintext pairs, with 3.18% saturation<br>Possible plaintext pairs for the first two bytes:<br> 4 9 N !&amp;amp; !M !Q "/ "5 "W #K #d #g #p $K $O $s %) %Z %\ %r &amp;amp;( &amp;amp;T '- '0 '7 'D<br>'F ( (v (| )+ ). )E )W *c *p *q *t *x +C -5 -A -[ -a .% .D .S .f /t 02 07 0? <br>0e 0{ 0| 1A 1U 1V 1Z 1d 2V 2e 2q 3P 3a 3k 3m 4E 4M 4P 4X 4f 6 6, 6C 7: 7@ 7S <br>7z 8F 8H 9R 9U 9_ 9~ :- :q :s ;G ;J ;Z ;k &amp;lt;! &amp;lt;8 =! =3 =H =L =N =Y &amp;gt;V &amp;gt;X ?1 @#<br>@W @v @| AO B/ B0 BO Bz C( D8 D&amp;gt; E8 EZ F@ G&amp;amp; G? Gj Gy H4 I@ J JN JT JU Jh Jq <br>Ks Ku M) M{ N, N: NC NF NQ Ny O/ O[ P9 Pc Q! QA Qi Qv RA Sg Sv T0 Te U&amp;amp; U&amp;gt; UO <br>VT V[ V] Vc Vg Vi W: WG X" X6 XZ X` Xp YT YV Y^ Yl Yy Y{ Za [$ [* [9 [m [z \" \<br>+ \C \O \w ]( ]: ]@ ]w _K _j `q a. aN a^ ae au b: bG bP cE cP dU d] e! fI fv g! <br>gG h+ h4 hc iI iT iV iZ in k. kp l5 l` lm lq m, m= mE n0 nD nQ n~ o# o: o^ p0 <br>p1 pC pc q* q0 qQ q{ rA rY s" sD sz tK tw u- v$ v. v3 v; v_ vi vo wP wt x" x&amp;amp; <br>x+ x1 xQ xX xi yN yo zO zP zU z[ z^ zf zi zr zt {- {B {a |s }) }+ }? }y ~L ~m <br><br>Filtering possible plaintext bytes for the last two characters:<br>only 1 vector of 4: 3821 plaintext pairs, with 42.34% saturation<br>vectors 1 AND 2 merged: 1677 plaintext pairs, with 18.58% saturation<br>first 3 vectors merged: 713 plaintext pairs, with 7.90% saturation<br>all 4 vectors merged: 297 plaintext pairs, with 3.29% saturation<br>Possible plaintext pairs for the last two bytes:<br> ! &amp;amp; != !H !I !K !P !X !o !~ "r "{ "} #% #0 $5 $] %K %M %T &amp;amp;" &amp;amp;% &amp;amp;( &amp;amp;0 &amp;amp;4 &amp;amp;I <br>&amp;amp;q &amp;amp;} 'B 'Q 'd )j )w *I *] *e *j *k *o *w *| +B +W ,' ,J ,V -z . .$ .T /' /_ <br>0Y 0i 0s 1! 1= 1l 1v 2- 2/ 2g 2k 3n 4K 4Y 4\ 4y 5- 5M 5O 5} 6+ 62 6E 6j 7* 74 <br>8E 9Q 9\ 9a 9b :8 :; :A :H :S :w ;" ;&amp;amp; ;L &amp;lt;L &amp;lt;m &amp;lt;r &amp;lt;u =, =4 =v &amp;gt;v &amp;gt;x ?&amp;amp; ?` ?j <br>?w @0 A* B B@ BT C8 CF CJ CN C} D+ D? DK Dc EM EQ FZ GO GR H) Hj I: I&amp;gt; J( J+ <br>J3 J6 Jm K# K) K@ L, L1 LT N* NW N` O= O[ Ot P: P\ Ps Q- Qa R% RJ RS S3 Sa T! <br>T$ T@ TR T_ Th U" U1 V* V{ W3 Wy Wz X% X* Y* Y? Yw Z7 Za Zh Zi Zm [F \( \3 \5 \<br>_ \a \b \| ]$ ]. ]2 ]? ]d ^[ ^~ `1 `F `f `y a8 a= aI aK az b, b- bS bz c( cg dB <br>e, eF eJ eK eu fT fW fo g( g&amp;gt; gW g\ h$ h9 h: h@ hk i? jN ji jn k= kj l7 lo m&amp;lt; <br>m= mT me m| m} n% n? n~ o oF oG oM p" p9 p\ q} r6 r= rB sA sN s{ s~ tX tp u <br>u2 uQ uU uk v# vG vV vW vl w* w&amp;gt; wD wv x2 xA y: y= y? yM yU yX zK zv {# {) {= <br>{O {m |I |Z }. }; }d ~+ ~C ~a <br>Building probability vectors...<br>&amp;lt;a name="idx-CHP-7-1944"&amp;gt;&amp;lt;/a&amp;gt;Cracking remaining 85239 possibilites..<br>&amp;lt;a name="idx-CHP-7-1945"&amp;gt;&amp;lt;/a&amp;gt;Password : h4R%<br>reader@hacking:~/booksrc $&amp;lt;a name="idx-CHP-7-1946"&amp;gt;&amp;lt;/a&amp;gt;<br><br> &amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;<br><br>&amp;lt;p class="docText"&amp;gt;&amp;lt;a name="diffusion provided"&amp;gt;&amp;lt;/a&amp;gt;These programs are proof-of-concept hacks, which take advantage of the bit diffusion provided by hash functions. There are other time-space trade-off attacks, and some have become quite popular. &amp;lt;a name="idx-CHP-7-1947"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;a name="has support"&amp;gt;&amp;lt;/a&amp;gt;RainbowCrack is a popular tool, which has support for multiple algorithms. If you want to learn more, consult the Internet.&amp;lt;/p&amp;gt;<br><br><br><br>&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;<br>&amp;lt;table width="100%" border="0" cellspacing="0" cellpadding="0"&amp;gt;<br>&amp;lt;tr&amp;gt;&amp;lt;td align="right"&amp;gt;&amp;lt;div STYLE="MARGIN-LEFT: 0.15in;"&amp;gt;<br>&amp;lt;a href=hybrid_ciphers.html&amp;gt;&amp;lt;img src="http://programming4.us/image/82010/31/15284432.jpg" width="20" height="20" border="0" align="absmiddle" alt="Previous Page"&amp;gt;&amp;lt;/a&amp;gt;<br>&amp;lt;a href=wireless_80211b_encryption.html&amp;gt;&amp;lt;img src="http://programming4.us/image/82010/31/15285432.jpg" width="20" height="20" border="0" align="absmiddle" alt="Next Page"&amp;gt;&amp;lt;/a&amp;gt;<br>&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;<br>&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/plaintext&gt;&lt;/stdio.h&gt;&lt;/unistd.h&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</plaintext></stdio.h></unistd.h></pre></div></div> <script type="text/javascript"> var sc_project=11388663; var sc_invisible=1; var sc_security="7db37af3"; var scJsHost = (("https:" == document.location.protocol) ? "https://secure." : "http://www."); document.write("<sc"+"ript type='text/javascript' src='" + scJsHost+ "statcounter.com/counter/counter.js'></"+"script>"); </script> <noscript><div class="statcounter"><a title="site stats" href="http://statcounter.com/" target="_blank"><img class="statcounter" src="//c.statcounter.com/11388663/0/7db37af3/1/" alt="site stats"></a></div></noscript> </body></html></span></h5> </td> </tr> <tr><td height="2px"></td></tr> <tr><td> <table id="ctl00_ContentPlaceHolder1_table2" cellpadding="0" cellspacing="0" align="left"> <tr> <td valign="top"> </td> </tr> </table> </td></tr> <tr><td height="3px"></td></tr> <tr> <td> </td> </tr> <tr><td height="2px"></td></tr> <tr> <td><table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td><table width="100%" height="25px" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="100px" bgcolor="#FFFFFF" style="padding-left:5px"><span class="f11blue">Other</span></td> <td background="http://programming4.us/images/bg-ch.gif">&nbsp;</td> </tr> </table></td> </tr> <tr> <td style="padding:5px"> <div> <table id="ctl00_ContentPlaceHolder1_DataList2" cellspacing="0" border="0" style="width:100%;border-collapse:collapse;"> <tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/670.aspx' class="a4"> <li>&nbsp;Wireless 802.11b Encryption</li></a> </td> </tr> </table> </td> </tr><tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/668.aspx' class="a4"> <li>&nbsp;Host-Based Security in Windows Vista</li></a> </td> </tr> </table> </td> </tr><tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/666.aspx' class="a4"> <li>&nbsp;Where Windows Malware Hides</li></a> </td> </tr> </table> </td> </tr><tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/664.aspx' class="a4"> <li>&nbsp;Post-Boot Startup in Windows Vista</li></a> </td> </tr> </table> </td> </tr><tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/662.aspx' class="a4"> <li>&nbsp;Windows Vista Services that You Need Understand</li></a> </td> </tr> </table> </td> </tr><tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/660.aspx' class="a4"> <li>&nbsp;Registry in Windows Vista</li></a> </td> </tr> </table> </td> </tr><tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/658.aspx' class="a4"> <li>&nbsp;Logon Authentication in Windows Vista</li></a> </td> </tr> </table> </td> </tr><tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/656.aspx' class="a4"> <li>&nbsp;Access Control in Windows Vista</li></a> </td> </tr> </table> </td> </tr><tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/654.aspx' class="a4"> <li>&nbsp;User Account Control in Windows Vista: Basic</li></a> </td> </tr> </table> </td> </tr><tr> <td> <table> <tr> <td valign="top" align="left"> <a href='http://programming4.us/security/652.aspx' class="a4"> <li>&nbsp;User Account Control Is More Than You Think</li></a> </td> </tr> </table> </td> </tr> </table> </div></td> </tr> </table></td> </tr> </table> </div> </td></tr> </table> </form> </td> </tr> <tr> <td>&nbsp;</td> </tr> </table></td> </tr> </table></td> </tr> </table></td> <td width="10px"></td> <td width="300px" valign="top"> <table border="0" cellspacing="0" cellpadding="0" width="300px"> <tr><td div align="left"> <form action="http://programming4.us/search.aspx" id="cse-search-box"> <div> <input type="hidden" name="cx" value="partner-pub-9195818004687059:s40plvb67rq" /> <input type="hidden" name="cof" value="FORID:10" /> <input type="hidden" name="ie" value="ISO-8859-1" /> <input type="text" name="q" size="32" /> <input type="submit" name="sa" value="Search" /> </div> </form> <script type="text/javascript" src="http://www.google.com/cse/brand?form=cse-search-box&amp;lang=en"></script> </td></tr> <tr><td align="center"> <div id="fb-root"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/all.js#xfbml=1"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script> </center> <center><div class="fb-like-box" data-href="https://www.facebook.com/programming4us" data-width="320px" data-height="30px" data-colorscheme="light" data-show-faces="true" data-header="true" data-stream="false" data-show-border="true"></center> </td></tr> <tr><td height="2px"></td></tr> <tr><td valign="top" align="center"> </td></tr> <tr><td valign="top"> <table><tr> <td> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="12px"><img src="http://programming4.us/images/t-left.png" width="12" height="28" /></td> <td background="http://programming4.us/images/t-bg.png"><div align="center"><span class="f11trang">Top 10</span></div></td> <td width="12px"><img src="http://programming4.us/images/t-right.png" width="12" height="28" /></td> </tr> </table></td> </tr> <tr> <td class="bor2-1"> <table id="ctl00_ContentPlaceHolder1_Mainright_DataList1" cellspacing="0" border="0" style="width:100%;border-collapse:collapse;"> <tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/camera/25368.aspx' > Review : Sigma 24mm f/1.4 DG HSM Art</a></td></tr> </table> </td> </tr><tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/camera/25366.aspx' > Review : Canon EF11-24mm f/4L USM</a></td></tr> </table> </td> </tr><tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/multimedia/25364.aspx' > Review : Creative Sound Blaster Roar 2</a></td></tr> </table> </td> </tr><tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/multimedia/25362.aspx' > Review : Philips Fidelio M2L</a></td></tr> </table> </td> </tr><tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/laptop/25360.aspx' > Review : Alienware 17 - Dell's Alienware laptops</a></td></tr> </table> </td> </tr><tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/multimedia/25358.aspx' > Review Smartwatch : Wellograph</a></td></tr> </table> </td> </tr><tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/mobile/25356.aspx' > Review : Xiaomi Redmi 2</a></td></tr> </table> </td> </tr><tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/website/25354.aspx' > Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator</a></td></tr> </table> </td> </tr><tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/website/25352.aspx' > Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator</a></td></tr> </table> </td> </tr><tr> <td> <table ><tr><td valign="top" > -&nbsp;<a class="a5" href='http://programming4.us/mobile/25350.aspx' > 3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart</a></td></tr> </table> </td> </tr> </table> </td> </tr> <tr> <td><img src="http://programming4.us/images/t-bttom.png" width="320" height="10" /></td> </tr> </table> </td> </tr></table> </td></tr> <tr><td height="2px"></td></tr> <tr><td height="2px"></td></tr> <tr> <td> <table border="0" cellspacing="0" cellpadding="0" valign="top" height="600px"> <tr> <td> <script type="text/javascript"> var googletag = googletag || {}; googletag.cmd = googletag.cmd || []; (function() { var gads = document.createElement("script"); gads.async = true; gads.type = "text/javascript"; var useSSL = "https:" == document.location.protocol; gads.src = (useSSL ? "https:" : "http:") + "//www.googletagservices.com/tag/js/gpt.js"; var node =document.getElementsByTagName("script")[0]; })(); </script> <div id=""> <script type='text/javascript'> googletag.cmd.push(function() { googletag.defineSlot('/8456/IDG.Programming4us.com_Homepage', [160,600],'') .addService(googletag.pubads()) .setTargeting("pos", "ATF"); googletag.enableServices(); googletag.display(''); }); </script> </div> </td> <td width="2px"> </td> <td > <script type="text/javascript"> var googletag = googletag || {}; googletag.cmd = googletag.cmd || []; (function() { var gads = document.createElement("script"); gads.async = true; gads.type = "text/javascript"; var useSSL = "https:" == document.location.protocol; gads.src = (useSSL ? "https:" : "http:") + "//www.googletagservices.com/tag/js/gpt.js"; var node =document.getElementsByTagName("script")[0]; })(); </script> <div id=""> <script type='text/javascript'> googletag.cmd.push(function() { googletag.defineSlot('/8456/IDG.Programming4us.com_Homepage', [160,600],'') .addService(googletag.pubads()) .setTargeting("pos", "BTF"); googletag.enableServices(); googletag.display(''); }); </script> </div> </td> </tr> </table> </td></tr> <tr><td valign="top"> <table><tr> <td> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="12px"><img src="http://programming4.us/images/t-left.png" width="12" height="28" /></td> <td background="http://programming4.us/images/t-bg.png"><div align="center"><span class="f11trang">REVIEW</span></div></td> <td width="12px"><img src="http://programming4.us/images/t-right.png" width="12" height="28" /></td> </tr> </table></td> </tr> <tr> <td class="bor2-1"> - <a href='http://programming4.us/multimedia/25282.aspx' class="a14">First look: Apple Watch</a> <br /> <br /> - <a href='http://programming4.us/Mobile/25348.aspx' class="a13">3 Tips for Maintaining Your Cell Phone Battery (part 1)</a> <br /> <br /> - <a href='http://programming4.us/Mobile/25350.aspx' class="a13">3 Tips for Maintaining Your Cell Phone Battery (part 2)</a> </td> </tr> <tr> <td><img src="http://programming4.us/images/t-bttom.png" width="320" height="10" /></td> </tr> </table> </td> </tr></table> </td></tr> <tr><td height="2px"></td></tr> <tr><td valign="top"> <table><tr> <td> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="12px"><img src="http://programming4.us/images/t-left.png" width="12" height="28" /></td> <td background="http://programming4.us/images/t-bg.png"><div align="center"><span class="f11trang">VIDEO TUTORIAL</span></div></td> <td width="12px"><img src="http://programming4.us/images/t-right.png" width="12" height="28" /></td> </tr> </table></td> </tr> <tr> <td class="bor2-1"> - <a href='http://guides.programming4.us/office/Video-tutorial---How-to-create-your-first-Swimlane-Diagram-or-Cross-Functional-Flowchart-Diagram-by-using-Microsoft-Visio-2010-%28Part-1%29.aspx' class="a13" target="_blank">How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1) </a> <br /> <br /> - <a href='http://guides.programming4.us/office/Video-tutorial---How-to-create-your-first-Swimlane-Diagram-or-Cross-Functional-Flowchart-Diagram-by-using-Microsoft-Visio-2010-%28Part-2%29.aspx' class="a13">How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)</a> <br /> <br /> - <a href='http://guides.programming4.us/office/Video-tutorial---How-to-create-your-first-Swimlane-Diagram-or-Cross-Functional-Flowchart-Diagram-by-using-Microsoft-Visio-2010-%28Part-3%29.aspx' class="a13">How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)</a> </td> </tr> <tr> <td><img src="http://programming4.us/images/t-bttom.png" width="320" height="10" /></td> </tr> </table> </td> </tr></table> </td></tr> <tr><td height="2px"></td></tr> <tr> <td> <script type="text/javascript"> var googletag = googletag || {}; googletag.cmd = googletag.cmd || []; (function() { var gads = document.createElement("script"); gads.async = true; gads.type = "text/javascript"; var useSSL = "https:" == document.location.protocol; gads.src = (useSSL ? "https:" : "http:") + "//www.googletagservices.com/tag/js/gpt.js"; var node =document.getElementsByTagName("script")[0]; })(); </script> <div id=""> <script type='text/javascript'> googletag.cmd.push(function() { googletag.defineSlot('/8456/IDG.Programming4us.com_Homepage', [300,250],'') .addService(googletag.pubads()) .setTargeting("pos", "BTF"); googletag.enableServices(); googletag.display(''); }); </script> </div> </td></tr> <tr><td height="2px"></td></tr> <tr><td valign="top"> <table><tr> <td> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="12px"><img src="http://programming4.us/images/t-left.png" width="12" height="28" /></td> <td background="http://programming4.us/images/t-bg.png"><div align="center"><span class="f11trang">Popular Tags</span></div></td> <td width="12px"><img src="http://programming4.us/images/t-right.png" width="12" height="28" /></td> </tr> </table></td> </tr> <tr> <td class="bor2-1"> <a href='http://tutorial.programming4.us/tag/microsoft access.aspx' class="a13">Microsoft Access</a> <a href='http://tutorial.programming4.us/tag/microsoft excel.aspx' class="a14">Microsoft Excel</a> <a href='http://tutorial.programming4.us/tag/microsoft onenote.aspx' class="a13">Microsoft OneNote</a> <a href='http://tutorial.programming4.us/tag/microsoft powerpoint.aspx' class="a13">Microsoft PowerPoint</a> <a href='http://tutorial.programming4.us/tag/microsoft project.aspx' class="a13">Microsoft Project</a> <a href='http://tutorial.programming4.us/tag/microsoft visio.aspx' class="a14">Microsoft Visio</a> <a href='http://tutorial.programming4.us/tag/microsoft word.aspx' class="a13">Microsoft Word</a> <a href='http://tutorial.programming4.us/tag/active directory.aspx' class="a13">Active Directory</a> <a href='http://tutorial.programming4.us/tag/biztalk.aspx' class="a13">Biztalk</a> <a href='http://tutorial.programming4.us/tag/exchange server.aspx' class="a14">Exchange Server</a> <a href='http://tutorial.programming4.us/tag/microsoft lync Server.aspx' class="a13">Microsoft LynC Server</a> <a href='http://tutorial.programming4.us/tag/microsoft dynamic.aspx' class="a13">Microsoft Dynamic</a> <a href='http://tutorial.programming4.us/tag/sharepoint.aspx' class="a14">Sharepoint</a> <a href='http://tutorial.programming4.us/tag/sql server.aspx' class="a13">Sql Server</a> <a href='http://tutorial.programming4.us/tag/windows server 2008.aspx' claSs="a13">Windows Server 2008</a> <a href='http://tutorial.programming4.us/tag/windows server 2012.aspx' claSs="a14">Windows Server 2012</a> <a href='http://tutorial.programming4.us/tag/Windows 7.aspx' class="a13">Windows 7</a> <a href='http://tutorial.programming4.us/tag/Windows 8.aspx' class="a14">Windows 8</a> <a href='http://tutorial.programming4.us/tag/adobe indesign.aspx' class="a13">Adobe Indesign</a> <a href='http://tutorial.programming4.us/tag/Adobe Flash Professional.aspx' class="a13">Adobe Flash Professional</a> <a href='http://tutorial.programming4.us/tag/Adobe Dreamweaver.aspx' class="a14">Dreamweaver</a> <a href='http://tutorial.programming4.us/tag/Adobe Illustrator.aspx' class="a13">Adobe Illustrator</a> <a href='http://tutorial.programming4.us/tag/Adobe After Effects.aspx' class="a13">Adobe After Effects</a> <a href='http://tutorial.programming4.us/tag/Adobe Photoshop.aspx' class="a14">Adobe Photoshop</a> <a href='http://tutorial.programming4.us/tag/Adobe Fireworks.aspx' class="a13">Adobe Fireworks</a> <a href='http://tutorial.programming4.us/tag/Adobe Flash Catalyst.aspx' class="a13">Adobe Flash Catalyst</a> <a href='http://tutorial.programming4.us/tag/Corel Painter X.aspx' class="a13">Corel Painter X</a> <a href='http://tutorial.programming4.us/tag/CorelDRAW X5.aspx' class="a14">CorelDRAW X5</a> <a href='http://tutorial.programming4.us/tag/CorelDraw 10.aspx' class="a13">CorelDraw 10</a> <a href='http://tutorial.programming4.us/tag/QuarkXPress 8.aspx' class="a13">QuarkXPress 8</a> <a href='http://tutorial.programming4.us/tag/windows phone 7.aspx' class="a13">windows Phone 7</a> <a href='http://tutorial.programming4.us/tag/windows phone 8.aspx' class="a13">windows Phone 8</a> </td> </tr> <tr> <td><img src="http://programming4.us/images/t-bttom.png" width="320" height="10" /></td> </tr> </table> </td> </tr></table> </td></tr> </table> </td> </tr> </table> </td> </tr> <tr><td height="2px"></td></tr> <tr><td> <div align="center"> <script type="text/javascript"> var googletag = googletag || {}; googletag.cmd = googletag.cmd || []; (function() { var gads = document.createElement("script"); gads.async = true; gads.type = "text/javascript"; var useSSL = "https:" == document.location.protocol; gads.src = (useSSL ? "https:" : "http:") + "//www.googletagservices.com/tag/js/gpt.js"; var node =document.getElementsByTagName("script")[0]; })(); </script> <div id="div-gpt-ad-5725088434512-0"> <script type='text/javascript'> googletag.cmd.push(function() { googletag.defineSlot('/8456/IDG.Programming4us.com_Homepage/IDG.Programming4us.com_Article', [728,90],'div-gpt-ad-5725088434512-0') .addService(googletag.pubads()) .setTargeting("pos", "BTF"); googletag.enableServices(); googletag.display('div-gpt-ad-5725088434512-0'); }); </script> </div> </div> </td></tr> <tr> <td><table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="8px"><img src="http://programming4.us/images/bo-left-mnu.png" width="8" height="40" /></td> <td background="http://programming4.us/images/blockdefault.gif" height="40px"><table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td>&nbsp;</td> <td width="690px"><table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td><div id="ddtabs" class="solidblockmenu"> <ul> <li><a href="http://programming4.us/security.aspx">Security</a></li> <li><a href="http://programming4.us/website.aspx" rel="sb3">Website</a></li> <li><a href="http://programming4.us/software.aspx">Software</a></li> <li><a href="http://programming4.us/Imaging_devices.aspx">Imaging devices</a></li> <li><a href="http://faq.programming4.us/microsoft.aspx" rel="sb1">Microsoft</a></li> <li><a href="http://faq.programming4.us/software.aspx" rel="sb1">FAQ Software</a></li> <li><a href="mailto:admin@programming4.us" rel="sb1">Contact</a></li> </ul> </div></td> </tr> </table></td> <td>&nbsp;</td> </tr> </table></td> <td width="8px"><img src="http://programming4.us/images/bo-right-mnu.png" width="8" height="40" /></td> </tr> </table></td> </tr> </table></td> <td>&nbsp;</td> </tr> </table> <!-- Quantcast Tag --> <script type="text/javascript"> var _qevents = _qevents || []; (function() { var elem = document.createElement('script'); elem.src = (document.location.protocol == "https:" ? "https://secure" : "http://edge") + ".quantserve.com/quant.js"; elem.async = true; elem.type = "text/javascript"; var scpt = document.getElementsByTagName('script')[0]; scpt.parentNode.insertBefore(elem, scpt); })(); _qevents.push({ qacct:"p-sx3Hdyg4wnj7u" }); </script> <noscript> <div style="display:none;"> <img src="//pixel.quantserve.com/pixel/p-sx3Hdyg4wnj7u.gif" border="0" height="1" width="1" alt="Quantcast"/> </div> </noscript> <!-- End Quantcast tag --> <script type="text/javascript"> var sc_project=11388663; var sc_invisible=1; var sc_security="7db37af3"; var scJsHost = (("https:" == document.location.protocol) ? "https://secure." : "http://www."); document.write("<sc"+"ript type='text/javascript' src='" + scJsHost+ "statcounter.com/counter/counter.js'></"+"script>"); </script> <noscript><div class="statcounter"><a title="site stats" href="http://statcounter.com/" target="_blank"><img class="statcounter" src="//c.statcounter.com/11388663/0/7db37af3/1/" alt="site stats"></a></div></noscript> </body> </html>