Taking on the King: Killing Injection Vulnerabilities Scripting A4 A1 A2 A3 A7. ... Some SQL...

57
Taking on the King: Killing Injection Vulnerabilities Taking on the King: Killing Injection Vulnerabilities Justin Collins @presidentbeef AppSec California 2018

Transcript of Taking on the King: Killing Injection Vulnerabilities Scripting A4 A1 A2 A3 A7. ... Some SQL...

Taking on the King:Killing Injection Vulnerabilities

Taking on the King:Killing Injection Vulnerabilities

Justin Collins@presidentbeef

AppSec California 2018

About Me

I’m this guy over here

About Me

Or maybe over there

This is a “Thought” Talk

Injection in the OWASP Top 10

2004 2009 2010 2013 2017

Injection A6 A2 A1 A1 A1

Cross-Site Scripting A4 A1 A2 A3 A7

Injection in the OWASP Top 10

Injection in the Wild

HackerOne Hacker-Powered Security Report 2017

Injection in the Wild

“...the average payout for SQLi was the highest at $1,058”

BugCrowd State of Bug Bounty Report 2017

What is an Injection Vulnerability?

DATA interpreted as CODE

What is an Injection Vulnerability?

DATA interpreted as CODEQuery Parameters

Form ValuesHeader ValuesUploaded Files

Database Values...

SQLHTMLJavaScriptBash ScriptXML Entities...

What is an Injection Vulnerability?

Developer Code Attacker Code

Interpreter

What is an Injection Vulnerability?

Developer Code Attacker Code

Interpreter

Developer Code

What is an Injection Vulnerability?

Developer Code Attacker Code

Interpreter

Developer Code

Database DriverBrowserWeb ServerMail ServerShellTemplating LibraryXML ParserLDAP Parsereval()...

SQL Injection Example

query = "SELECT * FROM users WHERE email='" + email + "'"

db.execute(query)

SQL Injection Example

query = "SELECT * FROM users WHERE email='" + email + "'"

db.execute(query)

Expected to be DATA

SQL Injection Example

query = "SELECT * FROM users WHERE email='" + email + "'"

db.execute(query)

Expected to be DATALooks like DATA,but is CODE

SQL Injection Example

email = request.params["email"]

query = "SELECT * FROM users WHERE email='" + email + "'"

db.execute(query)

User Input

SQL Injection Example

email = "' OR 1=1;--"

query = "SELECT * FROM users WHERE email='" + email + "'"

db.execute(query)

Attacker Input

SQL Injection Example

email = "' OR 1=1;--"

query = "SELECT * FROM users WHERE email='' OR 1=1;--'"

db.execute(query)

DATA now interpreted as CODE

Some SQL Injection

Python

query = "SELECT * FROM users WHERE email = '%s'" % (request.POST.get('email'))

cursor.execute(query)

Ruby / ActiveRecord

query = "SELECT * FROM users WHERE email = '#{params[:email]}'"ActiveRecord::Base.connection.execute(query)

Java

String user = request.getParameter("email");Statement st = conn.createStatement();String query = "SELECT * FROM user where userId='" + email + "'";st.executeQuery(query)

Go

db.Query("SELECT *FROM users WHERE email='" + req.FormValue("email") + "'")

Some Command Injection

Python

cmd = "rm -rf /tmp/%s" % request.POST.get("username")

os.system(cmd)

Ruby

`rm -rf #{params[:username]}`

Java

String cmd = String.format("sh -c rm -rf /tmp/%s", request.getParameter("username"))Runtime.getRuntime().exec(cmd)

Go

