8
\$\begingroup\$

The Challenge

Create a program that brute-force* decodes the MD5-hashed string, given here: 92a7c9116fa52eb83cf4c2919599c24a which translates to code-golf

The Rules

  1. You may not use any built-in functions that decode the hash for you if the language you choose has such.
  2. You may not use an online service to decode the hash for you.
  3. This is code-golf, the program that accomplishes the task in the shortest amount of characters wins.

Good luck. May the brute-force be with you.

*Brute-Force definition: looping from a-zzz.. including abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-=[]\;',./~!@#$%^&*()_+{}|:"`<>? in a string, hashing it, and testing it against the hash given until the correct string that has been hashed has been found. Print the string to the program's output stream.

mbomb007
23.6k7 gold badges66 silver badges143 bronze badges
asked Nov 30, 2014 at 16:36
\$\endgroup\$
11
  • 5
    \$\begingroup\$ Interesting challenge. Just have one doubt: Will you hash any character or the input is restricted to a maximum length and/or specific chars? \$\endgroup\$ Commented Nov 30, 2014 at 16:41
  • \$\begingroup\$ @IsmaelMiguel well, typically brute force is a-zzzz.. and along the way it finds the answer, so I'll say stick with general ascii. Sorry if that didn't answer your question, I didn't particularly understand it \$\endgroup\$ Commented Nov 30, 2014 at 17:38
  • 1
    \$\begingroup\$ The term "brute force" is not sufficiently precise unless you state the search space, and (for example) it's not equally difficult to search all nine-character strings as to search all strings of increasing length. \$\endgroup\$ Commented Nov 30, 2014 at 17:41
  • \$\begingroup\$ If you want from a-zzzz...., I give you a solution in less than 5 seconds. If you want ALL ASCII chars, it will take some time. You forgot to mention the winning criterion (e.g.: shortest number of chars or bytes). \$\endgroup\$ Commented Nov 30, 2014 at 17:48
  • 1
    \$\begingroup\$ @MtnViewMark you never know. someone'd probably go and look for a language that has one just to make a short script... \$\endgroup\$ Commented Nov 30, 2014 at 18:34

6 Answers 6

5
\$\begingroup\$

Haskell, 101 characters

import Data.Hash.MD5
main=interact$ \m->head.filter((==m).md5s.Str)$iterate([' '..'~']:)[]>>=sequence

Use it like so:

& ghc -O2 -o brute 42043-BruteForce.hs
& echo -n c-g | md5
bc098aea57599de26c8f17a9edbd492e
& echo -n bc098aea57599de26c8f17a9edbd492e | ./brute
c-g

This requires the common package MissingH.

answered Nov 30, 2014 at 18:57
\$\endgroup\$
3
  • \$\begingroup\$ How long does it take to decrypt the given hash value? \$\endgroup\$ Commented Nov 30, 2014 at 19:35
  • 1
    \$\begingroup\$ @Jakube - Approximately 282,512 years! I seriously doubt that code here could ever complete the task before the computers they were running on failed! Testing up through all 9 character strings (with a 95 character alphabet) requires ~6x10^17 test cases! The code above compiled -O2, does ~71k tests / second. Clearly, even if you could generate and hash 100x faster, it would still take millenia! \$\endgroup\$ Commented Dec 1, 2014 at 8:00
  • 5
    \$\begingroup\$ Also, please note: You can't decrypt a hash value. All you can do is find strings that hash to the same value. \$\endgroup\$ Commented Dec 1, 2014 at 8:02
4
\$\begingroup\$

PHP (155)

<?$a=[];function r($s){for($c=32;$c<127;$c++){$k=$s+chr($c);md5($k)=="92a7c9116fa52eb83cf4c2919599c24a"?die($k):array_push($a,$k)}r(array_shift($a))}r('');

Ungolfed:

<?
$a=[];
function r($s){
 for($c=32;$c<127;$c++){
 $k=$s+chr($c);
 md5($k)=="92a7c9116fa52eb83cf4c2919599c24a" ? die($k) : array_push($a,$k);
 }
 r(array_shift($a));
}
r('');

