Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions acme-admin-portal/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
188 changes: 188 additions & 0 deletions acme-admin-portal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# ACME Internal Admin Portal — CTF Challenge

**Category:** Web Exploitation
**Difficulty:** Beginner / Intermediate
**Vulnerability:** OS Command Injection (`shell=True`)
**Educational Purpose:** Demonstrating insecure subprocess usage and secure remediation

---

## Project Overview

This is a purpose-built Capture The Flag (CTF) web challenge for an independent study cybersecurity course. The challenge simulates an accidentally exposed internal IT administration portal for a fictional company, ACME Technologies.

The portal contains a network ping utility that is intentionally vulnerable to OS Command Injection due to insecure use of Python's `subprocess` module with `shell=True` and unsanitized user input.

Players must discover and exploit this vulnerability to retrieve a hidden flag.

---

## Repository Structure

```
acme-admin-portal-ctf/
├── author/ # Full source code (instructor/author view)
│ └── src/ # Complete annotated application source
│ ├── app.py # Vulnerable Flask application
│ ├── flag.txt # Challenge flag
│ ├── requirements.txt
│ ├── Dockerfile
│ ├── templates/ # Jinja2 HTML templates
│ ├── static/ # CSS stylesheet
│ └── fake_data/ # Immersive fake content
├── deploy/ # Deployable challenge environment
│ ├── app.py # Same vulnerable app
│ ├── Dockerfile
│ ├── requirements.txt
│ ├── templates/
│ ├── static/
│ ├── fake_data/
│ ├── flag.txt
│ └── DEPLOY.md # Deployment instructions
├── solution/ # Solve script + academic writeup
│ ├── solve.py # Automated exploit script
│ └── WRITEUP.md # Step-by-step solution and vulnerability analysis
└── challenge_description/ # Public-facing challenge prompt
└── challenge.md
```

---

## Vulnerability Description

### Root Cause

The `/ping` endpoint in `app.py` passes user input directly into a shell command using Python's `subprocess.run()` with `shell=True`:

```python
# VULNERABLE CODE
result = subprocess.run(
f"ping -c 1 {target}", # user input concatenated into shell string
shell=True, # shell interprets metacharacters
capture_output=True,
text=True,
timeout=10
)
```

With `shell=True`, the command is executed by `/bin/sh`. This means any shell metacharacters (`;`, `&&`, `|`) in the user-supplied `target` parameter will be interpreted by the shell, allowing command injection.

### Example Exploit

Input:
```
8.8.8.8; cat flag.txt
```

Shell executes:
```bash
ping -c 1 8.8.8.8; cat flag.txt
```

Result: the flag is returned in the HTTP response.

---

## Installation & Docker Usage

### Build

```bash
cd deploy/
docker build -t acme-portal .
```

### Run

```bash
docker run -p 5000:5000 acme-portal
```

### Access

```
http://localhost:5000
```

---

## Intended Solution

1. Browse the application and identify the `/tools` page
2. Notice the Ping Utility is the only active tool
3. Test it with a normal IP (`8.8.8.8`) — observe real command output
4. Inject shell metacharacters: `8.8.8.8; cat flag.txt`
5. The flag appears in the output

Full step-by-step walkthrough: [`solution/WRITEUP.md`](solution/WRITEUP.md)
Automated exploit: [`solution/solve.py`](solution/solve.py)

---

## Security Mitigation

The vulnerability is fixed by:

1. **Removing `shell=True`** and passing arguments as a list
2. **Validating input** against a strict allowlist

```python
# SECURE VERSION
import re

target = request.args.get("target", "")

# Validate: only allow IP/hostname characters
if not re.match(r'^[\d\.a-zA-Z\-]+$', target) or len(target) > 64:
return render_template("tools.html", ping_result="Error: Invalid target.")

result = subprocess.run(
["ping", "-c", "1", target], # List form — no shell interpretation
capture_output=True,
text=True,
timeout=10
# shell=True removed entirely
)
```

**Key principles:**
- Never use `shell=True` with user-controlled input
- Validate and allowlist all user input before passing it to system calls
- Use argument arrays instead of string concatenation
- Run applications with minimum required privileges

---

## Learning Objectives

After completing this challenge, students should understand:

- What OS Command Injection is and how it occurs
- Why `shell=True` is dangerous with unsanitized input
- How shell metacharacters (`;`, `&&`, `|`) enable command chaining
- How to identify injection points through reconnaissance
- How to remediate the vulnerability with secure subprocess patterns
- Basic web exploitation methodology

---

## Educational Purpose Disclaimer

This application is **intentionally vulnerable** and was created solely for educational purposes as part of a cybersecurity coursework project. It demonstrates real-world vulnerability patterns in a controlled, isolated environment.

**Do not deploy this application on a public server or in any production environment.**

All company names, characters, and scenarios are fictional and for educational use only.

---

## References

