T09 · Insecure Skill Coding Practices
- Location
scripts/auto_login.py:198- Finding
WHUT Credentials Can Be Submitted to an Attacker-Controlled Host
- Content
View full analysis
Vulnerability Details
File Location:
scripts/auto_login.py:198-224
Vulnerability Type: Unvalidated destination for credential submission
Risk Level: HighVulnerable Code
python def main(): username, password = load_secret() url = sys.argv[1] if len(sys.argv) > 1 else f'https://{TARGET_HOST}' ab('open', url) time.sleep(2) for _ in range(8): snap = snapshot() changed = close_popup_once(snap) if not changed: break for _ in range(20): snap = snapshot() close_popup_once(snap) user_ref, pass_ref = find_login_fields(snap) if user_ref and pass_ref: fill_ref(user_ref, username) time.sleep(0.4) fill_ref(pass_ref, password) time.sleep(0.5) login_ref = find_login_button(snap) if login_ref: click_ref(login_ref) else: press('Enter') time.sleep(2) if is_logged_in(): build_page_dump('LOGIN_OK') returnTechnical Analysis
The script accepts a target URL directly from the first command-line argument and opens it without validating its scheme, hostname, port, or origin. It then searches the resulting page for fields that resemble username and password controls. If matching controls are found, the script automatically fills them using credentials loaded from environment variables or a local secret file and submits the form.
Although
is_logged_in()later checks whether the current URL containswhut.ai-augmented.com, that check occurs only after the credentials have already been entered and the login form has been submitted. It therefore cannot prevent credential disclosure.An attacker-controlled page can expose controls whose accessible names include terms recognized by
find_login_fields(), such as `use ...[truncated 2061 chars]- Remediation
View remediation
Remediation Suggestions
- Parse the requested URL before opening it and reject malformed URLs, user-info components, non-HTTPS schemes, unexpected ports, and unapproved hosts.
- Use an exact hostname allowlist. If subdomains are required, accept only the base host or names ending in
.followed by the base host; do not use substring matching. - Revalidate the browser's current URL immediately before filling either credential field and again immediately before submitting the form. This protects against redirects and navigation performed by page scripts.
- Abort if the browser leaves the approved origin at any point in the authentication flow.
- Prefer a fixed, trusted authentication endpoint instead of allowing arbitrary command-line destinations during credential submission.
- Separate navigation from authentication: arbitrary target pages may be opened only after authentication has completed on a trusted origin.
- Consider enforcing the following validation pattern:
python from urllib.parse import urlparse def is_allowed_url(url): parsed = urlparse(url) host = (parsed.hostname or '').lower() allowed_host = TARGET_HOST.lower() return ( parsed.scheme == 'https' and parsed.username is None and parsed.password is None and parsed.port in (None, 443) and (host == allowed_host or host.endswith('.' + allowed_host)) ) def require_allowed_current_url(): current_url = get_url() if not is_allowed_url(current_url): raise RuntimeError( f'refusing to submit credentials to unapproved URL: {current_url}' ) - Call
require_allowed_current_url()before bothfill_ref()operations and beforeclick_ref()orpress('Enter'). - Add tests covering attacker-controlled hosts, deceptive hostnames such as
whut.ai-augmented.com.attacker.example, HTTP URL ...[truncated 65 chars]