What it does is define an empty array $a, which will be our operations-to-do-later array. r is a recursive function and $s is the current string we're working with. We begin with an empty string, and loop through all the characters noted above, which are ASCII 32 to 127, and checking them all. While that happens, all of those are pushed onto the end of the array. Then, when all of the 1-length strings are done and all of them are put into the array, we now call r again with the first element in the array. Then, $s will be set to ASCII 32, and it will loop through 2 character strings that start with that character, checking them all and putting them at the end of the array. Since they are at the end, the next call to r will check ASCII 33 rather than them, since it is now the first one in the array. This prevents an infinite loop from occuring where it checks one ASCII 32 and then 2 ASCII 32's and then 3 ASCII 32's... etc.

answered Dec 1, 2014 at 0:24
\$\endgroup\$
6
  • \$\begingroup\$ This code would run out of memory before getting through all five character strings! \$\endgroup\$ Commented Dec 1, 2014 at 8:07
  • 1
    \$\begingroup\$ @MtnViewMark You can increase the memory limit. \$\endgroup\$ Commented Dec 1, 2014 at 21:07
  • 2
    \$\begingroup\$ @IsmaelMiguel - Do you have 40GB of RAM? That is how many characters would be needed for get through all 5 character strings. At 6 characters, you'll need 6.5TB of memory... I doubt any system has that much swap space. Storing all 8 character strings, needed before you can get to "code-golf", you'll need 53 PetaBytes of storage. That's almost half the size of the largest storage arrays ever built. \$\endgroup\$ Commented Dec 2, 2014 at 5:42
  • 3
    \$\begingroup\$ @MtnViewMark Considering that such a small code needs such a big amount of memory is quite remarkable! I've never seen a code-golf solution being downvoted for being impractical. Give the man an upvote. He had the effort to put such a code. \$\endgroup\$ Commented Dec 2, 2014 at 9:26
  • \$\begingroup\$ @MtnViewMark - Yea, I thought of another way but decided that this way would be the shortest to write. I expected it to take up a lot of memory, but didn't expect it to take that much! \$\endgroup\$ Commented Dec 2, 2014 at 22:47
3
\$\begingroup\$

Python (182 characters)

import sys,string as c,hashlib as h,itertools as i
m=sys.stdin.read()
j=''.join
print(next(j(v)for n in i.count()for v in i.product(*n*(c.printable,))if h.md5(j(v)).hexdigest()==m))

Example:

$ echo -n Hi! | md5sum
5360706c803a759e3a9f2ca54a651950 -
$ echo -n 5360706c803a759e3a9f2ca54a651950 | python2.7 decode.py
Hi!

The itertools module does most of the work, with .count() responsible for gradual escalation. The nested generator expression is filtered until we get a match on the MD5 hash.

answered Dec 1, 2014 at 0:41
\$\endgroup\$
2
  • \$\begingroup\$ You can import md5 and use md5.new instead of hashlib.md5. \$\endgroup\$ Commented Jun 16, 2016 at 21:49
  • \$\begingroup\$ 177 bytes \$\endgroup\$ Commented Sep 29, 2019 at 9:10
0
\$\begingroup\$

bash - about 105 characters, not the greatest at golfing bash scripts!

Requires makepasswd and openssl.

until [ "$Y" = "3ドル" ]
do
 X=($(makepasswd --string=1ドル --chars=2ドル))
 Y=($(echo -n $X | md5sum))
done
echo $X

You use it by supplying three arguments: ./reverse-md5 character-set length-of-password md5-hash-to-match, e.g.:

$ ./reverse-md5 'ab' 4 54a8723466e5d487247f3d93d51c66bc
abba

Hoorah, have found the name of an obscure Swedish pop band!

Note that this is a directed attack in that you have some idea of the characters used and the length of the password.

So you could use the script in a directed attack as follows to complete the challenge:

$ ./reverse-md5 'cdefglo-' 9 92a7c9116fa52eb83cf4c2919599c24a
[wait a long time]
code-golf

Or as a brute force attack, run in parallel like this:

$ for L in `seq 1 20` ; do ./reverse-md5 '!"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~' $L 92a7c9116fa52eb83cf4c2919599c24a & done
[wait a really long time!]
code-golf

Which, depending on the hardware, may give you an answer faster than going sequentially from '!' to '~~~...'

answered Dec 2, 2014 at 20:47
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Remove the spaces around the pipe near md5sum, and get rid of the indentation. \$\endgroup\$ Commented Sep 22, 2015 at 18:24
0
\$\begingroup\$

Hassium, 138 Bytes

use Math;func main(){d=["laptop","code-golf"];p="312f91285e048e09bb4aefef23627994";foreach(l in d){if(Math.hash("md5",l)==p)println(l);}}

Expanded: http://HassiumLang.com/Hassium/index.php?code=77bf67650e57549addd25daf7503db06

answered Sep 22, 2015 at 16:29
\$\endgroup\$
0
\$\begingroup\$

Perl, 75 + 18 = 93 bytes

This program contains nonprintable characters, so here's a hexdump:

00000000: 245f 3d31 3b73 7c30 2a28 2e29 7c24 263d $_=1;s|0*(.)|$&=
00000010: 7e79 3d20 2d7e 3d21 2d7e 203d 722e 3178 ~y= -~=!-~ =r.1x
00000020: 2124 317c 6520 7768 696c 6520 6d64 3528 !1ドル|e while md5(
00000030: 245f 296e 6527 92a7 c911 6fa5 2eb8 3cf4 $_)ne'....o...<.
00000040: c291 9599 c24a 273b 7361 79 .....J';say

This program requires the command line argument -MDigest::MD5=md5 (an 18 byte penalty, 17 for the argument and 1 for the space separating it from the other arguments).

Explanation

Here's the program with whitespace and comments added and the binary string replaced with '...':

$_ = 1; # $_ is the string to check; start at '1'
s|0*(.)| # replace a string of leading 0s plus 1 character
 $& =~ y= -~=!-~ =r # by rotating 1 printable ASCII character forward
 . 1 x !1ドル # and appending a 1 if the replaced section ends in 0
|e # (the previous two lines are a Perl expression)
 while md5($_) ne '...'; # while it has the wrong md5
say # print out the value of $_ we found

The basic trick here is that although we want to check from shortest to longest, there's no particular reason to check the characters in ASCIIbetical order. As a result, we check in the order from 1 to ~ then to 0. To do this, we repeatedly increment the first character after the leading 0s, and reset the leading 0s back to leading 1s (which we can do by incrementing every character up to and including the character after the leading 0s). If the string consists entirely of 0s, we need to append a new 1 to the end; we do this via checking to see if the string consists entirely of 0s via seeing if the last matched character in the regex is 0 (as the 0* match is greedy, this will only happen if there are no non-0 characters in the string).

The reason 1 and 0 are picked as the first and last character in the range is that a) they don't need quoting (the string "1" can be written as 1 because Perl doesn't really distinguish between strings and integers), and b) that Perl has a very short way (!) of checking a single-character string for equality with 0 (Perl has three falsey values: undef, "", and "0", with all other values being truthy).

Note that we have no reason to translate the MD5 hash in question into hexadecimal; it's specified in the question, and so we can pack it into binary ourselves and just compare the binary representations directly. Perl's builtin for producing an md5 hash in binary has a shorter name than the hexadecimal equivalent, and the binary representation takes up half the space in the source, so this is a win in two different ways.

Verification

I tested the program with the hash of the string abc inserted into the source code rather than the hash of the string code-golf, and it found the correct answer in 1.3 seconds. (It's unlikely to be able to reverse the hash of code-golf in a reasonable length of time, due to the need to check a substantial subset of the printable ASCII range.)

answered Jan 16, 2017 at 19:11
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.