- [OWASP: Command Injection](https://owasp.org/www-community/attacks/Command_Injection)
- [Python subprocess — Security Considerations](https://docs.python.org/3/library/subprocess.html#security-considerations)
- [CWE-78: OS Command Injection](https://cwe.mitre.org/data/definitions/78.html)
- [PortSwigger: OS Command Injection](https://portswigger.net/web-security/os-command-injection)
55 changes: 55 additions & 0 deletions acme-admin-portal/author/README_AUTHOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# ACME Admin Portal — Author Notes (Internal)

This file is for the challenge author / instructor only.

---

## Flag

```
flag{internal_tools_should_not_use_shell_true}
```

Located in: `app.py` root directory (both `author/src/` and `deploy/`)

---

## Vulnerability Summary

- **File:** `app.py`, function `ping()`, route `/ping`
- **Root cause:** `subprocess.run(f"ping -c 1 {target}", shell=True, ...)`
- **Attack vector:** GET parameter `target` with no sanitization
- **Exploit:** `8.8.8.8; cat flag.txt`

---

## Testing the Challenge

Start the app:
```bash
cd author/src
pip install flask
python app.py
```

Verify vulnerability:
```
curl "http://localhost:5000/ping?target=8.8.8.8;+cat+flag.txt"
```

Run automated solve:
```bash
python solution/solve.py
```

---

## Submission Checklist

- [x] Source code in `author/src/`
- [x] Deployable Docker environment in `deploy/`
- [x] Deployment instructions in `deploy/DEPLOY.md`
- [x] Solve script at `solution/solve.py`
- [x] Written writeup at `solution/WRITEUP.md`
- [x] Challenge description at `challenge_description/challenge.md`
- [x] Root `README.md` with full project documentation
12 changes: 12 additions & 0 deletions acme-admin-portal/author/src/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 5000

CMD ["python", "app.py"]
76 changes: 76 additions & 0 deletions acme-admin-portal/author/src/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""
ACME Internal Admin Portal
==========================
Author copy — includes full source and vulnerability notes.

INTENTIONALLY VULNERABLE APPLICATION — FOR EDUCATIONAL PURPOSES ONLY
This application is designed to demonstrate OS command injection vulnerabilities.
DO NOT deploy in a production environment.
"""

import subprocess
from flask import Flask, render_template, request

app = Flask(__name__)


@app.route("/")
def index():
return render_template("index.html")


@app.route("/tools")
def tools():
return render_template("tools.html")


@app.route("/logs")
def logs():
with open("fake_data/logs.txt", "r") as f:
log_content = f.read()
return render_template("logs.html", logs=log_content)


@app.route("/about")
def about():
return render_template("about.html")


# =============================================================================
# VULNERABLE ENDPOINT — DO NOT USE IN PRODUCTION
# Vulnerability: OS Command Injection via shell=True
# The user-supplied `target` parameter is passed directly into a shell command
# without any sanitization. An attacker can inject shell metacharacters such as
# ; | && to chain arbitrary commands.
#
# Example exploit:
# /ping?target=8.8.8.8; cat flag.txt
# =============================================================================
@app.route("/ping")
def ping():
target = request.args.get("target", "")

if not target:
return render_template("tools.html", ping_result="Error: No target specified.")

# VULNERABLE: shell=True with unsanitized user input
try:
result = subprocess.run(
f"ping -c 1 {target}", # <-- user input concatenated directly!
shell=True, # <-- shell=True enables metacharacter injection
capture_output=True,
text=True,
timeout=10
)
output = result.stdout + result.stderr
except subprocess.TimeoutExpired:
output = "Error: Request timed out."
except Exception as e:
output = f"Error: {str(e)}"

return render_template("tools.html", ping_result=output)


if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=False)
24 changes: 24 additions & 0 deletions acme-admin-portal/author/src/fake_data/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# ACME Admin Portal — TODO

## High Priority
- [ ] Implement authentication (portal is currently unauthenticated!)
- [ ] Migrate diagnostics tools to safe subprocess calls (no shell=True)
- [ ] Input validation for all form fields
- [ ] Enable HTTPS

## Medium Priority
- [ ] Implement DNS lookup backend
- [ ] Implement port scanner (rate-limited)
- [ ] Improve logging — add timestamps and user tracking
- [ ] Add session management

## Low Priority
- [ ] Dark/light mode toggle
- [ ] Export logs to CSV
- [ ] Integrate with internal ticketing system

## Done
- [x] Basic dashboard layout
- [x] Ping utility (NEEDS SECURITY REVIEW)
- [x] Fake log viewer
- [x] Docker containerization
28 changes: 28 additions & 0 deletions acme-admin-portal/author/src/fake_data/admin_notes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
ACME Technologies — Internal Admin Notes
========================================
Last updated: 2024-03-15 | Author: j.harris@acme-tech.internal

REMINDERS (before next sprint):
--------------------------------
- [ ] Sanitize ALL user input on diagnostics endpoints
- [ ] Remove shell=True before production deployment — THIS IS A SECURITY RISK
- [ ] Rotate admin credentials (current ones have been shared too widely)
- [ ] Restrict portal access to internal VPN only
- [ ] Add authentication layer — portal is currently wide open
- [ ] Audit subprocess calls in app.py

KNOWN ISSUES:
-------------
- Ping utility does not validate IP format
- DNS lookup is not yet implemented (placeholder only)
- Port scanner stub needs backend logic
- Log viewer loads entire log file (could be slow at scale)

TEMP CREDENTIALS (DO NOT LEAVE IN PRODUCTION):
-----------------------------------------------
admin / admin123 <-- CHANGE THIS IMMEDIATELY

NOTES FROM LAST MEETING:
-------------------------
Marcus flagged the diagnostics page as a potential risk vector.
We agreed to patch before Q2 external audit. Deadline: EOD Friday.
Loading