[My contribution is mostly providing a higher probability word list based on the user's request, thus significantly reducing the password check time. Let me call it a modified brute-force enumeration. It's no longer the original brute-force, using all possible characters, which would take longer time to finish than necessary.] Retrieve Oracle Password Have you ever got calls from your users asking what their password is? They don't remember it and don't want to change it, because changing it may break something they can't think of for now. There're many ways you can help them, such as (1) Allow them to login through proxy (until they can remember their password): create user tmp_proxy identified by tmp_proxy_passwd; alter user usr_forgt_passwd grant connect through tmp_proxy; connect tmp_proxy[usr_forgt_passwd]/tmp_proxy_passwd (2) You as DBA do the work for them, prefix the schema's object with schema name, or alter session set current_schema to save typing. For tasks only that user can do (submit a dbms job, create a private database link, etc.), create a procedure in his schema to do the work, execute the procedure and drop it. (3) Quickly change the password, login, and quickly change back based on password hash (dba_users.password or sys.user$.password and in newer versions, user$.spare4 as well). (4) Help the users find who else could use this account and so know the password, by checking SQL history (dba_hist_xxx, v$sql), audit log (dba_audit_trail), listener log, etc. Search company Intranet web site or sites. Believe it or not, there may be passwords accidentally uploaded by a stressed-out developer or DBA, especially at a technology company. (If you do find it, of course you need to take some action.) But all those are temporary solutions, except (4), which is not a solution per se. In any case, the users or developers still want and really want you to help, as if you had the superpower to get the clear-text password back. In spite of controversy,[note1] I think this is a perfectly legitimate case where password cracking offers an alternative solution to a business problem in real life. There're many password cracking tools on the Internet. I'm using Orabf as an example: http://www.toolcrypt.org/tools/orabf/index.html http://www.vulnerabilityassessment.co.uk/orabf.htm BF here means brute force, enumerating all characters whose type you specify and computing their hash values to compare with the one you see in data dictionary. However, there's one enhancement I'd like to make. The usage of orabf is such that you either provide a list of possible passwords in a file, or specify one of 6 types of possible characters that make up the password, such as all-digits, or all-letters, or alphanumeric, or alphanumeric plus a pre-determined set of punctuation marks, etc. The enhancement needed by Orabf, or any password cracking program currently on the Internet, is customization of the set of candidate passwords based on common password pattern used by users. For instance, users, at least those I have worked with, have these patterns: 1. Password does not start with a digit or punctuation mark. 2. Does not end with a punctuation mark except "!". 3. Does not use "`", "~", "(", ")", "^", "<", ">", "[", "]", "{", "}", "/", "\". Of course, "does not" may be qualified to be "has a lower probability", so that backtick "`" is much more unlikely used than "^" (most users don't even know the backtick symbol), "x" is not as likely as "e" (on average, a password is a somewhat modified English word). If we keep these common-sense rules, or rather, personal habit, in mind, allowing Orabf or any brute force tool to enumerate those character combinations that we know won't be or are very unlikely to be our users' password is a big waste of computing time. Ideally, such program would allow me to specify the password pattern with a regular expression, for instance, '^[a-z][a-z0-9#$!]{1,5}[a-z0-9!]$' (i.e., a letter, followed by an alphanumeric or #,$,!, which may repeat up to 4 more times, followed by an an alphanumeric or !). But incorporating such string generation capability based on regexp is a tall order.[note2] So, here's a much simpler solution: generate our candidate passwords on our own, using a simple program, let's call it gen_string.pl. #!perl -w #gen_string.pl: Generate fixed length strings, each character of which is set in here foreach $c1 ('a'..'z') { foreach $c2 ('a'..'z') { #foreach $c3 ('a'..'z','0'..'9','!','$') { foreach $c3 ('a'..'z','0'..'9') { #foreach $c4 ('a'..'z','0'..'9','!','$') { foreach $c4 ('a'..'z','0'..'9') { #foreach $c5 ('a'..'z','0'..'9','!','$') { foreach $c5 ('a'..'z','0'..'9') { print "$c1$c2$c3$c4$c5\n"; } } } } } This little Perl program generates strings of 5 characters long, the first 2 being letters only, the remaining 3 being alphanumerics (letters or digits). You can switch the commented with uncommented lines to include ! and $ if you think the password may contain these characters. Listen to the user anyway; he may say "I don't remember the password but I know for sure there's no dollar sign, but there may be an exclamation,...". So modify this progam accordingly. Since Orabf runs on Windows, run this Perl program on Windows if it has Perl interpreter. If there's Oracle10g client (or server), the path is %oracle_home%\perl\5.8.3\bin\MSWin32-x86-multi-thread\perl.exe You can redirect the output to a file, e.g. guess.txt. ---------------------------------------------------------------------------------------------------------------- C:\TEMP>d:\oracle\product\10.2.0\client_1\perl\5.8.3\bin\MSWin32-x86-multi-thread\perl gen_string.pl > guess.txt ---------------------------------------------------------------------------------------------------------------- For the above 5-char password, the file size will be about 220MB. Then run Orabf as follows ( where 8212B29904CE7372 is the password hash value (dba_users.password) and test is the username): ---------------------------------------------------------------------------------------------------------------- C:\TEMP>orabf 8212B29904CE7372:test -c guess.txt orabf v0.7.6, (C)2005 orm@toolcrypt.org --------------------------------------- Trying default passwords...done Starting wordlist session (# = 1,000,000 words) ####################### password found: TEST:TEST1 23258728 passwords tried. elapsed time 00:00:49. t/s:472408 ---------------------------------------------------------------------------------------------------------------- It takes 49 seconds to correctly retrieve the 5-character password test1 for user test on this dual 2.8 GHz Intel Pentium 4 Xeon (Hyper-Threaded) server. Alternatively, you can pipe the output of the Perl program to Orabf to save disk space. --------------------------------------------------------------------------------------------------------------------------------------- C:\TEMP>d:\oracle\product\10.2.0\client_1\perl\5.8.3\bin\MSWin32-x86-multi-thread\perl gen_string.pl | orabf 8212B29904CE7372:test -c - orabf v0.7.6, (C)2005 orm@toolcrypt.org --------------------------------------- Trying default passwords...done Starting wordlist session (# = 1,000,000 words) ####################### password found: TEST:TEST1 23258728 passwords tried. elapsed time 00:00:51. t/s:451215 --------------------------------------------------------------------------------------------------------------------------------------- Note that the elapsed time 51 seconds is only 2 seconds longer than the previous one using a pre-created password file. Obviously, you'll have higher CPU usage during this period because both Perl and Orabf are aggressively consuming CPU time. But you saved the time to separately create the file of candidate password list. If you expand the Perl program to generate 6-char words, the pre-created file would be about 7GB in size! In that case, it makes little sense to not use the pipe method. With exponentially longer running time, it's better to modify the Perl program to generate partial list one "chunk" at a time. That's a good way to have multiple machines run part of the whole character range at the same time. The Perl program can simply change the first for-line from foreach $c1 ('a'..'z') { to foreach $c1 ('a'..'m') { and run this on one box. Then on the second box, change it to foreach $c1 ('n'..'z') { You get the idea. A more meaningful change is based on the user's probability guess. If he says "I don't think we used any non-alphanumeric in the password so don't include that for now", then you can omit any non-alphanumeric in the character list. After running through the whole list without success, he says "maybe there's one single $ somewhere". Then add one $ to the list but remove the all pure-letter combinations. This is a little tricky. Basically, you change one level of for-loop, to foreach $c ('$') { and leave other lines intact. Run once. If it fails to find the password, restore this for-loop, change another for-loop. Run again, and so on. Hope you get the idea. Q&A: * What hardware and software is needed to run the program? Any hardware that runs Microsoft Windows, because Orabf is a Windows program, the faster CPU, the better, preferably with 2 CPUs (or more), with one running Orabf, the other Perl, the remaining if any running OS routine processes. Perl is needed for my program. (But if you don't have Perl and don't want to install it, you can take a few minutes to rewrite my Perl script in VBScript which is guaranteed available on Windows.) Only orabf.exe, Perl interpreter, and the little Perl script are needed. Oracle server, or Oracle client itself, is NOT needed. * What if I don't know the length of the password? It's true that the user almost never can say how long his password is. Unfortunately, there's no good solution. Just start from a shorter one unless the probability is much lower. If he says, "I don't know the length, but it should be at least 6 chars, but most likely 7 or 8." Use your judgment to decide whether to start with 6 or 7. 6 uses significantly less time but lower probability to find it according to the user. 7 takes much longer but has a better chance. But don't try 8 because it takes much longer than 7 and yet has the same chance as 7 according to the user. * What are some ballpark figures for the time to guess longer passwords? As you saw, it took the 2.6 GHz machine about 50 seconds to reach "test1" in a 5-char word brute force crack. The Perl program is written in a way to provide strings in alphabetic order. So if the password were "zz999", it would definitely take longer, perhaps a little over 1 minute. If you add another alphanumeric character to guess a 6-char password, you add one more layer of for-loop. Since the complete alphanumeric character is 36 characters, a rough estimate of runtime is 40 minutes. To guess a 7-char password, it's 40 x 36 = 1440 min or 24 hours. To guess an 8-char password, it's 36 days. All these assume that the execution is on one computer only, the first 2 chars are letters, the remaining are alphanumeric, the password is very "unlucky" to use letters close to the end of the alphabet and digits close to 9. That is, these time estimates are the worst case scenarios. On the other hand, the password may be extremely "lucky" to be found within the first few seconds! * When I create a user, is it better to use "z" and "9" than "a" and "0" in the password, given the same length? I believe most if not all brute force cracking programs generate strings in alphabetic order, so the answer is Yes, especially if you use "z" and "9" toward the beginning of the string. * How do I parallelize the computing work? When the business need arises, it's almost imperative you use multiple computers to split the task so the password can be found in the shortest time. Fortunately, with this Perl program, it's very easy. See above about changing the first level for-loop. That's one of the advantages of using the external Perl program to feed a brute force program. * Any other advantage? And disadvantage? You can adjust the output based on the user's request, such as "definitely no $, 90% sure there's a #, likely ends with !"... The disadvantage is that this customizability is not part of Orabf; if all this were done inside Orabf, it should use less overall CPU and run a little faster. * Anying special about 11g related to this work? By default, 11g passwords are case-sensitive. So [a-z] should be changed to [A-Za-z], significantly increasing the permutation count. However, if the account was created in pre-11g and migrated to 11g or created with "... identified by values ...", or because sec_case_sensitive_logon was set to false, dba_users.password_versions will be '10g'. In that case, [a-z] doesn't need to change. 11g user password hashes are not visible in dba_users; they're in user$.password. This makes malicious password cracking much more difficult. * Why not use Rainbow table? The answer is partially technical, partially ethical. Rainbow crack is designed to crack one single critical account, such as system, in order to compromise the database. The username system is hardcoded in the binary and in the rainbow tables, which are generated with days to months of computing time. Our purpose is to help the user or developer retrieve a regular user's password. You would NOT have envisioned the username and pre-created the rainbow tables before the incident. _____________________ [note1] Pete Fennigan opposes password cracking for whatever reason you can think of! [note2] This is a preliminary attemp: http://search.cpan.org/~bowmanbs/Regexp-Genex-0.07/lib/Regexp/Genex.pm