cmd := exec.Command(fmt.String("rm -rf /tmp/%s", req.FormValue("username"))

err := cmd.Run()

Some Server-Side Template Injection

Ruby on Rails

render inline: "Hello, #{params[:name]}!"

Python / Jinja

from jinja2 import Environment

name = request.GET.get('name')Environment().from_string('Hello ' + name + '!').render()

Some Cross-Site Scripting?

<html> <head> <title>{{ title }} - My Site</title> <style> body { color: {{ theme['color'] }}; } </style> </head> <body> <script type="text/javascript"> var init = {{ data }}; </script>

<a href={{ home_url }}>Home</a>

</body></html>

Some Cross-Site Scripting?

<html> <head> <title>{{ title }} - My Site</title> <style> body { color: {{ theme['color'] }}; } </style> </head> <body> <script type="text/javascript"> var init = {{ data }}; </script>

<a href={{ home_url }}>Home</a>

</body></html>

HTML Context

CSS Context

JavaScript Context

HTML Attribute Context

Web Programming is Metaprogramming

Web Programming is Metaprogramming

“Writing Code that Writes Code”

Web Programming is Metaprogramming

SQL / NoSQL

HTTP, HTML, JavaScript, JSON, CSS, ...

HTTP, JSON, XML, Form Data...

HTTP, JSON, XML, ...HTML, JavaScript, CSS, ...

Web ProgrammingIs

Compiler Construction

Web ProgrammingIs

Compiler ConstructionWith Untrusted Values!

Web Programming is Compiler Construction

SQL / NoSQL

HTTP, HTML, JavaScript, JSON, CSS, ...

HTTP, JSON, XML, ...

HTTP, JSON, XML, ...HTML, JavaScript, CSS, ...

Compiler Components

Lexer

Parser

Semantic Analyzer

Code Generator(s)

Source Code

Token Stream

Abstract Syntax Tree

Semantic Graph

IntermediateRepresentations

Compiled Code

We Are Using String Manipulationto Write Complex Compilers

We Are Using String Manipulationto Write Complex Compilers

With Untrusted Values!

Web Programming is Compiler Construction

SQL / NoSQL

HTTP, HTML, JavaScript, JSON, CSS, ...

HTTP, JSON, XML, ...

HTTP, JSON, XML, ...HTML, JavaScript, CSS, ...

Via String ManipulationWith Untrusted Values!

Back to the Real World

SQL Injection Prevention

ORM

User.where(email: params[:email])

Query Parameterization

User.where(["email = ?", params[:email]])

Manual escaping

email = ActiveRecord::Base.connection.quote_string(params[:email])

query = "SELECT * FROM users WHERE email = #{email}"

ActiveRecord::Base.connection.execute(query)

Preventing Injection(Some Suggestions)

Remove Unsafe Interfaces

Remove Unsafe Interfaces

db.execute(...) NO!

system.run(...) NO!

blah.html_safe NO!

{{ … | safe }} NO!

blah.innerHTML = ... NO!

Stop Providing String Interfaces

ARel

users = Arel::Table.new(:users)

users.where(users[:name].eq('bob').or(users[:age].lt(25)))

But I Want To...

schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')

result = query(<<-SQL, name)

SELECT distinct i.relname, d.indisunique, d.indkey, t.oid

FROM pg_class t, pg_class i, pg_index d

WHERE i.relkind = 'i'

AND d.indexrelid = i.oid

AND d.indisprimary = 'f'

AND t.oid = d.indrelid

AND t.relname = '#{table_name}'

AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )

ORDER BY i.relname

SQL

Types to the Rescue?

User.where("name = '#{params[:email]}'")

SecurityError in UsersController#search:

Cannot add dangerous input to String

Is This a Good Idea?

mab = Markaby::Builder.new

mab.html do

head { title "Boats.com" }

body do

h1 "Boats.com has great deals"

ul do

li "$49 for a canoe"

li "$39 for a raft"

li "$29 for a huge boot that floats and can fit 5 people"

end

end

end

Restrict Accepted Language

Restrict Accepted Language

Do you really need a Turing-complete language..?

Provide Context-Aware Escaping

Context-Aware Auto-Escaping

<html> <head> <title>{{ title }} - My Site</title> <style> body { color: {{ theme['color'] }}; } </style> </head> <body> <script type="text/javascript"> var init = {{ data }}; </script>

<a href={{ home_url }}>Home</a>

</body></html>

HTML Context

CSS Context

JavaScript Context

HTML Attribute Context

HTML Context-Aware Auto-Escaping

Go Templates

CTemplates

Latte (PHP)

SecureHandlebars

SQL Context-Aware Auto-Escaping

Shell Context-Aware Auto-Escaping

“But, but, framework XYZ does this!”

https://www.flickr.com/photos/cogdog/6706527857

EVERY Framework Needs to Do This

How?

Quality, simple, portable libraries (e.g. libnacl, libpasta)

Make it “standard” in new web frameworks

Have the “cool kids” (Google, Facebook, etc.) push it

How?

Quality, simple, portable libraries (e.g. libnacl, libpasta)

Make it “standard” in new web frameworks

Have the “cool kids” (Google, Facebook, etc.) push it

Thank You

@presidentbeef