bilalahmedss/Application-Security-Testing

GitHub: bilalahmedss/Application-Security-Testing

一个基于 DVWA 和 Docker 的应用安全测试实验项目,涵盖主流 Web 漏洞的实操复现、请求拦截、安全分析与防御修复。

Stars: 0 | Forks: 0

# Application Security Testing Lab A hands-on web application security assessment performed against Damn Vulnerable Web Application (DVWA) in an isolated Docker environment. The project demonstrates vulnerability discovery, manual exploitation, request manipulation, remediation analysis, and OWASP Top 10 mapping across multiple application-security test cases. ## Assessment Summary | Area | Coverage | |---|---| | Target | Damn Vulnerable Web Application | | Environment | Docker-based local lab | | Testing approach | Manual testing and request interception | | Security levels | Low, Medium and High | | Main tool | Burp Suite | | Standards mapping | OWASP Top 10 | | Purpose | Defensive security learning and vulnerability analysis | ## Vulnerabilities Tested - SQL injection - Blind SQL injection - Reflected, stored and DOM-based XSS - Cross-site request forgery - Operating-system command injection - File inclusion - Unrestricted file upload - Brute-force authentication - Weak session identifiers - Insecure CAPTCHA handling ## Table of Contents 1. [Install Docker](#1-install-docker) 2. [Deploy DVWA in Docker](#2-deploy-dvwa-in-docker) 3. [Vulnerability Testing](#3-vulnerability-testing) - [3.1 SQL Injection](#31-sql-injection) - [3.2 SQL Injection (Blind)](#32-sql-injection-blind) - [3.3 XSS Reflected](#33-xss-reflected) - [3.4 XSS Stored](#34-xss-stored) - [3.5 XSS DOM](#35-xss-dom) - [3.6 CSRF](#36-csrf) - [3.7 Command Injection](#37-command-injection) - [3.8 File Inclusion](#38-file-inclusion) - [3.9 File Upload](#39-file-upload) - [3.10 Brute Force](#310-brute-force) - [3.11 Insecure CAPTCHA](#311-insecure-captcha) - [3.12 Weak Session IDs](#312-weak-session-ids) 4. [Docker Inspection Tasks](#4-docker-inspection-tasks) 5. [Security Analysis](#5-security-analysis) 6. [OWASP Top 10 Mapping](#6-owasp-top-10-mapping) 7. [Conclusion](#7-conclusion) 8. [Bonus: Nginx + HTTPS](#8-bonus-nginx--https) 9. [GitHub Repository](#9-github-repository) ## 1. Install Docker ### Docker Installation Docker version 29.2.1, build a5c7197 ![docker version](https://static.pigsec.cn/wp-content/uploads/repos/cas/37/37aa5b21f43b611d188e2a7047c4dc99719237d66391a4c7680ac07ef8afbbdb.png) ## 2. Deploy DVWA in Docker docker pull vulnerables/web-dvwa docker run -d --name dvwa -p 8080:80 vulnerables/web-dvwa DVWA is accessible at `http://localhost:8080`. Database initialized via the setup page. Login confirmed with `admin / password`. ![dvwa dashboard](https://static.pigsec.cn/wp-content/uploads/repos/cas/40/406c62240d7a7c540f89132f1bfe0bd06f673d828282781738fac32602c5e6bd.png) ## 3. Vulnerability Testing ### 3.1 SQL Injection #### Low **Payload:** 1' OR '1'='1 **Result:** All 5 user records were dumped - admin, Gordon Brown, Hack Me, Pablo Picasso, and Bob Smith. The database returned every row because the injected condition `'1'='1` is always true. ![sql injection low](https://static.pigsec.cn/wp-content/uploads/repos/cas/3a/3a131bfddedafa69a2081424bece72bac76cc94a124275c75178941e7ad23bf6.png) **Why it worked:** The input is concatenated directly into the SQL query with no sanitization. The resulting query becomes `SELECT * FROM users WHERE id='1' OR '1'='1'` which returns all rows. #### Medium **Payload:** Security level changed to Medium. A dropdown replaced the free text field. Burp Suite was used to intercept the POST request and the `id` parameter was manually changed to `1 OR 1=1` before forwarding. 1 OR 1=1 **Result:** All 5 users were still returned. The attack succeeded despite the dropdown restricting the UI, because the server-side query was still vulnerable. ![sql injection medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/44/44913d5b32e4ccb72aade87d68aa465b2a17b3701d920e9d64fab88fae112b50.png) **Analysis:** Medium only changes the input surface from a text box to a dropdown. It adds no real server-side protection. Intercepting the request with Burp and modifying the parameter directly bypasses the UI restriction entirely. #### High **Test performed:** The same authentication-bypass payload was submitted through the separate input window. **Observed result:** The application returned only a limited result rather than exposing all rows. **Analysis:** The higher security level introduced additional restrictions that prevented the original full-table extraction technique from succeeding as it did at Low and Medium. The result demonstrates that reducing output and changing input handling can limit an attack, but complete prevention should rely on parameterized queries, strict server-side validation, and least-privilege database access. | Field | Details | |---|---| | Vulnerability | SQL Injection | | Security Levels Tested | Low, Medium, High | | Payload | `1' OR '1'='1` / `1 OR 1=1` | | Low Result | All 5 users dumped | | Medium Result | All 5 users dumped via Burp interception | | High Result | Single record returned, full dump prevented | | OWASP Category | A03:2021 Injection | ### 3.2 SQL Injection (Blind) #### Low **Payload:** `1' AND 1=1#` and `1' AND 1=2#` **Result:** `1' AND 1=1#` returned "User ID exists in the database." Switching to `1' AND 1=2#` returned "User ID is MISSING." The database responds differently to true vs false conditions confirming boolean-based blind injection. ![sql injection blind low](https://static.pigsec.cn/wp-content/uploads/repos/cas/6e/6ee2ea2c691559bb7b2187247bb0874a5300f27a7a532b354a05335888c0acef.png) **Why it worked:** No data is displayed on screen but the query still executes the injected logic. By observing different responses to true vs false conditions we can extract information without ever seeing raw data. This is boolean-based blind SQLi. #### Medium **Payload:** `1 AND 1=1` via Burp Suite interception **Result:** Same true/false behavior confirmed. Intercepted the POST request in Burp, modified the id parameter directly. Server returned exists for true and missing for false. ![sql injection blind medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/ea/ea16e3eb4d82b443622dff4179a8dc46deaac6f7ce9f082e9ad7c46c023b216c.png) **Analysis:** Medium restricts the UI to a dropdown but the server-side query is still unsanitized. Intercepting and modifying the request in Burp bypasses the UI restriction entirely. #### High **Payload:** `1' AND 1=2#` via cookie input popup **Result:** Attack still succeeded. High level moves input to a cookie parameter via a separate popup but does not sanitize it. True condition returned exists, false condition returned missing. ![sql injection blind high](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad8ec3ba7510f8811cf32e59074430c41a5e2dbd9406ea7672672f5d97431db9.png) **Defense Mechanism:** The intended defense was obscuring the input by moving it to a cookie. However the cookie value is still passed unsanitized to the SQL query. Proper prevention requires prepared statements regardless of where input originates. | Field | Details | |---|---| | Vulnerability | SQL Injection (Blind) | | Security Levels Tested | Low, Medium, High | | Low Payload | `1' AND 1=1#` / `1' AND 1=2#` | | Medium Payload | `1 AND 1=1` via Burp Suite | | High Payload | `1' AND 1=1#` via cookie popup | | Low Result | Boolean response confirmed injection | | Medium Result | Boolean response confirmed via Burp | | High Result | Still vulnerable via unsanitized cookie | | OWASP Category | A03:2021 Injection | #### Recommended Remediation - Use parameterized queries rather than dynamically constructed SQL. - Validate input server-side using an allowlist appropriate to the expected field. - Apply least-privilege permissions to the application database account. - Avoid exposing detailed database errors to users. - Log and alert on repeated malformed query patterns. ### 3.3 XSS Reflected #### Low **Payload:** `` **Result:** Alert popup fired with "XSS". The script tag was reflected directly in the page response and executed by the browser. ![xss reflected low](https://static.pigsec.cn/wp-content/uploads/repos/cas/fb/fb79578f54ebc35d1a8d9a6e6148c515d8758fb6e3b1a37c29c9c85be8511ecd.png) **Why it worked:** Input is reflected back in the response with zero sanitization. The browser sees the script tag and executes it immediately. #### Medium **Payload:** `` **Result:** Initial `` | | Medium Payload | `` | | High Payload | `` | | Low Result | Alert fired | | Medium Result | Script tag stripped, img bypass worked | | High Result | Still vulnerable via img onerror | | OWASP Category | A03:2021 Injection | #### Recommended Remediation - Apply context-aware output encoding. - Sanitize HTML only when HTML input is genuinely required. - Avoid unsafe DOM sinks such as `innerHTML`. - Enforce a restrictive Content Security Policy. - Validate input on the server rather than relying on client-side restrictions. ### 3.4 XSS Stored #### Low **Payload:** `` in the Message field **Result:** Alert popup fired with "Stored XSS". The script was saved to the database and executed every time the page loaded. Maxlength attribute on the message field was changed via browser inspect to allow the full payload. ![xss stored low](https://static.pigsec.cn/wp-content/uploads/repos/cas/cb/cbf80e5ee275fe5dd0d1964b8b137659c0eb0b36bc67c425fd49115e87803d8e.png) **Why it worked:** Input is saved to the database with no sanitization and rendered raw on every page load. Any user visiting the page triggers the script automatically. #### Medium **Payload:** `` **Result:** Script tags were stripped but the img onerror bypass fired the alert successfully. Payload was stored and executed on page load. ![xss stored medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/ec/ecdaf570cb6c9c9769c0a7df8b8ca34db54cf1c4658b81bdd28ab2d03fdb5328.png) **Analysis:** Medium filters `` | | Medium Payload | `` | | High Payload | `` | | Low Result | Alert fired on page load | | Medium Result | Script stripped, img bypass worked | | High Result | Payload stripped, no execution | | OWASP Category | A03:2021 Injection | ### 3.5 XSS DOM #### Low **Payload:** `?default=` via URL **Result:** Alert popup fired. The script was injected directly into the URL parameter and executed by the browser via DOM manipulation. ![XSS DOM Low](https://static.pigsec.cn/wp-content/uploads/repos/cas/a1/a1914a9959f187ba9288f59aeba477107d134d64d67beea022f6d3ffd437bf5f.png) **Why it worked:** The page reads the URL parameter and writes it directly into the DOM using JavaScript with no sanitization. The browser executes whatever is injected. #### Medium **Payload:** `?default=` **Result:** Alert fired. Closed the existing HTML select element first then injected an img tag with onerror handler to execute JavaScript. ![XSS DOM Medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/09/09aaabb4e671df13f6c21f0cdfbf0bb0278113e88ea3b22776f32d21ea3e8a9e.png) **Analysis:** Medium blocks direct script tags but does not account for HTML element breakout. By closing the select and option tags first, the injected img tag lands outside the expected context and executes. #### High **Payload:** `?default= ` **Result:** No alert fired. High level blocked all payloads attempted. ![XSS DOM High](https://static.pigsec.cn/wp-content/uploads/repos/cas/1b/1bfb1acc9334806105d34102044ee5524b6fcf7adef4c9c34542fc5abd2558e6.png) **Defense Mechanism:** High level uses a strict whitelist of allowed values for the default parameter. Only predefined language values are accepted. Anything outside that list is rejected before it reaches the DOM. | Field | Details | |---|---| | Vulnerability | XSS DOM | | Security Levels Tested | Low, Medium, High | | Low Payload | `?default=` | | Medium Payload | `?default=` | | High Payload | `?default=` | | Low Result | Alert fired via direct URL injection | | Medium Result | Alert fired via HTML breakout and img onerror | | High Result | Payload blocked by whitelist | | OWASP Category | A03:2021 Injection | ### 3.6 CSRF #### Low **Payload:** Forged HTML form that auto-submits a password change request
**Result:** Password changed successfully without any user interaction. The server accepted the forged request because no token was required. ![CSRF Low](https://static.pigsec.cn/wp-content/uploads/repos/cas/fc/fc52f5eaf1a25a0a9ea9102c2a6db55a177921b31c2f92c8fc3aa85ffbf5fffa.png) **Why it worked:** No CSRF token exists at Low level. Any request with a valid session cookie is accepted regardless of where it originated. #### Medium **Payload:** Stored XSS used to inject a forged request from within DVWA itself **Result:** Attack succeeded. Burp HTTP History confirmed the CSRF request fired from within DVWA, bypassing the Referer header check. ![CSRF Medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/7a/7a53292711369773bcc4300e8b63af397abfd59134e8ad382173b3075806cba0.png) **Analysis:** Medium checks the Referer header to confirm the request came from DVWA. Serving the forged request via Stored XSS makes it originate from DVWA itself, satisfying the check. #### High **Payload:** Same Stored XSS img src injection attempt **Result:** Attack failed. No CSRF request appeared in Burp HTTP History. High level blocked the forged request. ![CSRF High](https://static.pigsec.cn/wp-content/uploads/repos/cas/f4/f4b20e6f37904d8b7106d4bd0f176d261e491885549f52639b77c0b2505c7941.png) **Defense Mechanism:** High level requires a valid CSRF token embedded in every form submission. The token is unique per session and per request. A forged request from any origin cannot know the token, so the server rejects it. | Field | Details | |---|---| | Vulnerability | CSRF | | Security Levels Tested | Low, Medium, High | | Low Payload | Forged HTML form via local file | | Medium Payload | Stored XSS img src injection | | High Payload | Stored XSS img src injection | | Low Result | Password changed successfully | | Medium Result | Attack succeeded via XSS bypass | | High Result | Blocked by CSRF token requirement | | OWASP Category | A01:2021 Broken Access Control | ### 3.7 Command Injection #### Low **Payload:** `127.0.0.1; ls` **Result:** Ping ran successfully then ls listed the directory contents — help, index.php, source. Both commands executed. ![Command Injection Low](https://static.pigsec.cn/wp-content/uploads/repos/cas/d3/d36bdfe316d7459ef090360020e7fd66abaaa0fa3dece8e7d15a833fa00f708f.png) **Why it worked:** Input is passed directly to a system shell call with no sanitization. The semicolon separates two commands and the shell executes both. #### Medium **Payload:** `127.0.0.1 | ls` **Result:** Semicolon and && were stripped but the pipe character was not filtered. Directory listing returned successfully. ![Command Injection Medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/9a/9a55cdc425f299632c4809addb58d0ac0d964907e9ada0103b5570af010460ed.png) **Analysis:** Medium strips some shell metacharacters like `;` and `&&` but misses the pipe `|` operator. The filter is incomplete and bypassable. #### High **Payload:** `127.0.0.1|ls` **Result:** Attack succeeded. Removing the space before the pipe bypassed the High level filter. Directory listing returned. ![Command Injection High](https://static.pigsec.cn/wp-content/uploads/repos/cas/75/7548747c956e58468220b8f5be4b6beb2a6a53e2267dd9c0d44efd6993d28e28.png) **Defense Mechanism:** High level attempts to block pipe and other metacharacters but the filter checks for ` | ` with spaces. Removing the space bypasses the check entirely. Proper defense requires a strict input whitelist allowing only valid IP address characters. | Field | Details | |---|---| | Vulnerability | Command Injection | | Security Levels Tested | Low, Medium, High | | Low Payload | `127.0.0.1; ls` | | Medium Payload | `127.0.0.1 \| ls` | | High Payload | `127.0.0.1\|ls` | | Low Result | Both commands executed | | Medium Result | Pipe bypass worked | | High Result | No-space pipe bypass worked | | OWASP Category | A03:2021 Injection | ### 3.8 File Inclusion #### Low **Payload:** `http://localhost:9090/vulnerabilities/fi/?page=../../../../../../etc/passwd` **Result:** Full contents of /etc/passwd exposed. All system user accounts including root, daemon, mysql and others were visible at the top of the page. ![File Inclusion Low](https://static.pigsec.cn/wp-content/uploads/repos/cas/46/46744d1877c734098e8bc2ede5e5c2e70a2f98ae86c75f5ed9be8438bc84cc49.png) **Why it worked:** The page parameter is passed directly to a PHP include statement with no path restriction. Traversing up the directory tree with ../../ reaches the filesystem root and includes any file. #### Medium **Payload:** `....//....//....//etc/passwd` **Result:** Page returned empty. Medium level blocked the path traversal attempt. ![File Inclusion Medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/a8/a8240eeea368c882ac90e1d239038303edcd91fb5e3f85a7fb4fa29ce8f43e00.png) **Analysis:** Medium strips `../` from the input. The double dot slash bypass `....//` attempts to survive that strip but the filter at Medium level was sufficient to block it. #### High **Payload:** `file:///etc/passwd` **Result:** Full /etc/passwd file exposed again. High level blocked directory traversal but did not block the file:// protocol wrapper. ![File Inclusion High](https://static.pigsec.cn/wp-content/uploads/repos/cas/88/88c0e3574ee136df36ece69d79d469ac8ce5c76fc59241ca84fd70587bd776a8.png) **Defense Mechanism:** High blocks `../` traversal but fails to restrict PHP stream wrappers like `file://`. Full protection requires validating input against a strict allowlist of permitted filenames only. | Field | Details | |---|---| | Vulnerability | File Inclusion | | Security Levels Tested | Low, Medium, High | | Low Payload | `../../../../../../etc/passwd` | | Medium Payload | `....//....//....//etc/passwd` | | High Payload | `file:///etc/passwd` | | Low Result | /etc/passwd fully exposed | | Medium Result | Blocked | | High Result | /etc/passwd exposed via file:// wrapper | | OWASP Category | A05:2021 Security Misconfiguration | ### 3.9 File Upload #### Low **Approach:** Uploaded a PHP webshell as a `.php` file with no restrictions. **Result:** File uploaded successfully. Visiting `http://localhost:9090/hackable/uploads/shell.php?c=ls` executed the command and returned directory contents confirming remote code execution. ![File Upload Low](https://static.pigsec.cn/wp-content/uploads/repos/cas/97/97bff2e3826f3b022b46152372a3d8a2d08a164b328f1cb39dbcdff3a332be70.png) ![File Upload Low Shell Output](https://static.pigsec.cn/wp-content/uploads/repos/cas/89/8969a4a3029556886e178e39a19b9fefd9ca1c222c86073c03416c4eebf70df7.png) **Why it worked:** No file type validation exists at Low. Any file extension is accepted and PHP files are executed directly by the server. #### Medium **Approach:** Uploaded the same PHP webshell via Burp Suite. Intercepted the request and changed the Content-Type header from `application/x-php` to `image/jpeg` while keeping the `.php` extension. **Result:** Upload succeeded. Shell was accessible at the same URL and executed commands successfully. ![File Upload Medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/e1/e16ac0c4c12e43c6a32a1a1c4183f9dbfbbf47e20d63c56ab55b90ad00833e7b.png) ![File Upload Medium Shell Output](https://static.pigsec.cn/wp-content/uploads/repos/cas/b4/b494a686b271a4087da27bafcdc74c23163906758e97a19604a9717f02f9b866.png) **Analysis:** Medium checks the Content-Type header sent in the request, not the actual file content. Changing the header in Burp to image/jpeg while keeping the .php extension bypassed the check entirely. #### High **Approach:** Attempted direct upload of `shell.php` without any modification. **Result:** Upload rejected with "Your image was not uploaded. We can only accept JPEG or PNG images." ![File Upload High](https://static.pigsec.cn/wp-content/uploads/repos/cas/a0/a0c6ae710ae68cc74795a548b135db787d89ffdab769689418e887c17653fb97.png) **Defense Mechanism:** High inspects the actual file extension and content, not just the Content-Type header. A PHP file cannot be disguised as an image at this level. | Field | Details | |---|---| | Vulnerability | File Upload | | Security Levels Tested | Low, Medium, High | | Low Payload | PHP webshell uploaded directly | | Medium Payload | PHP webshell with spoofed Content-Type via Burp | | High Payload | Direct upload attempt | | Low Result | Remote code execution confirmed | | Medium Result | Content-Type bypass worked, RCE confirmed | | High Result | Upload blocked | | OWASP Category | A04:2021 Insecure Design | ### 3.10 Brute Force #### Low **Approach:** Used Burp Suite Intruder to brute force the password field. Sent the login request from HTTP History to Intruder, set the password as the payload position, and ran a small wordlist. **Result:** Password "password" returned a longer response length confirming successful login. "Welcome to the password protected area" visible in the response. ![Brute Force Low](https://static.pigsec.cn/wp-content/uploads/repos/cas/29/29f56a30f9ed7dbf13c8b52461a32f56e0d0b7403e2be1b2f186a8ab18a2a35a.png) **Why it worked:** No rate limiting, lockout policy, or delay exists at Low. Requests are processed as fast as they arrive making automated attacks trivial. #### Medium **Approach:** Same Burp Intruder attack at Medium level. **Result:** Attack succeeded. Password identified via longer response length. Medium only adds a 2 second delay per request, slowing the attack but not stopping it. ![Brute Force Medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/59/593f5a9b9bf2f39afb420ac8caafe8e912ca443da584304f6638d772ffb0c7f9.png) **Analysis:** A time delay is not a real defense. It slows brute force but any automated tool will simply wait. No lockout or token requirement exists. #### High **Approach:** Same Burp Intruder attack at High level. **Result:** Attack still succeeded. Password identified via response length difference. ![Brute Force High](https://static.pigsec.cn/wp-content/uploads/repos/cas/00/00756086ad83cec39027a3b9242ea0a82632d3cf7c32322d689ce54bfefa3a4e.png) **Defense Mechanism:** High level was expected to require a CSRF token making automation harder. However the attack still worked via Burp Intruder which handles the token automatically. True protection requires account lockout after a set number of failed attempts combined with CSRF tokens. | Field | Details | |---|---| | Vulnerability | Brute Force | | Security Levels Tested | Low, Medium, High | | Tool Used | Burp Suite Intruder | | Low Result | Password found, no rate limiting | | Medium Result | Password found, delay only slowed attack | | High Result | Password found, no effective lockout | | OWASP Category | A07:2021 Identification & Authentication Failures | ### 3.11 Insecure CAPTCHA #### Module Status: Non-Functional **Reason:** The CAPTCHA module requires a Google reCAPTCHA API key configured in `/var/www/html/config/config.inc.php`. This key was not present in the DVWA Docker setup, so the module displayed the error "reCAPTCHA API key missing" across all security levels. ![Insecure CAPTCHA](https://static.pigsec.cn/wp-content/uploads/repos/cas/b0/b05b1baebcd9771ba4996b4b8d3a8b6ee9054313fbea8ab875e15bdbe61b20f8.png) #### Expected Behavior (Theoretical Analysis) #### Low **Approach:** Parameter manipulation via Burp Suite. Intercept the password change request and change `step=1` to `step=2` to skip CAPTCHA validation entirely. **Why it would work:** Low level validates CAPTCHA only at step 1. Jumping directly to step 2 bypasses the check entirely since no server-side verification exists for the step value. #### Medium **Approach:** Intercept request and add `passed_captcha=true` as a POST parameter alongside changing the step value. **Why it would work:** Medium checks for a `passed_captcha` parameter. Since it trusts client-supplied values, simply adding it to the request tricks the server into thinking CAPTCHA was solved. #### High **Expected Result:** Attack would fail. High level performs server-side verification with Google's reCAPTCHA API. The server sends the CAPTCHA response token to Google directly for validation. A forged or missing token gets rejected. | Field | Details | |---|---| | Vulnerability | Insecure CAPTCHA | | Security Levels Tested | Low, Medium, High | | Module Status | Non-functional, missing reCAPTCHA API key | | Low Result | Not testable, theoretical bypass via step manipulation | | Medium Result | Not testable, theoretical bypass via passed_captcha parameter | | High Result | Not testable, server-side validation expected | | OWASP Category | A07:2021 Identification & Authentication Failures | ### 3.12 Weak Session IDs #### Low **Approach:** Clicked Generate multiple times and observed the Set-Cookie header in Burp response tab. **Result:** Session IDs were sequential integers — 6, 7, 8... incrementing by 1 each click. Completely predictable. An attacker knowing one session ID can guess all others. ![Weak Session IDs Low](https://static.pigsec.cn/wp-content/uploads/repos/cas/88/88d5d14864a7b519c97a5c4307f0e5724d07be9d23ef92a1fbb2ee1d96b87820.png) **Why it worked:** Session IDs are generated using a simple counter. Any attacker who knows one valid session ID can predict all others and hijack any active session trivially. #### Medium **Approach:** Same observation at Medium via Burp response tab. **Result:** Session IDs changed to Unix timestamps — 1772951595, 1772951596. Different format but still predictable since the current time is publicly known. ![Weak Session IDs Medium](https://static.pigsec.cn/wp-content/uploads/repos/cas/54/549155df19b99b80a8c0b15c1c40aff96c2ef9d050c50ce1ea51d85490bb5ff9.png) **Analysis:** Medium improves on sequential IDs by using timestamps but timestamps are not random. An attacker can narrow down valid session IDs by approximating when a user logged in. #### High **Approach:** Same observation at High via Burp response tab. **Result:** Session ID was an MD5 hash — c51ce410c124a10e0db5e4b97fc2af39. Not sequential or time-based, significantly harder to predict. ![Weak Session IDs High](https://static.pigsec.cn/wp-content/uploads/repos/cas/48/48f0c1369621cb78ade997ddc4350a904438b4b02c2758ebe8d3949be209037a.png) **Defense Mechanism:** High level hashes the session ID with MD5 making it non-predictable at a glance. However MD5 is a weak algorithm. Production systems should use cryptographically secure random generators, not hashed counters. | Field | Details | |---|---| | Vulnerability | Weak Session IDs | | Security Levels Tested | Low, Medium, High | | Low Result | Sequential integers, fully predictable | | Medium Result | Unix timestamps, still predictable | | High Result | MD5 hash, not easily predictable | | OWASP Category | A07:2021 Identification & Authentication Failures | ## 4: Docker Inspection Tasks ### 4.1 Docker ps ![docker ps](https://static.pigsec.cn/wp-content/uploads/repos/cas/c6/c65ec61a1b53b2f49c15657ec05b8a024a5eb3d7b868a740a95be8cb6d8e2751.png) The container is named `upbeat_bohr`, running the `vulnerables/web-dvwa` image. Port 9090 on the host maps to port 80 inside the container. ### 4.2 Docker inspect ![docker inspect](https://static.pigsec.cn/wp-content/uploads/repos/cas/c2/c2abb33ef816ceea7e2a181fb6ad263a93daf2e97383a933cce47b207b2172c4.png) Key findings from the inspect output: - **Image:** `vulnerables/web-dvwa` - **Container ID:** `404da67ad95e` - **IP Address:** `172.17.0.2` (bridge network) - **Port Binding:** `0.0.0.0:9090 -> 80/tcp` - **Network Mode:** bridge - **AutoRemove:** true (container deletes itself on stop) - **Entrypoint:** `/main.sh` (starts Apache and MariaDB) ### 4.3 Docker logs ![docker logs](https://raw.githubusercontent.com/bilalahmedss/Application-Security-Testing/main/images/docker_logs.png) The logs confirm: - MariaDB started successfully - Apache 2.4.25 (Debian) started on port 80 - All HTTP requests from the testing session are logged with timestamps, source IP, HTTP method, endpoint, and status codes - PHP errors are visible, including the `HTTP_REFERER` notice from CSRF Medium and `include()` failures from File Inclusion Medium ### 4.4 Docker exec - Container Shell Access ![docker exec shell](https://static.pigsec.cn/wp-content/uploads/repos/cas/ea/ea0e608dcad6eee22ffbcee154e87fa18dcd3de84b039ce997b483eefa51c49a.png) docker exec -it upbeat_bohr /bin/bash root@404da67ad95e:/# ls /var/www/html **Output:** CHANGELOG.md README.md config dvwa external hackable ids_log.php index.php login.php logout.php phpinfo.php robots.txt security.php setup.php vulnerabilities ### 4.5 Explanations **Where application files are stored** All DVWA application files live inside the container at `/var/www/html`. The `vulnerabilities/` directory holds the 12 exploit modules. The `hackable/uploads/` directory is where uploaded files (like the PHP web shell) get stored. The `config/` directory holds the database connection settings. **What backend technology DVWA uses** DVWA runs on a LAMP stack: Linux (Debian), Apache 2.4.25, MariaDB, and PHP. The web application itself is written in PHP. The database stores user accounts and application data. Apache serves all requests and logs them to `/var/log/apache2/`. **How Docker isolates the environment** Docker isolates DVWA using Linux namespaces and cgroups. The container has its own network namespace with a private IP (`172.17.0.2`) on the Docker bridge network, separate from the host's network stack. The filesystem is isolated using OverlayFS, so any changes inside the container (uploaded shells, modified files) do not affect the host. The container runs as its own process tree with PID isolation. Only port 9090 is explicitly exposed to the host, meaning no other container ports are accessible from outside. ## 5: Security Analysis ### Q1: Why does SQL Injection succeed at Low security? At Low, DVWA passes user input directly into the SQL query with no sanitization. The vulnerable query looks like this: SELECT * FROM users WHERE user_id = '$id'; When I entered `1' OR '1'='1`, the query became: SELECT * FROM users WHERE user_id = '1' OR '1'='1'; The single quote closes the `user_id` string early. The `OR '1'='1'` condition is always true, so the database returns every row in the table. The application never intended to expose all users, but because input was treated as code rather than data, the query logic was completely rewritten by the attacker. ### Q2: What control prevents SQL Injection at High? At High, DVWA uses PDO prepared statements. The query is defined first with a placeholder: $stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = :id"); $stmt->bindParam(':id', $id); The database receives the query structure and the user input as two separate things. The query is compiled before the input is ever inserted. Whatever the user submits — including quotes, SQL keywords, or comment sequences — the database engine treats it as a literal string value, not executable code. There is no way to break out of the data context because the code context was already fixed. This is why `1' OR '1'='1` at High simply returns no results instead of dumping the table. ### Q3: Does HTTPS prevent these attacks? No. HTTPS encrypts the connection between the browser and the server, protecting data from interception in transit. Once the server receives and decrypts the request, HTTPS plays no further role. All of the attacks in this lab operate at the application layer, after decryption has already happened. A SQL injection payload sent over HTTPS arrives at the PHP interpreter in exactly the same form as one sent over HTTP. The same applies to XSS payloads, command injection strings, CSRF requests, and brute force login attempts. The vulnerability is in how the application processes input, not in how the request was delivered. HTTPS solves a transport problem. These are application logic problems. ### Q4: What risks exist if DVWA were deployed publicly? **Remote code execution.** The file upload module at Low and Medium allows uploading a PHP web shell with no real restriction. An attacker gets direct command execution on the server, with the same OS privileges as the web server process. **Full database compromise.** SQL injection at Low gives read access to every table in the database. With `UNION`-based injection or `sqlmap`, an attacker can dump credentials, emails, and any other stored data. With write privileges, they can modify records or drop tables entirely. **Account takeover.** Brute force at all three levels cracked the admin password using a short wordlist. Weak session IDs at Low are sequential integers, meaning an attacker watching their own session cookie can enumerate other active sessions. CSRF at Low and Medium allows password changes without any user interaction. **Stored XSS as a persistent threat.** A payload injected into the guestbook at Low fires for every user who loads the page. This enables session cookie theft, credential harvesting pages, or malware distribution at scale, affecting every visitor until the payload is manually removed. **Lateral movement.** A compromised server on a corporate network becomes a foothold. Command injection gives shell access, from which an attacker can scan internal subnets, access internal services, or exfiltrate data that is never exposed to the internet directly. ### Q5: Why does security increase at each DVWA level? At Low, there are no defenses. Input goes directly into SQL queries, shell commands, file paths, and HTML output. The application trusts everything the user sends. At Medium, basic filtering is added — blacklists that strip or escape specific characters. This stops the most obvious payloads but is consistently bypassable. For SQL injection, the `mysql_real_escape_string()` function stops quote-based attacks but the dropdown still accepts unsanitized integer input via Burp. For command injection, semicolons and `&&` are blocked but the pipe character is not. Medium demonstrates the problem with blacklist-based security: attackers only need to find one character or encoding variant that was not anticipated. At High, the approach changes entirely. SQL injection is blocked with PDO prepared statements, which make injection structurally impossible rather than just harder. XSS stored is blocked by stripping all HTML tags on input. CSRF is blocked with per-session tokens that cannot be forged from a cross-origin request. Command injection is blocked with a strict allowlist that accepts only valid IP address formats. ## The pattern across all three levels shows that security comes from correct design, not from adding filters on top of vulnerable code. Medium is security theater. High is actual defense ## 6: OWASP Top 10 Mapping | Vulnerability | OWASP Top 10 Category | |---|---| | SQL Injection | A03:2021 Injection | | SQL Injection (Blind) | A03:2021 Injection | | XSS Reflected | A03:2021 Injection | | XSS Stored | A03:2021 Injection | | XSS DOM | A03:2021 Injection | | CSRF | A01:2021 Broken Access Control | | Command Injection | A03:2021 Injection | | File Inclusion | A05:2021 Security Misconfiguration | | File Upload | A04:2021 Insecure Design | | Brute Force | A07:2021 Identification & Authentication Failures | | Insecure CAPTCHA | A07:2021 Identification & Authentication Failures | | Weak Session IDs | A07:2021 Identification & Authentication Failures | ## 7: Conclusion Testing DVWA across three security levels made one thing clear: the difference between vulnerable and secure code is not about effort, it's about approach. At Low, every module fell to textbook attacks. No input validation, no output encoding, no access controls. SQL injection dumped the entire user table in seconds. A PHP shell uploaded without resistance gave direct command execution. Stored XSS fired on every page load. The application trusted everything the user sent, and paid for it across the board. Medium added filters, and those filters failed. Blacklists are inherently incomplete. For every character or pattern blocked, there is usually an encoding variant, an alternate syntax, or an overlooked operator that achieves the same result. The pipe character bypassed command injection filters. The `img onerror` attribute bypassed XSS script tag stripping. Burp Intruder ignored the 2-second brute force delay and cracked the password anyway. Medium demonstrated exactly why "add a filter" is not a security strategy. High used structural defenses: prepared statements that make SQL injection impossible by design, CSRF tokens that cannot be forged cross-origin, allowlists that reject anything not explicitly permitted. Most High-level modules held. The ones that did not, like XSS Stored, revealed that a single unprotected input field is enough. If I were auditing a production application, I would start by looking for any place user input touches a SQL query, shell command, file path, or HTML output. I would check whether the application uses parameterized queries consistently, not just in obvious places. I would look for CSRF protection on every state-changing request, not just password changes. I would check session token entropy and rotation on login. And I would treat any file upload endpoint as high-risk by default, verifying that MIME type, extension, and file content are all validated server-side. The broader takeaway is that most real-world breaches do not require novel techniques. They exploit the same classes of vulnerability demonstrated here, against applications that never moved past Medium. ## 8: Bonus: Nginx + HTTPS ### Setup Overview DVWA was deployed behind an Nginx reverse proxy using Docker Compose. Nginx handles all incoming traffic on ports 8080 (HTTP) and 8443 (HTTPS), forwarding requests to the DVWA container on its internal port 80. The DVWA container has no ports exposed directly to the host. The docker-compose.yml defines two services: `dvwa` using the `vulnerables/web-dvwa` image with port 80 exposed only internally, and `nginx` using `nginx:alpine` with ports 8080 and 8443 bound to the host. ### Nginx Configuration ![nginx config](https://static.pigsec.cn/wp-content/uploads/repos/cas/6a/6a0bf420503e351a410a51ed2b236ae51c8afcac03ddbe061366b0f45697f0aa.png) Nginx runs two server blocks. The first listens on port 80 and issues a 301 permanent redirect to HTTPS on port 8443, forcing all HTTP traffic to upgrade. The second listens on port 443 with SSL enabled, loads the self-signed certificate and key from `/etc/nginx/certs/`, and proxies all requests to the DVWA container at `http://dvwa:80` using Docker's internal DNS. ### Self-Signed Certificate openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout certs/nginx.key \ -out certs/nginx.crt \ -subj "//C=PK\ST=Sindh\L=Karachi\O=HabibUniversity\CN=localhost" ![openssl output](https://static.pigsec.cn/wp-content/uploads/repos/cas/e4/e45c7d3399bf7beef9dc54baadad04d3b95fa816df968a214dfdc1a31cab8d0a.png) A 2048-bit RSA self-signed certificate was generated using OpenSSL. The certificate is valid for 365 days. Because it is self-signed rather than issued by a trusted CA, browsers display a certificate warning on first visit. ![browser warning](https://static.pigsec.cn/wp-content/uploads/repos/cas/c3/c31377a90ae636e2639674554537b50738ad489b5d08488be3a5facbb00ea58e.png) After proceeding past the warning, DVWA loads over HTTPS at `https://localhost:8443`. ![dvwa over https](https://static.pigsec.cn/wp-content/uploads/repos/cas/4a/4aedc2fcafa1f83e81eea7d140547750f9b89d34203665449f9cbbd6b94b88ae.png) ### Certificate Details ![certificate details](https://static.pigsec.cn/wp-content/uploads/repos/cas/d2/d2b02d31bc6993f8cf7ebe1d62a05eba978c2662c7097f5247c3a47d338a50ff.png) The certificate viewer confirms: - **Common Name (CN):** localhost - **Organization (O):** HabibUniversity - **Issued On:** Monday, March 9, 2026 - **Expires On:** Tuesday, March 9, 2027 - The certificate is self-signed, meaning it was issued by the same entity it was issued to. ### HTTP vs HTTPS Traffic Comparison #### HTTP — Plaintext Credentials Visible ![wireshark http](https://static.pigsec.cn/wp-content/uploads/repos/cas/69/694070e4395b7d9b5978d4586549aea6c2b81aa20479f612cbad1e6caaab699b.png) With Nginx configured to proxy HTTP directly on port 8080, a Wireshark capture on the loopback adapter captured the login request in full. Following the HTTP stream shows the POST body in plaintext: username=admin&password=password&Login=Login The credentials, session cookie, and all request headers are fully readable to anyone with access to the network traffic. #### HTTPS — Traffic Encrypted ![wireshark https](https://static.pigsec.cn/wp-content/uploads/repos/cas/62/623d85af9fb76d7ff4049e84627fc776f3ef4b50dd3e2c26b8ee1e47e7f8bba6.png) With the 301 redirect restored and HTTPS enforced on port 8443, the same login attempt produces only TLSv1.3 packets. Following the TLS stream returns an empty window — no readable content. The entire HTTP conversation including credentials, cookies, and headers is encrypted inside the TLS tunnel before leaving the browser. ### Key Difference HTTP sends everything in plaintext. A passive observer on the same network captures credentials with no effort. HTTPS wraps the entire session in TLS 1.3, making the payload unreadable without the private key. The Wireshark captures above demonstrate this directly: one shows `username=admin&password=password` in clear text, the other shows nothing. ## 8. GitHub Repository **Repository:** [https://github.com/bilalahmedss/Application-Security-Testing](https://github.com/bilalahmedss/Application-Security-Testing) ## Project Context This project was completed as part of the Cybersecurity: Theory and Tools course in March 2026. *This lab was performed exclusively on a local machine. No external systems were tested or attacked.*
标签:Burp Suite, CISA项目, Docker, OWASP Top 10, Web安全, 安全测试, 安全防御评估, 攻击性安全, 数据泄露, 漏洞靶场, 蓝队分析, 请求拦截, 防御检测