Summary
During a security audit of the Tsugi admin console, I identified several vulnerabilities:
1. SSRF via Admin Proxy Endpoint (MEDIUM)
File: admin/proxy_small_json.php
The proxy endpoint allows fetching arbitrary URLs via file_get_contents() with no private/internal IP validation:
$proxyUrl = U::get($_GET, 'proxyUrl');
$content = file_get_contents($proxyUrl, FALSE, NULL, 0, 10000);
An admin can request internal URLs like http://169.254.169.254/latest/meta-data/ (cloud metadata). While admin-only, in cloud environments this exposes instance credentials and internal services.
Fix: Add private IP validation before fetching (check for 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8).
2. No CSRF Protection on Admin Operations (MEDIUM)
No CSRF tokens are used anywhere in the admin panel. All state-changing operations (key management, user management, data deletion, email sending, database operations) rely solely on session cookies. The CrudForm library also lacks CSRF validation.
If an admin visits a malicious page, an attacker can forge requests to delete users, create/modify LTI keys, approve key requests, send emails, or expire PII data.
Fix: Add CSRF token generation and validation to CrudForm and all admin forms.
3. No Brute-Force Protection on Admin Password (MEDIUM)
File: admin/gate.php
The admin password form has no rate limiting, lockout, or CAPTCHA. Unlimited attempts are allowed. Failed attempts are logged but not blocked.
Fix: Add rate limiting (e.g., exponential backoff or account lockout after N failures).
4. Loose Password Comparison (LOW)
File: admin/gate.php line 37
if ( (strpos($apw, 'sha256:') === false && $phrase == $apw ) ||
Uses PHP == instead of ===. With type juggling, if $adminpw were numeric (e.g., 0), any non-numeric string would match.
Fix: Use === for strict comparison.
Found during authorized security research. Happy to discuss or help with fixes.
Summary
During a security audit of the Tsugi admin console, I identified several vulnerabilities:
1. SSRF via Admin Proxy Endpoint (MEDIUM)
File:
admin/proxy_small_json.phpThe proxy endpoint allows fetching arbitrary URLs via
file_get_contents()with no private/internal IP validation:An admin can request internal URLs like
http://169.254.169.254/latest/meta-data/(cloud metadata). While admin-only, in cloud environments this exposes instance credentials and internal services.Fix: Add private IP validation before fetching (check for 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8).
2. No CSRF Protection on Admin Operations (MEDIUM)
No CSRF tokens are used anywhere in the admin panel. All state-changing operations (key management, user management, data deletion, email sending, database operations) rely solely on session cookies. The
CrudFormlibrary also lacks CSRF validation.If an admin visits a malicious page, an attacker can forge requests to delete users, create/modify LTI keys, approve key requests, send emails, or expire PII data.
Fix: Add CSRF token generation and validation to
CrudFormand all admin forms.3. No Brute-Force Protection on Admin Password (MEDIUM)
File:
admin/gate.phpThe admin password form has no rate limiting, lockout, or CAPTCHA. Unlimited attempts are allowed. Failed attempts are logged but not blocked.
Fix: Add rate limiting (e.g., exponential backoff or account lockout after N failures).
4. Loose Password Comparison (LOW)
File:
admin/gate.phpline 37Uses PHP
==instead of===. With type juggling, if$adminpwwere numeric (e.g.,0), any non-numeric string would match.Fix: Use
===for strict comparison.Found during authorized security research. Happy to discuss or help with fixes.