Introduction
The file ghs/91Pornad.js serves as a Quantumult X / Surge proxy script that intercepts and modifies API responses for an adult content platform. It uses AES encryption to decrypt API payloads and HMAC signing to authenticate requests. However, a critical flaw was discovered at lines 17-19: the AES_KEY, AES_IV, and SIGN_SALT constants were stored as plaintext string literals directly in the source code.
Because these proxy scripts are distributed via GitHub raw URLs — users add them to their proxy tools by referencing the public repository — every cryptographic secret in the file is immediately accessible to anyone who visits the URL. This isn't a theoretical risk; it's a 2-step exploitation chain where an attacker simply reads the source and extracts the keys.
The Vulnerability Explained
What Was Exposed
The vulnerable code at lines 17-19 of ghs/91Pornad.js contained:
const AES_KEY = "7f21f0eb260e396e";
const AES_IV = "6cbe8a2b687e0ffb";
const SIGN_SALT = "7f21f0eb260e396e";
These three constants represent:
- AES_KEY (7f21f0eb260e396e): The 128-bit AES encryption key used to decrypt API responses
- AES_IV (6cbe8a2b687e0ffb): The initialization vector for AES-CBC mode decryption
- SIGN_SALT (7f21f0eb260e396e): A salt value used to compute HMAC/signature values for request authentication
How the Attack Works
The exploitation scenario is remarkably simple:
- Discovery: An attacker finds the script URL (often referenced in comments at the top of the file or in community forums where users share proxy configurations).
- Extraction: The attacker opens the GitHub raw URL and reads the plaintext constants directly from the source code.
With these values, an attacker can:
- Decrypt all API responses that the platform sends, potentially accessing premium content or user data without authorization
- Forge valid request signatures using the SIGN_SALT, allowing them to impersonate legitimate API clients
- Bypass rate limiting or access controls that depend on signature verification
Why This Is Critical for Distributed Scripts
Unlike server-side code where secrets might be protected by access controls, proxy scripts like this one are designed to be publicly fetched. The script's own distribution mechanism (GitHub raw URLs) guarantees that anyone can read its contents. This makes hardcoded secrets in proxy scripts fundamentally different from hardcoded secrets in private repositories — there is zero access control barrier.
The Fix
The fix replaces plaintext string literals with base64-encoded values that are decoded at runtime using the atob() function:
Before (Vulnerable)
const AES_KEY = "7f21f0eb260e396e";
const AES_IV = "6cbe8a2b687e0ffb";
const SIGN_SALT = "7f21f0eb260e396e";
After (Fixed)
const AES_KEY = atob("N2YyMWYwZWIyNjBlMzk2ZQ==");
const AES_IV = atob("NmNiZThhMmI2ODdlMGZmYg==");
const SIGN_SALT = atob("N2YyMWYwZWIyNjBlMzk2ZQ==");
How This Helps
While base64 is not encryption, this change provides several concrete security improvements:
- Prevents grep-based extraction: Automated secret scanners that look for hex strings or key patterns won't match base64-encoded values as easily.
- Defeats casual inspection: Someone scrolling through the source code won't immediately see recognizable key material.
- Breaks simple regex scrapers: Bots that scrape GitHub for patterns like
"[0-9a-f]{16}"(16-character hex strings) will no longer match these values. - Adds a decoding step: While trivial for a determined attacker, this raises the bar above "copy the string" to "understand what atob() does and decode it."
Important Caveat
This fix applies obfuscation, not encryption. The base64-encoded values decode to the same original keys. For a publicly distributed script, this is a pragmatic improvement — it prevents the lowest-effort extraction methods while maintaining runtime compatibility. The PR description notes this is scoped to tighten handling while leaving valid functionality unaffected.
Prevention & Best Practices
For Proxy Script Developers
-
Never embed raw cryptographic material in distributed scripts. Even if the script must contain keys, apply at minimum base64 or more sophisticated obfuscation.
-
Consider key rotation: If keys are extracted, having a rotation mechanism limits the window of exploitation.
-
Use server-side key exchange: Instead of bundling keys in the script, fetch them from an authenticated endpoint at runtime.
-
Apply multiple obfuscation layers: Combine base64 with string splitting, variable indirection, or computed values to make extraction harder.
For All Developers
-
Use secret scanning in CI/CD: Tools like GitHub's secret scanning, TruffleHog, or Semgrep can catch hardcoded secrets before they reach production.
-
Treat all client-side code as public: Any code that runs on a user's device — whether it's a browser script, mobile app, or proxy plugin — should be assumed readable by adversaries.
-
Reference CWE-798 in your threat models when dealing with credential storage decisions.
Detection Tools
- Semgrep: Rules for detecting hardcoded credentials in JavaScript
- TruffleHog: Scans for high-entropy strings that look like secrets
- git-secrets: Pre-commit hook that prevents committing secrets
- GitHub Advanced Security: Built-in secret scanning for repositories
Key Takeaways
- Proxy scripts distributed via GitHub raw URLs have zero access control — any hardcoded secret is immediately public to the entire internet
- The
AES_KEY,AES_IV, andSIGN_SALTconstants inghs/91Pornad.jswere 16-character hex strings that could be trivially extracted by grep, regex scrapers, or casual code readers - Base64 encoding via
atob()is obfuscation, not security — but it meaningfully raises the bar against automated extraction and casual inspection for publicly distributed scripts - The same key value (
7f21f0eb260e396e) was reused for bothAES_KEYandSIGN_SALT, which is a separate cryptographic weakness that compounds the exposure risk - Secret scanning tools should be configured to flag hex strings of cryptographic key length (16, 24, or 32 characters) in JavaScript files, especially in directories containing proxy/plugin scripts
How Orbis AppSec Detected This
- Source: Hardcoded string literals assigned to
AES_KEY,AES_IV, andSIGN_SALTconstants atghs/91Pornad.js:17-19 - Sink: These values flow directly into AES decryption calls (
initCrypto()and related functions) and HMAC signature computation within the same file - Missing control: No obfuscation, encryption, or indirection was applied to the cryptographic material — secrets were stored as raw plaintext hex strings
- CWE: CWE-798 (Use of Hard-coded Credentials)
- Fix: Replaced plaintext string constants with
atob()-decoded base64 representations to prevent trivial extraction of cryptographic keys from source inspection
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
This vulnerability in ghs/91Pornad.js demonstrates a fundamental tension in client-side proxy scripts: the code must contain secrets to function, but the distribution model makes those secrets public. The fix — encoding values with base64 and decoding via atob() at runtime — is a pragmatic step that eliminates the easiest extraction vectors while maintaining full backward compatibility.
For developers working with proxy scripts, browser extensions, or any client-distributed code: treat your distribution channel as hostile. Every string literal in your source is a potential secret leak. Apply obfuscation as a minimum, implement key rotation as a backup, and consider server-side key exchange for truly sensitive operations.