Web penetration testing

In this tutorial we’re going to define base steps for web penetration testing and find vulnerabilities in DVWA.

Finding hidden content

At this stage you should know what web technologies are used on the target website based on your previous network research.

What you should be looking for:

  1. 1. File robots.txt 
  2. 2. Backup files: .back, .bak etc…
  3. 3. Other files: .pdf, .docx, etc…
  4. 4. Admin urls: admin, myadmin, etc…

If you find any of the above files or folders then check its contents for private information.

Burp Suite is a good choice for this task. You should select Target => Engagement tools => Discover content.

Common workflow

  1. 1. Enable Burp Proxy and manually browse all the pages of the target web app and inspect all requests/responses.
  2. 2. Crawl website using Burp Scan task.
  3. 3. Crawl website using Burp Content Discovery.
  4. 4. For each page found do the following.
  5. 5. Send each request to Burp Scanner for automatic scanning.
  6. 6. If this is a special page (login, registration, restore/change password, upload page) then proceed with the special page checklist(for example for login page you can try brute force or default credentials). After that proceed with the common page checklist.
  7. 7. If the page is not special then proceed with the common page checklist(ex: sqli, xss, etc…).
  8. 8. Gather all issues and create a report.

Common page checklist

  1. 1. Find all parameters processed by the backend. Check URLs for query params, headers, HTML inputs.
  2. 2. Check all APIs called on the web page.
  3. 3. Check if plain http mode works.
  4. 4. Check if error messages reveal sensitive information.
  5. 5. Check for application logic issues. (100% discount, 0$ item, etc…)
  6. 6. Check if a web page can be accessed without authentication.
  7. 7. Check if APIs and web page resources can be accessed without authentication.
  8. 8. Check if a user with a different set of permissions has access to the admin page or APIs.
  9. 9. Check for header security best practices:
    X-frame-options: can the website be rendered inside iframe, frame, embed or object tags.
    X-content-type-options: forces Content-Type headers to be followed.
    Strict-transport-security: forces web browsers to use https.
    Content-security-policy: specifies what app resources are allowed.
    X-XSS-protection:1;mode=block: stop web page load when XSS is detected.
  10. 10. Check cookie/session id for randomness.
  11. 11. Try to brute-force the cookie/session id.
  12. 12. Check that a new session is generated on each login.
  13. 13. Try to decode cookie/session id.
  14. 14. Try to manipulate cookies. (ex: isSuperUser=1)
  15. 15. Find out session duration.
  16. 16. Check the client source code(HTML and JS) for: comments, debug info, logic issues, hidden inputs, disabled inputs.
  17. 17. Search for user id and try to brute-force it. Check if you can access other user’s data within your current session.
  18. 18. Check for CSRF.
  19. 19. Check that the server validates all inputs.
  20. 20. Check for SQLi.
  21. 21. Check for XSS.
  22. 22. Check for command injection.
  23. 23. Check for LFI.
  24. 24. Check for RFI.

Special page checklist

Login page

  1. 1. Check for default credentials.
  2. 2. Try to brute-force credentials.
  3. 3. Check if DOS can be accomplished when the account is temporarily blocked after a few failed logins.
  4. 4. Is there a “remember me” feature?

Registration page

  1. 1. Are weak passwords allowed?
  2. 2. If you register with an existing username(email, phone, etc…), is user enumeration available?
  3. 3. Check for weak secret questions(favourite color?).
  4. 4. Check if DOS can be accomplished via automating user registration.

Reset/change password page

  1. 1. Check if you can change the password of another user.
  2. 2. Check the password change workflow.
  3. 3. Check if a user receives a confirmation email after a password change.
  4. 4. What data is required to change the password?
  5. 5. How strong are the new or temporary passwords?
  6. 6. Check if a user must change the random password after password reset on login.
  7. 7. Check if a user must enter his old password during the password change.

Upload page

  1. 1. Check if you can access the uploaded file via URL. 
  2. 2. Check if you can see other users’ files.
  3. 3. Check if you can upload a web shell.
  4. 4. Check if you can upload a backdoor if the web app allows executables to be uploaded.


In this section we’re going to investigate https://github.com/digininja/DVWA and find all vulnerabilities there. 

The simplest way to install DVWA is using docker command docker run --rm -it -p 80:80 vulnerables/web-dvwa.

There are 4 difficulty levels in DVWA:

  1. 1. Low: no security measures.
  2. 2. Medium: security measures implemented in a bad way.
  3. 3. High: security measures implemented in almost perfect way.
  4. 4. Impossible: secure code. No way to break it.

Brute force

Click on the Brute Force menu item and intercept a login request. Send request to Intruder, set username to admin and mark password field to be iterated. Select standard Passwords dictionary for Payload Options and hit Start attack. You should see that the response with a correct password has different content length.

So valid credentials are admin:password.

Medium difficulty adds 2 seconds delay between requests.

High difficulty adds a CSRF token. You can create a custom script that opens the web page in a headless browser and brute forces credentials.

Impossible level shows error message “Username and/or password incorrect” and blocks account for 15 minutes after 3 failed login attempts.

Command injection

Open the Command Injection menu. You can see an input where you can ping the machine by ip address. You can enter && ls to get a list of files in the current folder:

Medium difficulty hardcoded some symbols but not all of them.

High difficulty blacklisted almost all symbols.

Impossible difficulty uses whitelisting instead of blacklisting.


Open the CSRF menu. Intercept update password request and send it to the repeater. In the context menu select Engagement tools => Generate CSRF PoC => Test in browser => Submit. Password should be updated:

For medium difficulty you can use reflected XSS to update a user’s password.

High difficulty adds CSRF token so you should use XSS to get the CSRF token and update the user’s password.

Impossible difficulty adds the old password input which fixes the vulnerability.

File inclusion

Open the File inclusion menu. You can see the following URL: http://localhost:81/vulnerabilities/fi/?page=include.php. This url is vulnerable for LFI(Local File Inclusion). Enter the following URL to get a list of users: http://localhost:81/vulnerabilities/fi/?page=../../../../../etc/passwd:

That URL is also vulnerable for RFI(Remote File Inclusion). Enter the following URL http://localhost:81/vulnerabilities/fi/?page=https://google.com to see google page inside web content.

Medium difficulty blacklists some symbols.

High difficulty allows inclusion only for files starting with file prefix.

Impossible difficulty uses whitelisting instead of blacklisting.

File upload

Select the File Upload menu. Generate a reverse shell:

Upload myfile.php via the upload file form. Now try to establish a connection:

Medium difficulty checks the Content-Type header and allows only images. So you need to intercept the request and set image/jpeg as a content type instead of application/x-php.

High difficulty resizes an image. So you need to rename myfile.php to myfile.jpeg, then add GIF89a; at the beginning of the shell to make the file look like an image, and finally use LFI to execute the shell.

Impossible difficulty adds loads of security measures.

Insecure CAPTCHA 

Open the Insecure CAPTCHA menu. There are 2 requests when sending the form. The 1st one to  /vulnerabilities/captcha/ that gets a recaptcha token. And the 2nd to the same URL  /vulnerabilities/captcha/ but without the recaptcha token. So you can intercept the request and send only the 2nd one.

Medium difficulty adds step and passed_captcha params which can also be manipulated.

High difficulty allows special User-Agent header and param g-recaptcha-response to bypass validation. These values are hidden in comments.

Impossible difficulty adds current password field and sends only 1 request which must contain a valid captcha response.

SQL injection

Select the SQL injection menu. In the input field enter 1’ UNION SELECT user,password from users# to get a list of all users:

Medium difficulty adds mysql_real_escape_string() sanitization method but SQL injection still works without any quotes. You can also set value of the select input to the following: 1’ UNION SELECT user,password from users#

High difficulty requires user id to be entered on another page but vulnerability still exists with the same payload.

Impossible difficulty uses parameterized queries.

SQL injection (blind)

Select the Sql Injection (Blind) menu. Enter the following sleep query in the input field: 1' and sleep(5)#. The request should be executed in ~5 seconds.

Medium difficulty adds select input but we can modify the select input’s value anyway.

High difficulty adds a query to the Cookie header and returns response with a random sleep number so you need to use greater sleep values.

Impossible difficulty uses parameterized queries.

Weak session id

Select the Weak Session IDs menu. On the Generate button click a new value is assigned to the dvwaSession cookie. On each click this value is increased by 1 so you can predict the next session id.

Medium difficulty uses unix timestamp as a session id.

High difficulty uses md5 hash to encode a simple number.

Impossible difficulty hashes a random value and a word “impossible”.


Open the XSS (DOM) menu. Enter the following URL to see that XSS exists: http://localhost:81/vulnerabilities/xss_d/?default=English<script>alert(1)</script>

Medium difficulty disallows script tag usage but you can still use the img tag: /vulnerabilities/xss_d/?default=English>/option></select><img src='x' onerror='alert(1)'>

High difficulty adds whitelisted values but URL http://localhost:81/vulnerabilities/xss_d/?default=English#<script>alert(1)</script> still works.

Impossible difficulty encodes all URL content.

XSS (reflected)

Open the XSS (Reflected) menu. Enter the following string in the input field to see that XSS exists: test<script>alert(1)</script>

Medium difficulty rejects the script tag but allows sCrIpT.

High difficulty rejects all variations of the script tag but allows the img tag: <img src="x" onerror="alert(1)" />

Impossible difficulty escapes all characters.

XSS (stored)

Open the XSS (Stored) menu. Enter any value in the name field and <script>alert(1)</script> in the message field and press Sign Guestbook. Now, on each page load you should see the alert message.

Medium difficulty adds validation for the message field but not for the name field.

High difficulty removes symbols inside the script tag but allows the img tag.

Impossible difficulty filters all characters.

CSP bypass

To bypass the Content-Security-Policy header you should upload your script on the server.

Medium difficulty uses the Content-Security-Policy header with a nonce so we should add this nonce to the loaded script.

High difficulty adds Solve the sum button. On this button press request is sent to the server /vulnerabilities/csp/source/jsonp.php?callback=solveSum. You can change the callback param to your own JS code.

Impossible level hardcodes the callback function.


Open the JavaScript menu. Enter the success message and hit Submit. You will see an error Invalid token

If you inspect the source code you will find the following JS functions:

So the token is calculated as md5 hash from rot13 function. You can generate the token in your terminal echo -n ‘success’ | tr ‘A-Za-z’ ‘N-ZA-Mn-za-m’ | md5sum, intercept the request and replace the token. You should be able to see the “Well done” message.

Medium difficulty minimizes the code and adds a little bit more complex token calculations.

High difficulty obfuscates the code and adds even more complex token calculations.

Impossible is absent because client side code can always be inspected.


When all issues are detected it is time to write a report. You can find an example report here https://tcm-sec.com/wp-content/uploads/2021/04/TCMS-Demo-Corp-Security-Assessment-Findings-Report.pdf 

Leave a Reply

Your email address will not be published. Required fields are marked *